One more step. Wow, this was impressively broken.

git-svn-id: svn://anonsvn.kde.org/home/kde/trunk/kdesupport/taglib@588016 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
This commit is contained in:
Scott Wheeler
2006-09-24 16:59:54 +00:00
parent d0bfbf8ce4
commit 2ae6a804e2
9 changed files with 0 additions and 0 deletions

1
ape/CMakeLists.txt Normal file
View File

@ -0,0 +1 @@
INSTALL( FILES apetag.h apefooter.h apeitem.h DESTINATION ${INCLUDE_INSTALL_DIR}/taglib)

13
ape/Makefile.am Normal file
View File

@ -0,0 +1,13 @@
INCLUDES = \
-I$(top_srcdir)/taglib \
-I$(top_srcdir)/taglib/toolkit \
$(all_includes)
noinst_LTLIBRARIES = libape.la
libape_la_SOURCES = apetag.cpp apefooter.cpp apeitem.cpp
taglib_include_HEADERS = apetag.h apefooter.h apeitem.h
taglib_includedir = $(includedir)/taglib
EXTRA_DIST = $(libape_la_SOURCES) $(taglib_include_HEADERS)

170
ape/ape-tag-format.txt Normal file
View File

@ -0,0 +1,170 @@
================================================================================
= APE Tag Specification, Version 2.000
================================================================================
Original Content (C) 2004, Frank Klemm <frank.klemm@elster.offl.uni-jena.de>
Formatting / Editing (C) 2004, Scott Wheeler <wheeler@kde.org>
================================================================================
= Contents
================================================================================
1 - APE Tag General Structure
2 - APE Tag Header / Footer Format
3 - APE Tag Flags
4 - APE Tag Item Format
5 - APE Tag Item Supported Keys
6 - APE Tag Item Content
7 - Data Types
7.1 - Data Types / UTF-8
7.2 - Data Types / Dates
7.3 - Data Types / Timestamps
================================================================================
= 1 - APE Tag General Structure
================================================================================
Member of Basic Components of SV8 Stream Note:
It is strongly recommended that the data size be stored in the tags. The size
should normally be in the roughly one kilobyte, never more than 8 kilobytes.
Larger data should be stored externally using link entries. Linked data is much
easier to process by normal programs, so for instance JPEG data should not be
included inside the audio file.
APE Tag Version 2.000 (with header, recommended):
/================================\
| APE Tag Header | 32 bytes |
|-------------------|------------|
| APE Tag Item 1 | > 10 bytes |
| APE Tag Item 2 | > 10 bytes |
| APE Tag Item n-1 | > 10 bytes |
| APE Tag Item n | > 10 bytes |
|-------------------|------------|
| APE Tag Footer | 32 bytes |
\================================/
APE tag items should be sorted ascending by size. When streaming, parts of the
APE tag may be dropped to reduce the danger of drop outs between tracks. This
is not required, but is strongly recommended. It would be desirable for the i
tems to be sorted by importance / size, but this is not feasible. This
convention should only be broken when adding less important small items and it
is not desirable to rewrite the entire tag. An APE tag at the end of a file
(the recommended location) must have at least a footer; an APE tag at the
beginning of a file (strongly discouraged) must have at least a header.
APE Tag Version 1.000 (without header, deprecated)
/================================\
| APE Tag Item 1 | > 10 bytes |
| APE Tag Item 2 | > 10 bytes |
| APE Tag Item n-1 | > 10 bytes |
| APE Tag Item n | > 10 bytes |
|-------------------|------------|
| APE Tag Footer | 32 bytes |
\================================/
================================================================================
= 2 - APE Tag Header / Footer Format
================================================================================
Contains number, length and attributes of all tag items
Header and Footer are different in 1 bit in the Tags Flags to distinguish
between them.
Member of APE Tag 2.0
/===========================================================================\
| Preamble | 8 bytes | { 'A', 'P', 'E', 'T', 'A', 'G', 'E', 'X' } |
|----------------|---------|------------------------------------------------|
| Version Number | 4 bytes | 1000 = Version 1.000, 2000 = Version 2.000 |
|----------------|---------|------------------------------------------------|
| Tag Size | 4 bytes | Tag size in bytes including footer and all tag |
| | | items excluding the header (for 1.000 |
| | | compatibility) |
|----------------|---------|------------------------------------------------|
| Item Count | 4 bytes | Number of items in the tag |
|----------------|---------|------------------------------------------------|
| Tag Flags | 4 bytes | Global flags |
|----------------|---------|------------------------------------------------|
| Reserved | 8 bytes | Must be zeroed |
\===========================================================================/
================================================================================
= 3 - APE Tag Flags
================================================================================
The general flag structure for either items or headers / footers is the same.
Bits 31, 30 and 29 are specific to headers / footers, whereas 2 through 0 are
item specific.
Note: APE Tags from Version 1.0 do not use any of the following. All flags in
that version are zeroed and ignored when reading.
/=================================================================\
| Contains Header | Bit 31 | 1 - has header | 0 - no header |
|-----------------|-------------|---------------------------------|
| Contains Footer | Bit 30 | 1 - has footer | 0 - no footer |
|-----------------|-------------|---------------------------------|
| Is Header | Bit 29 | 1 - is header | 0 - is footer |
|-----------------|-------------|---------------------------------|
| Undefined | Bits 28 - 3 | Undefined, must be zeroed |
|-----------------|-------------|---------------------------------|
| Encoding | Bits 2 - 1 | 00 - UTF-8 |
| | | 01 - Binary Data * |
| | | 10 - External Reference ** |
| | | 11 - Reserved |
|-----------------|-------------|---------------------------------|
| Read Only | Bit 0 | 1 - read only | 0 - read/write |
\=================================================================/
(*) Should be ignored by tools for editing text values
(**) Allowed external reference formats:
- http://host/directory/filename.ext
- ftp://host/directory/filename.ext
- filename.ext
- /directory/filename.ext
- DRIVE:/directory/filename.ext
Note: External references are also UTF-8 encoded.
================================================================================
= 4 - APE Tag Item Format
================================================================================
APE Tag Items are stored as key-value pairs. APE Tags Item Key are case
sensitive, however it is illegal to use keys which only differ in case and
it is recommended that tag reading not be case sensitive.
Every key can only occur (at most) once. It is not possible to repeat a key
to signify updated contents.
Tags can be partially or completely repeated in the streaming format. This
makes it possible to display an artist and / or title if it was missed at the
beginning of the stream. It is recommended that the important information like
artist, album and title should occur approximately every 2 minutes in the
stream and again 5 to 10 seconds before the end. However, care should be tak
en not to replicate this information too often or during passages with high
bitrate demands to avoid unnecessary drop-outs.
/==============================================================================\
| Content Size | 4 bytes | Length of the value in bytes |
|----------------|---------------|---------------------------------------------|
| Flags | 4 bytes | Item flags |
|----------------|---------------|---------------------------------------------|
| Key | 2 - 255 bytes | Item key |
|----------------|---------------|---------------------------------------------|
| Key Terminator | 1 byte | Null byte that indicates the end of the key |
|----------------|---------------|---------------------------------------------|
| Value | variable | Content (formatted according to the flags) |
\==============================================================================/
================================================================================
Sections 5 - 7 haven't yet been converted from:
http://www.personal.uni-jena.de/~pfk/mpp/sv8/apetag.html

232
ape/apefooter.cpp Normal file
View File

@ -0,0 +1,232 @@
/***************************************************************************
copyright : (C) 2004 by Allan Sandfeld Jensen
(C) 2002, 2003 by Scott Wheeler (id3v2header.cpp)
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 *
***************************************************************************/
#include <iostream>
#include <bitset>
#include <tstring.h>
#include <tdebug.h>
#include "apefooter.h"
using namespace TagLib;
using namespace APE;
class Footer::FooterPrivate
{
public:
FooterPrivate() : version(0),
footerPresent(true),
headerPresent(false),
isHeader(false),
itemCount(0),
tagSize(0) {}
~FooterPrivate() {}
uint version;
bool footerPresent;
bool headerPresent;
bool isHeader;
uint itemCount;
uint tagSize;
static const uint size = 32;
};
////////////////////////////////////////////////////////////////////////////////
// static members
////////////////////////////////////////////////////////////////////////////////
TagLib::uint Footer::size()
{
return FooterPrivate::size;
}
ByteVector Footer::fileIdentifier()
{
return ByteVector::fromCString("APETAGEX");
}
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
Footer::Footer()
{
d = new FooterPrivate;
}
Footer::Footer(const ByteVector &data)
{
d = new FooterPrivate;
parse(data);
}
Footer::~Footer()
{
delete d;
}
TagLib::uint Footer::version() const
{
return d->version;
}
bool Footer::headerPresent() const
{
return d->headerPresent;
}
bool Footer::footerPresent() const
{
return d->footerPresent;
}
bool Footer::isHeader() const
{
return d->isHeader;
}
void Footer::setHeaderPresent(bool b) const
{
d->headerPresent = b;
}
TagLib::uint Footer::itemCount() const
{
return d->itemCount;
}
void Footer::setItemCount(uint s)
{
d->itemCount = s;
}
TagLib::uint Footer::tagSize() const
{
return d->tagSize;
}
TagLib::uint Footer::completeTagSize() const
{
if(d->headerPresent)
return d->tagSize + d->size;
else
return d->tagSize;
}
void Footer::setTagSize(uint s)
{
d->tagSize = s;
}
void Footer::setData(const ByteVector &data)
{
parse(data);
}
ByteVector Footer::renderFooter() const
{
return render(false);
}
ByteVector Footer::renderHeader() const
{
if (!d->headerPresent) return ByteVector();
return render(true);
}
////////////////////////////////////////////////////////////////////////////////
// protected members
////////////////////////////////////////////////////////////////////////////////
void Footer::parse(const ByteVector &data)
{
if(data.size() < size())
return;
// The first eight bytes, data[0..7], are the File Identifier, "APETAGEX".
// Read the version number
d->version = data.mid(8, 4).toUInt(false);
// Read the tag size
d->tagSize = data.mid(12, 4).toUInt(false);
// Read the item count
d->itemCount = data.mid(16, 4).toUInt(false);
// Read the flags
std::bitset<32> flags(data.mid(20, 4).toUInt(false));
d->headerPresent = flags[31];
d->footerPresent = !flags[30];
d->isHeader = flags[29];
}
ByteVector Footer::render(bool isHeader) const
{
ByteVector v;
// add the file identifier -- "APETAGEX"
v.append(fileIdentifier());
// add the version number -- we always render a 2.000 tag regardless of what
// the tag originally was.
v.append(ByteVector::fromUInt(2000, false));
// add the tag size
v.append(ByteVector::fromUInt(d->tagSize, false));
// add the item count
v.append(ByteVector::fromUInt(d->itemCount, false));
// render and add the flags
std::bitset<32> flags;
flags[31] = d->headerPresent;
flags[30] = false; // footer is always present
flags[29] = isHeader;
v.append(ByteVector::fromUInt(flags.to_ulong(), false));
// add the reserved 64bit
v.append(ByteVector::fromLongLong(0));
return v;
}

168
ape/apefooter.h Normal file
View File

@ -0,0 +1,168 @@
/***************************************************************************
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_APEFOOTER_H
#define TAGLIB_APEFOOTER_H
#include "tbytevector.h"
namespace TagLib {
namespace APE {
//! An implementation of APE footers
/*!
* This class implements APE footers (and headers). It attempts to follow, both
* semantically and programatically, the structure specified in
* the APE v2.0 standard. The API is based on the properties of APE footer and
* headers specified there.
*/
class Footer
{
public:
/*!
* Constructs an empty APE footer.
*/
Footer();
/*!
* Constructs an APE footer based on \a data. parse() is called
* immediately.
*/
Footer(const ByteVector &data);
/*!
* Destroys the footer.
*/
virtual ~Footer();
/*!
* Returns the version number. (Note: This is the 1000 or 2000.)
*/
uint version() const;
/*!
* Returns true if a header is present in the tag.
*/
bool headerPresent() const;
/*!
* Returns true if a footer is present in the tag.
*/
bool footerPresent() const;
/*!
* Returns true this is actually the header.
*/
bool isHeader() const;
/*!
* Sets whether the header should be rendered or not
*/
void setHeaderPresent(bool b) const;
/*!
* Returns the number of items in the tag.
*/
uint itemCount() const;
/*!
* Set the item count to \a s.
* \see itemCount()
*/
void setItemCount(uint s);
/*!
* Returns the tag size in bytes. This is the size of the frame content and footer.
* The size of the \e entire tag will be this plus the header size, if present.
*
* \see completeTagSize()
*/
uint tagSize() const;
/*!
* Returns the tag size, including if present, the header
* size.
*
* \see tagSize()
*/
uint completeTagSize() const;
/*!
* Set the tag size to \a s.
* \see tagSize()
*/
void setTagSize(uint s);
/*!
* Returns the size of the footer. Presently this is always 32 bytes.
*/
static uint size();
/*!
* Returns the string used to identify an APE tag inside of a file.
* Presently this is always "APETAGEX".
*/
static ByteVector fileIdentifier();
/*!
* Sets the data that will be used as the footer. 32 bytes,
* starting from \a data will be used.
*/
void setData(const ByteVector &data);
/*!
* Renders the footer back to binary format.
*/
ByteVector renderFooter() const;
/*!
* Renders the header corresponding to the footer. If headerPresent is
* set to false, it returns an empty ByteVector.
*/
ByteVector renderHeader() const;
protected:
/*!
* Called by setData() to parse the footer data. It makes this information
* available through the public API.
*/
void parse(const ByteVector &data);
/*!
* Called by renderFooter and renderHeader
*/
ByteVector render(bool isHeader) const;
private:
Footer(const Footer &);
Footer &operator=(const Footer &);
class FooterPrivate;
FooterPrivate *d;
};
}
}
#endif

220
ape/apeitem.cpp Normal file
View File

@ -0,0 +1,220 @@
/***************************************************************************
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 <tbytevectorlist.h>
#include <tdebug.h>
#include "apeitem.h"
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 &value)
{
d = new ItemPrivate;
d->key = key;
d->text.append(value);
}
APE::Item::Item(const String &key, const StringList &values)
{
d = new ItemPrivate;
d->key = key;
d->text = values;
}
APE::Item::Item(const Item &item)
{
d = new ItemPrivate(*item.d);
}
APE::Item::~Item()
{
delete d;
}
Item &APE::Item::operator=(const Item &item)
{
delete d;
d = new ItemPrivate(*item.d);
return *this;
}
void APE::Item::setReadOnly(bool readOnly)
{
d->readOnly = readOnly;
}
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
{
// This seems incorrect as it won't be actually rendering the value to keep it
// up to date.
return d->value;
}
void APE::Item::setKey(const String &key)
{
d->key = key;
}
void APE::Item::setValue(const String &value)
{
d->text = value;
}
void APE::Item::setValues(const StringList &value)
{
d->text = value;
}
void APE::Item::appendValue(const String &value)
{
d->text.append(value);
}
void APE::Item::appendValues(const StringList &values)
{
d->text.append(values);
}
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 isEmpty() ? String::null : 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().isEmpty())
return true;
return false;
case 2:
return d->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;
}
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)
d->text = StringList(ByteVectorList::split(d->value, '\0'), String::UTF8);
}
ByteVector APE::Item::render() const
{
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++;
for(; it != d->text.end(); ++it) {
value.append('\0');
value.append(it->data(String::UTF8));
}
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;
}

193
ape/apeitem.h Normal file
View File

@ -0,0 +1,193 @@
/***************************************************************************
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 "tbytevector.h"
#include "tstring.h"
#include "tstringlist.h"
namespace TagLib {
namespace APE {
//! An implementation of APE-items
/*!
* This class provides the features of items in the APEv2 standard.
*/
class Item
{
public:
/*!
* Enum of types an Item can have. The value of 3 is reserved.
*/
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
};
/*!
* Constructs an empty item.
*/
Item();
/*!
* Constructs an item with \a key and \a value.
*/
Item(const String &key, const String &value);
/*!
* Constructs an item with \a key and \a values.
*/
Item(const String &key, const StringList &values);
/*!
* Construct an item as a copy of \a item.
*/
Item(const Item &item);
/*!
* Destroys the item.
*/
virtual ~Item();
/*!
* Copies the contents of \a item into this item.
*/
Item &operator=(const Item &item);
/*!
* Returns the key.
*/
String key() const;
/*!
* Returns the binary value.
*
* \deprecated This will be removed in the next binary incompatible version
* as it is not kept in sync with the things that are set using setValue()
* and friends.
*/
ByteVector value() const;
/*!
* Sets the key for the item to \a key.
*/
void setKey(const String &key);
/*!
* Sets the value of the item to \a value and clears any previous contents.
*
* \see toString()
*/
void setValue(const String &value);
/*!
* Sets the value of the item to the list of values in \a value and clears
* any previous contents.
*
* \see toStringList()
*/
void setValues(const StringList &values);
/*!
* Appends \a value to create (or extend) the current list of values.
*
* \see toString()
*/
void appendValue(const String &value);
/*!
* Appends \a values to extend the current list of values.
*
* \see toStringList()
*/
void appendValues(const StringList &values);
/*!
* Returns the size of the full item.
*/
int size() const;
/*!
* Returns the value as a single string. In case of multiple strings,
* the first is returned.
*/
String toString() const;
/*!
* Returns the value as a string list.
*/
StringList toStringList() const;
/*!
* Render the item to a ByteVector.
*/
ByteVector render() const;
/*!
* Parse the item from the ByteVector \a data.
*/
void parse(const ByteVector& data);
/*!
* Set the item to read-only.
*/
void setReadOnly(bool readOnly);
/*!
* Return true if the item is read-only.
*/
bool isReadOnly() const;
/*!
* Sets the type of the item to \a type.
*
* \see ItemTypes
*/
void setType(ItemTypes type);
/*!
* Returns the type of the item.
*/
ItemTypes type() const;
/*!
* Returns if the item has any real content.
*/
bool isEmpty() const;
private:
class ItemPrivate;
ItemPrivate *d;
};
}
}
#endif

255
ape/apetag.cpp Normal file
View File

@ -0,0 +1,255 @@
/***************************************************************************
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"
#include "apefooter.h"
#include "apeitem.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;
Footer footer;
ItemListMap itemListMap;
};
////////////////////////////////////////////////////////////////////////////////
// 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;
}
ByteVector APE::Tag::fileIdentifier()
{
return ByteVector::fromCString("APETAGEX");
}
String APE::Tag::title() const
{
if(d->itemListMap["TITLE"].isEmpty())
return String::null;
return d->itemListMap["TITLE"].toString();
}
String APE::Tag::artist() const
{
if(d->itemListMap["ARTIST"].isEmpty())
return String::null;
return d->itemListMap["ARTIST"].toString();
}
String APE::Tag::album() const
{
if(d->itemListMap["ALBUM"].isEmpty())
return String::null;
return d->itemListMap["ALBUM"].toString();
}
String APE::Tag::comment() const
{
if(d->itemListMap["COMMENT"].isEmpty())
return String::null;
return d->itemListMap["COMMENT"].toString();
}
String APE::Tag::genre() const
{
if(d->itemListMap["GENRE"].isEmpty())
return String::null;
return d->itemListMap["GENRE"].toString();
}
TagLib::uint APE::Tag::year() const
{
if(d->itemListMap["YEAR"].isEmpty())
return 0;
return d->itemListMap["YEAR"].toString().toInt();
}
TagLib::uint APE::Tag::track() const
{
if(d->itemListMap["TRACK"].isEmpty())
return 0;
return d->itemListMap["TRACK"].toString().toInt();
}
void APE::Tag::setTitle(const String &s)
{
addValue("TITLE", s, true);
}
void APE::Tag::setArtist(const String &s)
{
addValue("ARTIST", s, true);
}
void APE::Tag::setAlbum(const String &s)
{
addValue("ALBUM", s, true);
}
void APE::Tag::setComment(const String &s)
{
addValue("COMMENT", s, true);
}
void APE::Tag::setGenre(const String &s)
{
addValue("GENRE", s, true);
}
void APE::Tag::setYear(uint i)
{
if(i <= 0)
removeItem("YEAR");
else
addValue("YEAR", String::number(i), true);
}
void APE::Tag::setTrack(uint i)
{
if(i <= 0)
removeItem("TRACK");
else
addValue("TRACK", String::number(i), true);
}
APE::Footer *APE::Tag::footer() const
{
return &d->footer;
}
const APE::ItemListMap& APE::Tag::itemListMap() const
{
return d->itemListMap;
}
void APE::Tag::removeItem(const String &key)
{
Map<const String, Item>::Iterator it = d->itemListMap.find(key.upper());
if(it != d->itemListMap.end())
d->itemListMap.erase(it);
}
void APE::Tag::addValue(const String &key, const String &value, bool replace)
{
if(replace)
removeItem(key);
if(!value.isEmpty()) {
if(d->itemListMap.contains(key) || !replace)
d->itemListMap[key.upper()].appendValue(value);
else
setItem(key, Item(key, value));
}
}
void APE::Tag::setItem(const String &key, const Item &item)
{
d->itemListMap.insert(key.upper(), item);
}
////////////////////////////////////////////////////////////////////////////////
// protected methods
////////////////////////////////////////////////////////////////////////////////
void APE::Tag::read()
{
if(d->file && d->file->isValid()) {
d->file->seek(d->tagOffset);
d->footer.setData(d->file->readBlock(Footer::size()));
if(d->footer.tagSize() == 0 ||
d->footer.tagSize() > uint(d->file->length()))
return;
d->file->seek(d->tagOffset + Footer::size() - d->footer.tagSize());
parse(d->file->readBlock(d->footer.tagSize() - Footer::size()));
}
}
ByteVector APE::Tag::render() const
{
ByteVector data;
uint itemCount = 0;
{
for(Map<const String, Item>::ConstIterator it = d->itemListMap.begin();
it != d->itemListMap.end(); ++it)
{
data.append(it->second.render());
itemCount++;
}
}
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 pos = 0;
// 11 bytes is the minimum size for an APE item
for(uint i = 0; i < d->footer.itemCount() && pos <= data.size() - 11; i++) {
APE::Item item;
item.parse(data.mid(pos));
d->itemListMap.insert(item.key().upper(), item);
pos += item.size();
}
}

157
ape/apetag.h Normal file
View File

@ -0,0 +1,157 @@
/***************************************************************************
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 "apeitem.h"
namespace TagLib {
class File;
//! An implementation of the APE tagging format
namespace APE {
class Footer;
/*!
* A mapping between a list of item names, or keys, and the associated item.
*
* \see APE::Tag::itemListMap()
*/
typedef Map<const String, Item> ItemListMap;
//! 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();
// 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);
/*!
* Returns a pointer to the tag's footer.
*/
Footer *footer() const;
/*!
* Returns a reference to the item list map. This is an ItemListMap of
* all of the items in the tag.
*
* This is the most powerfull structure for accessing the items of the tag.
*
* \warning You should not modify this data structure directly, instead
* use setItem() and removeItem().
*/
const ItemListMap &itemListMap() const;
/*!
* Removes the \a key item from the tag
*/
void removeItem(const String &key);
/*!
* Adds to the item specified by \a key the data \a value. If \a replace
* is true, then all of the other values on the same key will be removed
* first.
*/
void addValue(const String &key, const String &value, bool replace = true);
/*!
* Sets the \a key item to the value of \a item. If an item with the \a key is already
* present, it will be replaced.
*/
void setItem(const String &key, const Item &item);
protected:
/*!
* Reads from the file specified in the constructor.
*/
void read();
/*!
* Parses the body of the tag in \a data.
*/
void parse(const ByteVector &data);
private:
Tag(const Tag &);
Tag &operator=(const Tag &);
class TagPrivate;
TagPrivate *d;
};
}
}
#endif