+++ /dev/null
-/***************************************************************************
- 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 <tdebug.h>
-#include <tfile.h>
-#include <tstring.h>
-#include <tmap.h>
-
-#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<const String, ByteVector> 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<const String,Item>::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<String,ByteVector>::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<const String, Item>::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<const String, Item>::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<const String, Item>::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--;
- }
-}
+++ /dev/null
-/***************************************************************************
- 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<const String, Item> 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