272 lines
6.8 KiB
C++
272 lines
6.8 KiB
C++
/***************************************************************************
|
|
copyright : (C) 2004 by Allan Sandfeld Jensen
|
|
email : kde@carewolf.com
|
|
***************************************************************************/
|
|
|
|
/***************************************************************************
|
|
* This library is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU Lesser General Public License version *
|
|
* 2.1 as published by the Free Software Foundation. *
|
|
* *
|
|
* This library is distributed in the hope that it will be useful, but *
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
* Lesser General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU Lesser General Public *
|
|
* License along with this library; if not, write to the Free Software *
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
|
* 02110-1301 USA *
|
|
* *
|
|
* Alternatively, this file is available under the Mozilla Public *
|
|
* License Version 1.1. You may obtain a copy of the License at *
|
|
* http://www.mozilla.org/MPL/ *
|
|
***************************************************************************/
|
|
|
|
#include <memory>
|
|
|
|
#include "tbytevectorlist.h"
|
|
#include "tdebug.h"
|
|
|
|
#include "apeitem.h"
|
|
|
|
using namespace Strawberry_TagLib::TagLib;
|
|
using namespace APE;
|
|
|
|
struct ItemData {
|
|
ItemData() : type(Item::Text), readOnly(false) {}
|
|
|
|
Item::ItemTypes type;
|
|
String key;
|
|
ByteVector value;
|
|
StringList text;
|
|
bool readOnly;
|
|
};
|
|
|
|
class APE::Item::ItemPrivate {
|
|
public:
|
|
ItemPrivate() : data(new ItemData()) {}
|
|
|
|
std::shared_ptr<ItemData> data;
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// public members
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
APE::Item::Item() : d(new ItemPrivate()) {}
|
|
|
|
APE::Item::Item(const String &key, const String &value) : d(new ItemPrivate()) {
|
|
d->data->key = key;
|
|
d->data->text.append(value);
|
|
}
|
|
|
|
APE::Item::Item(const String &key, const StringList &values) : d(new ItemPrivate()) {
|
|
d->data->key = key;
|
|
d->data->text = values;
|
|
}
|
|
|
|
APE::Item::Item(const String &key, const ByteVector &value, bool binary) : d(new ItemPrivate()) {
|
|
d->data->key = key;
|
|
if (binary) {
|
|
d->data->type = Binary;
|
|
d->data->value = value;
|
|
}
|
|
else {
|
|
d->data->text.append(value);
|
|
}
|
|
|
|
}
|
|
|
|
APE::Item::Item(const Item &item) : d(new ItemPrivate(*item.d)) {}
|
|
|
|
APE::Item::~Item() {
|
|
delete d;
|
|
}
|
|
|
|
Item &APE::Item::operator=(const Item &item) {
|
|
|
|
Item(item).swap(*this);
|
|
return *this;
|
|
|
|
}
|
|
|
|
void APE::Item::swap(Item &item) {
|
|
|
|
using std::swap;
|
|
|
|
swap(d, item.d);
|
|
|
|
}
|
|
|
|
void APE::Item::setReadOnly(bool readOnly) {
|
|
d->data->readOnly = readOnly;
|
|
}
|
|
|
|
bool APE::Item::isReadOnly() const {
|
|
return d->data->readOnly;
|
|
}
|
|
|
|
void APE::Item::setType(APE::Item::ItemTypes val) {
|
|
d->data->type = val;
|
|
}
|
|
|
|
APE::Item::ItemTypes APE::Item::type() const {
|
|
return d->data->type;
|
|
}
|
|
|
|
String APE::Item::key() const {
|
|
return d->data->key;
|
|
}
|
|
|
|
ByteVector APE::Item::binaryData() const {
|
|
return d->data->value;
|
|
}
|
|
|
|
void APE::Item::setBinaryData(const ByteVector &value) {
|
|
d->data->type = Binary;
|
|
d->data->value = value;
|
|
d->data->text.clear();
|
|
}
|
|
|
|
void APE::Item::setKey(const String &key) {
|
|
d->data->key = key;
|
|
}
|
|
|
|
void APE::Item::setValue(const String &value) {
|
|
d->data->type = Text;
|
|
d->data->text = value;
|
|
d->data->value.clear();
|
|
}
|
|
|
|
void APE::Item::setValues(const StringList &value) {
|
|
d->data->type = Text;
|
|
d->data->text = value;
|
|
d->data->value.clear();
|
|
}
|
|
|
|
void APE::Item::appendValue(const String &value) {
|
|
d->data->type = Text;
|
|
d->data->text.append(value);
|
|
d->data->value.clear();
|
|
}
|
|
|
|
void APE::Item::appendValues(const StringList &values) {
|
|
d->data->type = Text;
|
|
d->data->text.append(values);
|
|
d->data->value.clear();
|
|
}
|
|
|
|
int APE::Item::size() const {
|
|
size_t result = 8 + d->data->key.size() + 1;
|
|
switch (d->data->type) {
|
|
case Text:
|
|
if (!d->data->text.isEmpty()) {
|
|
StringList::ConstIterator it = d->data->text.begin();
|
|
|
|
result += it->data(String::UTF8).size();
|
|
it++;
|
|
for (; it != d->data->text.end(); ++it)
|
|
result += 1 + it->data(String::UTF8).size();
|
|
}
|
|
break;
|
|
|
|
case Binary:
|
|
case Locator:
|
|
result += d->data->value.size();
|
|
break;
|
|
}
|
|
return result;
|
|
|
|
}
|
|
|
|
StringList APE::Item::values() const {
|
|
return d->data->text;
|
|
}
|
|
|
|
String APE::Item::toString() const {
|
|
if (d->data->type == Text && !isEmpty())
|
|
return d->data->text.front();
|
|
else
|
|
return String();
|
|
|
|
}
|
|
|
|
bool APE::Item::isEmpty() const {
|
|
switch (d->data->type) {
|
|
case Text:
|
|
if (d->data->text.isEmpty())
|
|
return true;
|
|
if (d->data->text.size() == 1 && d->data->text.front().isEmpty())
|
|
return true;
|
|
return false;
|
|
case Binary:
|
|
case Locator:
|
|
return d->data->value.isEmpty();
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|
|
void APE::Item::parse(const ByteVector &data) {
|
|
|
|
// 11 bytes is the minimum size for an APE item
|
|
|
|
if (data.size() < 11) {
|
|
debug("APE::Item::parse() -- no data in item");
|
|
return;
|
|
}
|
|
|
|
const unsigned int valueLength = data.toUInt32LE(0);
|
|
const unsigned int flags = data.toUInt32LE(4);
|
|
|
|
// An item key can contain ASCII characters from 0x20 up to 0x7E, not UTF-8.
|
|
// We assume that the validity of the given key has been checked.
|
|
|
|
d->data->key = String(&data[8], String::Latin1);
|
|
|
|
const ByteVector value = data.mid(8 + d->data->key.size() + 1, valueLength);
|
|
|
|
setReadOnly(flags & 1);
|
|
setType(ItemTypes((flags >> 1) & 3));
|
|
|
|
if (Text == d->data->type)
|
|
d->data->text = StringList(ByteVectorList::split(value, '\0'), String::UTF8);
|
|
else
|
|
d->data->value = value;
|
|
}
|
|
|
|
ByteVector APE::Item::render() const {
|
|
|
|
ByteVector data;
|
|
unsigned int flags = ((d->data->readOnly) ? 1 : 0) | (d->data->type << 1);
|
|
ByteVector value;
|
|
|
|
if (isEmpty())
|
|
return data;
|
|
|
|
if (d->data->type == Text) {
|
|
StringList::ConstIterator it = d->data->text.begin();
|
|
|
|
value.append(it->data(String::UTF8));
|
|
it++;
|
|
for (; it != d->data->text.end(); ++it) {
|
|
value.append('\0');
|
|
value.append(it->data(String::UTF8));
|
|
}
|
|
d->data->value = value;
|
|
}
|
|
else
|
|
value.append(d->data->value);
|
|
|
|
data.append(ByteVector::fromUInt32LE(value.size()));
|
|
data.append(ByteVector::fromUInt32LE(flags));
|
|
data.append(d->data->key.data(String::Latin1));
|
|
data.append(ByteVector('\0'));
|
|
data.append(value);
|
|
|
|
return data;
|
|
|
|
}
|