diff --git a/ape/Makefile.am b/ape/Makefile.am index 25637340..e28770d8 100644 --- a/ape/Makefile.am +++ b/ape/Makefile.am @@ -5,7 +5,7 @@ INCLUDES = \ noinst_LTLIBRARIES = libape.la -libape_la_SOURCES = apetag.cpp apefooter.cpp +libape_la_SOURCES = apetag.cpp apefooter.cpp apeitem.cpp taglib_include_HEADERS = apetag.h apefooter.h taglib_includedir = $(includedir)/taglib diff --git a/ape/apefooter.cpp b/ape/apefooter.cpp index 7d709dec..bde69782 100644 --- a/ape/apefooter.cpp +++ b/ape/apefooter.cpp @@ -181,7 +181,7 @@ void Footer::parse(const ByteVector &data) d->itemCount = data.mid(16, 4).toUInt(false); // Read the flags - std::bitset<32> flags(data.mid(8, 4).toUInt(false)); + std::bitset<32> flags(data.mid(20, 4).toUInt(false)); d->headerPresent = flags[31]; d->footerPresent = !flags[30]; diff --git a/ape/apeitem.cpp b/ape/apeitem.cpp new file mode 100644 index 00000000..1cc7950c --- /dev/null +++ b/ape/apeitem.cpp @@ -0,0 +1,167 @@ +/*************************************************************************** + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * + * USA * + ***************************************************************************/ + +#include "apeitem.h" +#include "tbytevectorlist.h" +#include "tstringlist.h" +#include + +using std::cout; + +using namespace TagLib; +using namespace APE; + + +class APE::Item::ItemPrivate +{ +public: + ItemPrivate() : type(Text), readOnly(false) {} + + Item::ItemTypes type; + String key; + ByteVector value; + StringList text; + bool readOnly; +}; + +APE::Item::Item() +{ + d = new ItemPrivate; +} + +APE::Item::Item(const String& key, const String& str) +{ + d = new ItemPrivate; + d->key = key; + d->text.append(str); +} + +APE::Item::Item(const Item& item) +{ + d = new ItemPrivate(*item.d); +} + +Item& APE::Item::operator=(const Item& item) +{ + delete d; + d = new ItemPrivate(*item.d); + return *this; +} + +void APE::Item::setReadOnly(bool val) { + d->readOnly = val; +} + +bool APE::Item::isReadOnly() const { + return d->readOnly; +} + +void APE::Item::setType(APE::Item::ItemTypes val) { + d->type = val; +} + +APE::Item::ItemTypes APE::Item::type() const { + return d->type; +} + +String APE::Item::key() const { + return d->key; +} + +ByteVector APE::Item::value() const { + return d->value; +} + +int APE::Item::size() const { + return 8 + d->key.size() + 1 + d->value.size(); +} + +StringList APE::Item::toStringList() const { + return d->text; +} + +String APE::Item::toString() const { + return d->text.front(); +} + +bool APE::Item::isEmpty() const { + switch(d->type) { + case 0: + case 1: + if(d->text.isEmpty()) return true; + if(d->text.size() == 1 && d->text.front() == "") return true; + return false; + case 2: + return d->value.isEmpty(); + default: + return false; + } +} + +void APE::Item::parse(const ByteVector& data) { + uint valueLength = data.mid(0, 4).toUInt(false); + uint flags = data.mid(4, 4).toUInt(false); + + d->key = String(data.mid(8), String::UTF8); + + d->value = data.mid(8 + d->key.size() + 1, valueLength); + + setReadOnly(flags & 1); + setType(ItemTypes((flags >> 1) & 3)); + + if ((int)(d->type) < 2) { + ByteVectorList bl = ByteVectorList::split(d->value, '\0'); + d->text = StringList(bl, String::UTF8); + cout << d->text.toString(",") << "\n"; + } + +} + +ByteVector APE::Item::render() +{ + ByteVector data; + TagLib::uint flags = ((d->readOnly) ? 1 : 0) | (d->type << 1); + ByteVector value; + + if(isEmpty()) + return data; + + if(d->type != Item::Binary) { + StringList::ConstIterator it = d->text.begin(); + value.append(it->data(String::UTF8)); + it++; + while(it != d->text.end()) { + value.append('\0'); + value.append(it->data(String::UTF8)); + it++; + } + d->value = value; + } else + value.append(d->value); + + data.append(ByteVector::fromUInt(value.size(), false)); + data.append(ByteVector::fromUInt(flags, false)); + data.append(d->key.data(String::UTF8)); + data.append(ByteVector('\0')); + data.append(value); + + return data; +} diff --git a/ape/apeitem.h b/ape/apeitem.h new file mode 100644 index 00000000..a450f01f --- /dev/null +++ b/ape/apeitem.h @@ -0,0 +1,90 @@ +/*************************************************************************** + copyright : (C) 2004 by Allan Sandfeld Jensen + email : kde@carewolf.org + ***************************************************************************/ + +/*************************************************************************** + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * + * USA * + ***************************************************************************/ + +#ifndef TAGLIB_APEITEM_H +#define TAGLIB_APEITEM_H + +#include +#include +#include + +namespace TagLib { + + namespace APE { + + //! An implementation of APE-items + + /*! + * This class provides the features of items in the APEv2 standard. + */ + struct Item + { + enum ItemTypes { + //! Item contains text information coded in UTF-8 + Text = 0, + //! Item contains binary information + Binary = 1, + //! Item is a locator of external stored information + Locator = 2 + }; + Item(); + explicit Item(ByteVector& bin); + explicit Item(const String&, const String&); + explicit Item(const String&, const StringList &); + Item(const Item&); + Item& operator=(const Item&); + + String key() const; + ByteVector value() const; + + int size() const; + + String toString() const; + StringList toStringList() const; + + ByteVector render(); + void parse(const ByteVector&); + + void setReadOnly(bool); + + bool isReadOnly() const; + + void setType(ItemTypes type); + + ItemTypes type() const; +/* + void setValue(ByteVector); + void setValue(const String&); + void setValue(const StringList&); + */ + bool isEmpty() const; + + private: + class ItemPrivate; + ItemPrivate *d; + }; + } + +} + +#endif + + diff --git a/ape/apetag.cpp b/ape/apetag.cpp index db936e2f..0d69a9f5 100644 --- a/ape/apetag.cpp +++ b/ape/apetag.cpp @@ -26,37 +26,11 @@ #include "apetag.h" #include "apefooter.h" +#include "apeitem.h" using namespace TagLib; using namespace APE; - -static ByteVector renderAPEItem(const String &key, const Item &item) -{ - ByteVector data; - TagLib::uint flags = ((item.readOnly) ? 1 : 0) | ((item.locator) ? 2 : 0); - ByteVector value; - - if(item.value.isEmpty()) - return data; - - StringList::ConstIterator it = item.value.begin(); - value.append(it->data(String::UTF8)); - it++; - while(it != item.value.end()) { - value.append('\0'); - value.append(it->data(String::UTF8)); - it++; - } - - data.append(ByteVector::fromUInt(value.size(), false)); - data.append(ByteVector::fromUInt(flags, false)); - data.append(key.data(String::UTF8)); - data.append(ByteVector('\0')); - data.append(value); - - return data; -} - +/* static StringList parseAPEString(const ByteVector &data) { StringList value; @@ -71,7 +45,7 @@ static StringList parseAPEString(const ByteVector &data) value.append(String(data.mid(pOld), String::UTF8)); return value; -} +}*/ class APE::Tag::TagPrivate { @@ -85,24 +59,8 @@ public: Footer footer; ItemListMap itemListMap; - Map binaries; }; -APE::Item::Item(const String& str) : readOnly(false), locator(false) -{ - value.append(str); -} - -APE::Item::Item(const StringList& values) : readOnly(false), locator(false) -{ - value.append(values); -} - -bool APE::Item::isEmpty() const -{ - return value.isEmpty(); -} - //////////////////////////////////////////////////////////////////////////////// // public methods //////////////////////////////////////////////////////////////////////////////// @@ -126,40 +84,6 @@ APE::Tag::~Tag() delete d; } -ByteVector APE::Tag::render() const -{ - ByteVector data; - uint itemCount = 0; - - { - Map::Iterator i = d->itemListMap.begin(); - while(i != d->itemListMap.end()) { - if(!i->second.value.isEmpty()) { - data.append(renderAPEItem(i->first, i->second)); - itemCount++; - } - i++; - } - } - - { - Map::Iterator i = d->binaries.begin(); - while(i != d->binaries.end()) { - if(!i->second.isEmpty()) { - data.append(i->second); - itemCount++; - } - i++; - } - } - - d->footer.setItemCount(itemCount); - d->footer.setTagSize(data.size()+Footer::size()); - d->footer.setHeaderPresent(true); - - return d->footer.renderHeader() + data + d->footer.renderFooter(); -} - ByteVector APE::Tag::fileIdentifier() { return ByteVector::fromCString("APETAGEX"); @@ -169,49 +93,49 @@ String APE::Tag::title() const { if(d->itemListMap["TITLE"].isEmpty()) return String::null; - return d->itemListMap["TITLE"].value.front(); + return d->itemListMap["TITLE"].toString(); } String APE::Tag::artist() const { if(d->itemListMap["ARTIST"].isEmpty()) return String::null; - return d->itemListMap["ARTIST"].value.front(); + return d->itemListMap["ARTIST"].toString(); } String APE::Tag::album() const { if(d->itemListMap["ALBUM"].isEmpty()) return String::null; - return d->itemListMap["ALBUM"].value.front(); + return d->itemListMap["ALBUM"].toString(); } String APE::Tag::comment() const { if(d->itemListMap["COMMENT"].isEmpty()) return String::null; - return d->itemListMap["COMMENT"].value.front(); + return d->itemListMap["COMMENT"].toString(); } String APE::Tag::genre() const { if(d->itemListMap["GENRE"].isEmpty()) return String::null; - return d->itemListMap["GENRE"].value.front(); + return d->itemListMap["GENRE"].toString(); } TagLib::uint APE::Tag::year() const { if(d->itemListMap["YEAR"].isEmpty()) return 0; - return d->itemListMap["YEAR"].value.front().toInt(); + return d->itemListMap["YEAR"].toString().toInt(); } TagLib::uint APE::Tag::track() const { if(d->itemListMap["TRACK"].isEmpty()) return 0; - return d->itemListMap["TRACK"].value.front().toInt(); + return d->itemListMap["TRACK"].toString().toInt(); } void APE::Tag::setTitle(const String &s) @@ -278,9 +202,9 @@ void APE::Tag::addValue(const String &key, const String &value, bool replace) removeItem(key); if(!value.isEmpty()) { if(d->itemListMap.contains(key) || !replace) - d->itemListMap[key.upper()].value.append(value); + d->itemListMap[key.upper()].toStringList().append(value); else - setItem(key, Item(value)); + setItem(key, Item(key, value)); } } @@ -309,24 +233,38 @@ void APE::Tag::read() } } +ByteVector APE::Tag::render() const +{ + ByteVector data; + uint itemCount = 0; + + { + Map::Iterator i = d->itemListMap.begin(); + while(i != d->itemListMap.end()) { + data.append(i->second.render()); + itemCount++; + i++; + } + } + + d->footer.setItemCount(itemCount); + d->footer.setTagSize(data.size()+Footer::size()); + d->footer.setHeaderPresent(true); + + return d->footer.renderHeader() + data + d->footer.renderFooter(); +} + void APE::Tag::parse(const ByteVector &data, uint count) { uint pos = 0; while(count > 0) { - uint valueLength = data.mid(pos + 0, 4).toUInt(false); - uint flags = data.mid(pos + 4, 4).toUInt(false); - String key = String(data.mid(pos + 8), String::UTF8); APE::Item item; + item.parse(data.mid(pos)); - if(flags < 4 ) { - ByteVector val = data.mid(pos + 8 + key.size() + 1, valueLength); - d->itemListMap.insert(key.upper(), Item(parseAPEString(val))); - } - else - d->binaries.insert(key.upper(), data.mid(pos, 8 + key.size() + 1 + valueLength)); + d->itemListMap.insert(item.key().upper(), item); - pos += 8 + key.size() + 1 + valueLength; + pos += item.size(); count--; } } diff --git a/ape/apetag.h b/ape/apetag.h index 9646d085..7c6438bd 100644 --- a/ape/apetag.h +++ b/ape/apetag.h @@ -28,6 +28,8 @@ #include "tstring.h" #include "tstringlist.h" +#include "apeitem.h" + namespace TagLib { class File; @@ -36,22 +38,6 @@ namespace TagLib { class Footer; - /*! - * A non-binary APE-item. - */ - struct Item - { - Item() {}; - explicit Item(const String &); - explicit Item(const StringList &); - bool readOnly; - /*! - * The value is a URL to external data - */ - bool locator; - StringList value; - bool isEmpty() const; - }; /*! * A mapping between a list of item names, or keys, and the associated item. @@ -120,7 +106,7 @@ namespace TagLib { const ItemListMap &itemListMap() const; /*! - * Removes the \a key comment from the tag + * Removes the \a key item from the tag */ void removeItem(const String &key);