From dbc0fe07c9d508598770c6933d5d3cdef2534467 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Mon, 26 Jul 2004 19:04:40 +0000 Subject: [PATCH] Make mpc use the new APETag-structure git-svn-id: svn://anonsvn.kde.org/home/kde/trunk/kdesupport/taglib@333012 283d02a7-25f6-0310-bc7c-ecb5cbfe19da --- Makefile.am | 4 +- mpc/Makefile.am | 3 +- mpc/apetag.cpp | 368 ------------------------------------------------ mpc/apetag.h | 156 -------------------- mpc/mpcfile.cpp | 35 +++-- mpc/mpcfile.h | 2 +- 6 files changed, 22 insertions(+), 546 deletions(-) delete mode 100644 mpc/apetag.cpp delete mode 100644 mpc/apetag.h diff --git a/Makefile.am b/Makefile.am index 437e3a0e..73a5e4cf 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = toolkit mpeg ogg flac mpc +SUBDIRS = toolkit mpeg ogg flac ape mpc INCLUDES = \ -I$(top_srcdir)/taglib/toolkit \ @@ -16,7 +16,7 @@ taglib_include_HEADERS = tag.h fileref.h audioproperties.h taglib_includedir = $(includedir)/taglib libtag_la_LDFLAGS = $(all_libraries) -no-undefined -version-info 2:1:0 -libtag_la_LIBADD = ./mpeg/libmpeg.la ./ogg/libogg.la ./flac/libflac.la ./mpc/libmpc.la ./toolkit/libtoolkit.la +libtag_la_LIBADD = ./mpeg/libmpeg.la ./ogg/libogg.la ./flac/libflac.la ./mpc/libmpc.la ./ape/libape.la ./toolkit/libtoolkit.la bin_SCRIPTS = taglib-config diff --git a/mpc/Makefile.am b/mpc/Makefile.am index 01219777..4d12cb66 100644 --- a/mpc/Makefile.am +++ b/mpc/Makefile.am @@ -1,13 +1,14 @@ INCLUDES = \ -I$(top_srcdir)/taglib \ -I$(top_srcdir)/taglib/toolkit \ + -I$(top_srcdir)/taglib/ape \ -I$(top_srcdir)/taglib/mpeg/id3v1 \ -I$(top_srcdir)/taglib/mpeg/id3v2 \ $(all_includes) noinst_LTLIBRARIES = libmpc.la -libmpc_la_SOURCES = mpcfile.cpp mpcproperties.cpp apetag.cpp +libmpc_la_SOURCES = mpcfile.cpp mpcproperties.cpp taglib_include_HEADERS = mpcfile.h mpcproperties.h taglib_includedir = $(includedir)/taglib diff --git a/mpc/apetag.cpp b/mpc/apetag.cpp deleted file mode 100644 index 551540a1..00000000 --- a/mpc/apetag.cpp +++ /dev/null @@ -1,368 +0,0 @@ -/*************************************************************************** - 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 -#include -#include -#include - -#include "apetag.h" - -using namespace TagLib; -using namespace APE; - -class APE::Tag::TagPrivate -{ -public: - TagPrivate() : file(0), tagOffset(-1), tagLength(0) {} - - File *file; - long tagOffset; - long tagLength; - - FieldListMap fieldListMap; - 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 -//////////////////////////////////////////////////////////////////////////////// - -APE::Tag::Tag() : TagLib::Tag() -{ - d = new TagPrivate; -} - -APE::Tag::Tag(File *file, long tagOffset) : TagLib::Tag() -{ - d = new TagPrivate; - d->file = file; - d->tagOffset = tagOffset; - - read(); -} - -APE::Tag::~Tag() -{ - delete d; -} - -using TagLib::uint; - -static ByteVector _render_APEItem(String key, Item item) -{ - ByteVector data; - uint flags = ((item.readOnly) ? 1 : 0) | ((item.locator) ? 2 : 0); - ByteVector value; - - if (item.value.isEmpty()) return data; - - StringList::Iterator 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 ByteVector _render_APEFrame(bool isHeader, uint dataSize, uint itemCount) { - ByteVector header; - TagLib::uint tagSize = 32 + dataSize; - // bit 31: Has a header - // bit 29: Is the header - TagLib::uint flags = (1U << 31) | ((isHeader) ? (1U << 29) : 0); - - header.append(APE::Tag::fileIdentifier()); - header.append(ByteVector::fromUInt(2, false)); - header.append(ByteVector::fromUInt(tagSize, false)); - header.append(ByteVector::fromUInt(itemCount, false)); - header.append(ByteVector::fromUInt(flags, false)); - header.append(ByteVector::fromLongLong(0, false)); - - return header; -} - -ByteVector APE::Tag::render() const -{ - ByteVector data; - uint itemCount = 0; - - { Map::Iterator i = d->fieldListMap.begin(); - while (i != d->fieldListMap.end()) { - if (!i->second.value.isEmpty()) { - data.append(_render_APEItem(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++; - } - } - - ByteVector tag; - tag.append(_render_APEFrame(true, data.size(), itemCount)); - tag.append(data); - tag.append(_render_APEFrame(false, data.size(), itemCount)); - - return tag; -} - -ByteVector APE::Tag::fileIdentifier() -{ - return ByteVector::fromCString("APETAGEX"); -} - -String APE::Tag::title() const -{ - if(d->fieldListMap["TITLE"].isEmpty()) - return String::null; - return d->fieldListMap["TITLE"].value.front(); -} - -String APE::Tag::artist() const -{ - if(d->fieldListMap["ARTIST"].isEmpty()) - return String::null; - return d->fieldListMap["ARTIST"].value.front(); -} - -String APE::Tag::album() const -{ - if(d->fieldListMap["ALBUM"].isEmpty()) - return String::null; - return d->fieldListMap["ALBUM"].value.front(); -} - -String APE::Tag::comment() const -{ - if(d->fieldListMap["COMMENT"].isEmpty()) - return String::null; - return d->fieldListMap["COMMENT"].value.front(); -} - -String APE::Tag::genre() const -{ - if(d->fieldListMap["GENRE"].isEmpty()) - return String::null; - return d->fieldListMap["GENRE"].value.front(); -} - -TagLib::uint APE::Tag::year() const -{ - if(d->fieldListMap["YEAR"].isEmpty()) - return 0; - return d->fieldListMap["YEAR"].value.front().toInt(); -} - -TagLib::uint APE::Tag::track() const -{ - if(d->fieldListMap["TRACK"].isEmpty()) - return 0; - return d->fieldListMap["TRACK"].value.front().toInt(); -} - -void APE::Tag::setTitle(const String &s) -{ - addField("TITLE", s, true); -} - -void APE::Tag::setArtist(const String &s) -{ - addField("ARTIST", s, true); -} - -void APE::Tag::setAlbum(const String &s) -{ - addField("ALBUM", s, true); -} - -void APE::Tag::setComment(const String &s) -{ - addField("COMMENT", s, true); -} - -void APE::Tag::setGenre(const String &s) -{ - addField("GENRE", s, true); -} - -void APE::Tag::setYear(uint i) -{ - if(i <=0 ) - removeField("YEAR"); - else - addField("YEAR", String::number(i), true); -} - -void APE::Tag::setTrack(uint i) -{ - if(i <=0 ) - removeField("TRACK"); - else - addField("TRACK", String::number(i), true); -} - -const APE::FieldListMap& APE::Tag::fieldListMap() const -{ - return d->fieldListMap; -} - -void APE::Tag::removeField(const String &key) { - Map::Iterator it = d->fieldListMap.find(key.upper()); - if(it != d->fieldListMap.end()) - d->fieldListMap.erase(it); -} - -void APE::Tag::addField(const String &key, const String &value, bool replace) { - if(replace) - removeField(key); - if(!value.isEmpty()) { - Map::Iterator it = d->fieldListMap.find(key.upper()); - if (it != d->fieldListMap.end()) - d->fieldListMap[key].value.append(value); - else - addItem(key, Item(value)); - } -} - -void APE::Tag::addField(const String &key, const StringList &values) { - removeField(key); - - if(values.isEmpty()) return; - else { - addItem(key, Item(values)); - } -} - -TagLib::uint APE::Tag::tagSize(const ByteVector &footer) -{ - // The reported length (excl. header) - - uint length = footer.mid(12, 4).toUInt(false); - - // Flags (bit 31: tag contains a header) - - uint flags = footer.mid(20, 4).toUInt(false); - - return length + ((flags & (1U << 31)) ? 32 : 0); -} - -//////////////////////////////////////////////////////////////////////////////// -// protected methods -//////////////////////////////////////////////////////////////////////////////// - -void APE::Tag::addItem(const String &key, const Item &item) { - removeField(key); - - Map::Iterator it = d->fieldListMap.find(key.upper()); - d->fieldListMap.insert(key, item); -} - -void APE::Tag::read() -{ - if(d->file && d->file->isValid()) { - d->file->seek(d->tagOffset); - // read the footer -- always 32 bytes - ByteVector footer = d->file->readBlock(32); - - // parse footer and some initial sanity checking - if(footer.size() == 32 && footer.mid(0, 8) == "APETAGEX") { - uint length = footer.mid(12, 4).toUInt(false); - uint count = footer.mid(16, 4).toUInt(false); - d->tagLength = length; - d->file->seek(d->tagOffset + 32 - length); - ByteVector data = d->file->readBlock(length - 32); - parse(data, count); - } - else - debug("APE tag is not valid or could not be read at the specified offset."); - } -} - -static StringList _parse_APEString(ByteVector val) -{ - StringList value; - int pold = 0; - int p = val.find('\0'); - while (p != -1) { - value.append(String(val.mid(pold, p), String::UTF8)); - pold = p+1; - p = val.find('\0', pold); - }; - value.append(String(val.mid(pold), String::UTF8)); - - return value; -} - -void APE::Tag::parse(const ByteVector &data, uint count) -{ - uint pos = 0; - uint vallen, flags; - String key, value; - while(count > 0) { - vallen = data.mid(pos+0,4).toUInt(false); - flags = data.mid(pos+4,4).toUInt(false); - key = String(data.mid(pos+8), String::UTF8); - key = key.upper(); - APE::Item item; - - if (flags < 4 ) { - ByteVector val = data.mid(pos+8+key.size()+1, vallen); - d->fieldListMap.insert(key, Item(_parse_APEString(val))); - } else { - d->binaries.insert(key,data.mid(pos, 8+key.size()+1+vallen)); - } - - pos += 8 + key.size() + 1 + vallen; - count--; - } -} diff --git a/mpc/apetag.h b/mpc/apetag.h deleted file mode 100644 index 37b102d0..00000000 --- a/mpc/apetag.h +++ /dev/null @@ -1,156 +0,0 @@ -/*************************************************************************** - 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_APETAG_H -#define TAGLIB_APETAG_H - -#include "tag.h" -#include "tbytevector.h" -#include "tmap.h" -#include "tstring.h" -#include "tstringlist.h" - -namespace TagLib { - - class File; - - namespace APE { - - /*! - * A non-binary APE-item. - */ - struct Item { - Item() {}; - explicit Item(const String&); - explicit Item(const StringList&); - bool readOnly; - bool locator; // URL to external data - StringList value; - bool isEmpty() const; - }; - - /*! - * A mapping between a list of item names, or keys, and the associated item. - * - * \see APE::Tag::itemListMap() - */ - typedef Map FieldListMap; - - - //! An APE tag implementation - - class Tag : public TagLib::Tag - { - public: - /*! - * Create an APE tag with default values. - */ - Tag(); - - /*! - * Create an APE tag and parse the data in \a file with APE footer at - * \a tagOffset. - */ - Tag(File *file, long tagOffset); - - /*! - * Destroys this Tag instance. - */ - virtual ~Tag(); - - /*! - * Renders the in memory values to a ByteVector suitable for writing to - * the file. - */ - ByteVector render() const; - - /*! - * Returns the string "APETAGEX" suitable for usage in locating the tag in a - * file. - */ - static ByteVector fileIdentifier(); - - /*! - * Returns the size of the tag calculated based on the footer. - */ - static uint tagSize(const ByteVector &footer); - - // Reimplementations. - - virtual String title() const; - virtual String artist() const; - virtual String album() const; - virtual String comment() const; - virtual String genre() const; - virtual uint year() const; - virtual uint track() const; - - virtual void setTitle(const String &s); - virtual void setArtist(const String &s); - virtual void setAlbum(const String &s); - virtual void setComment(const String &s); - virtual void setGenre(const String &s); - virtual void setYear(uint i); - virtual void setTrack(uint i); - - const FieldListMap &fieldListMap() const; - - /*! - * Removes the \a key comment from the tag - */ - void removeField(const String &key); - - /*! - * Adds the \a key comment with \a value - */ - void addField(const String &key, const String &value, bool replace = true); - - /*! - * Adds the \a key comment with \a values - */ - void addField(const String &key, const StringList &values); - - protected: - /*! - * Adds the \a key comment with \a item - */ - void addItem(const String &key, const Item &item); - - /*! - * Reads from the file specified in the constructor. - */ - void read(); - /*! - * Parses the body of the tag in \a data with \a count items. - */ - void parse(const ByteVector &data, uint count); - - private: - Tag(const Tag &); - Tag &operator=(const Tag &); - - class TagPrivate; - TagPrivate *d; - }; - } -} - -#endif diff --git a/mpc/mpcfile.cpp b/mpc/mpcfile.cpp index f57301fb..060934d0 100644 --- a/mpc/mpcfile.cpp +++ b/mpc/mpcfile.cpp @@ -27,6 +27,7 @@ #include "id3v1tag.h" #include "id3v2header.h" #include "apetag.h" +#include "apefooter.h" #include "mpctag.h" using namespace TagLib; @@ -36,7 +37,6 @@ class MPC::File::FilePrivate public: FilePrivate() : APETag(0), - APEFooter(-1), APELocation(-1), APESize(0), ID3v1Tag(0), @@ -197,8 +197,13 @@ void MPC::File::read(bool readProperties, Properties::ReadStyle /* propertiesSty findAPE(); + // Look for an APE tag + + d->APELocation = findAPE(); + if(d->APELocation >= 0) { - d->APETag = new APE::Tag(this, d->APEFooter); + d->APETag = new APE::Tag(this, d->APELocation); + d->APESize = d->APETag->footer()->completeTagSize(); d->hasAPE = true; } @@ -237,26 +242,20 @@ void MPC::File::read(bool readProperties, Properties::ReadStyle /* propertiesSty } } -bool MPC::File::findAPE() +long MPC::File::findAPE() { - if(!isValid()) - return false; + if(isValid()) { + if (d->hasID3v1) + seek(-160, End); + else + seek(-32, End); - if (d->hasID3v1) - seek(-160, End); - else - seek(-32, End); - long p = tell(); + long p = tell(); - ByteVector footer = readBlock(32); - if(footer.mid(0,8) == APE::Tag::fileIdentifier()) { - d->APEFooter = p; - d->APESize = APE::Tag::tagSize(footer); - d->APELocation = p + 32 - d->APESize; - return true; + if(readBlock(8) == APE::Tag::fileIdentifier()) + return p; } - - return false; + return -1; } long MPC::File::findID3v1() diff --git a/mpc/mpcfile.h b/mpc/mpcfile.h index 900cadb4..5443c9cb 100644 --- a/mpc/mpcfile.h +++ b/mpc/mpcfile.h @@ -137,7 +137,7 @@ namespace TagLib { void read(bool readProperties, Properties::ReadStyle propertiesStyle); void scan(); - bool findAPE(); + long findAPE(); long findID3v1(); long findID3v2();