summaryrefslogtreecommitdiff
path: root/libs/taglib/taglib/mpeg/id3v2/id3v2frame.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libs/taglib/taglib/mpeg/id3v2/id3v2frame.cpp')
-rw-r--r--libs/taglib/taglib/mpeg/id3v2/id3v2frame.cpp549
1 files changed, 549 insertions, 0 deletions
diff --git a/libs/taglib/taglib/mpeg/id3v2/id3v2frame.cpp b/libs/taglib/taglib/mpeg/id3v2/id3v2frame.cpp
new file mode 100644
index 0000000000..7c76ac7b94
--- /dev/null
+++ b/libs/taglib/taglib/mpeg/id3v2/id3v2frame.cpp
@@ -0,0 +1,549 @@
+/***************************************************************************
+ copyright : (C) 2002 - 2008 by Scott Wheeler
+ email : wheeler@kde.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 *
+ * *
+ * 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/ *
+ ***************************************************************************/
+
+#ifndef HAVE_ZLIB
+#include <config.h>
+#endif
+
+#if HAVE_ZLIB
+#include <zlib.h>
+#endif
+
+#include <bitset>
+
+#include <tdebug.h>
+#include <tstringlist.h>
+
+#include "id3v2frame.h"
+#include "id3v2synchdata.h"
+
+using namespace TagLib;
+using namespace ID3v2;
+
+class Frame::FramePrivate
+{
+public:
+ FramePrivate() :
+ header(0)
+ {}
+
+ ~FramePrivate()
+ {
+ delete header;
+ }
+
+ Frame::Header *header;
+};
+
+namespace
+{
+ bool isValidFrameID(const ByteVector &frameID)
+ {
+ if(frameID.size() != 4)
+ return false;
+
+ for(ByteVector::ConstIterator it = frameID.begin(); it != frameID.end(); it++) {
+ if( (*it < 'A' || *it > 'Z') && (*it < '1' || *it > '9') ) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// static methods
+////////////////////////////////////////////////////////////////////////////////
+
+TagLib::uint Frame::headerSize()
+{
+ return Header::size();
+}
+
+TagLib::uint Frame::headerSize(uint version)
+{
+ return Header::size(version);
+}
+
+ByteVector Frame::textDelimiter(String::Type t)
+{
+ ByteVector d = char(0);
+ if(t == String::UTF16 || t == String::UTF16BE || t == String::UTF16LE)
+ d.append(char(0));
+ return d;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+Frame::~Frame()
+{
+ delete d;
+}
+
+ByteVector Frame::frameID() const
+{
+ if(d->header)
+ return d->header->frameID();
+ else
+ return ByteVector::null;
+}
+
+TagLib::uint Frame::size() const
+{
+ if(d->header)
+ return d->header->frameSize();
+ else
+ return 0;
+}
+
+void Frame::setData(const ByteVector &data)
+{
+ parse(data);
+}
+
+void Frame::setText(const String &)
+{
+
+}
+
+ByteVector Frame::render() const
+{
+ ByteVector fieldData = renderFields();
+ d->header->setFrameSize(fieldData.size());
+ ByteVector headerData = d->header->render();
+
+ return headerData + fieldData;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected members
+////////////////////////////////////////////////////////////////////////////////
+
+Frame::Frame(const ByteVector &data)
+{
+ d = new FramePrivate;
+ d->header = new Header(data);
+}
+
+Frame::Frame(Header *h)
+{
+ d = new FramePrivate;
+ d->header = h;
+}
+
+Frame::Header *Frame::header() const
+{
+ return d->header;
+}
+
+void Frame::setHeader(Header *h, bool deleteCurrent)
+{
+ if(deleteCurrent)
+ delete d->header;
+
+ d->header = h;
+}
+
+void Frame::parse(const ByteVector &data)
+{
+ if(d->header)
+ d->header->setData(data);
+ else
+ d->header = new Header(data);
+
+ parseFields(fieldData(data));
+}
+
+ByteVector Frame::fieldData(const ByteVector &frameData) const
+{
+ uint headerSize = Header::size(d->header->version());
+
+ uint frameDataOffset = headerSize;
+ uint frameDataLength = size();
+
+ if(d->header->compression() || d->header->dataLengthIndicator()) {
+ frameDataLength = SynchData::toUInt(frameData.mid(headerSize, 4));
+ frameDataOffset += 4;
+ }
+
+#if HAVE_ZLIB
+ if(d->header->compression() &&
+ !d->header->encryption())
+ {
+ ByteVector data(frameDataLength);
+ uLongf uLongTmp = frameDataLength;
+ ::uncompress((Bytef *) data.data(),
+ (uLongf *) &uLongTmp,
+ (Bytef *) frameData.data() + frameDataOffset,
+ size());
+ return data;
+ }
+ else
+#endif
+ return frameData.mid(frameDataOffset, frameDataLength);
+}
+
+String Frame::readStringField(const ByteVector &data, String::Type encoding, int *position)
+{
+ int start = 0;
+
+ if(!position)
+ position = &start;
+
+ ByteVector delimiter = textDelimiter(encoding);
+
+ int end = data.find(delimiter, *position, delimiter.size());
+
+ if(end < *position)
+ return String::null;
+
+ String str = String(data.mid(*position, end - *position), encoding);
+
+ *position = end + delimiter.size();
+
+ return str;
+}
+
+String::Type Frame::checkEncoding(const StringList &fields, String::Type encoding) // static
+{
+ if(encoding != String::Latin1)
+ return encoding;
+
+ for(StringList::ConstIterator it = fields.begin(); it != fields.end(); ++it) {
+ if(!(*it).isLatin1()) {
+ debug("Frame::checkEncoding() -- Rendering using UTF8.");
+ return String::UTF8;
+ }
+ }
+
+ return String::Latin1;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Frame::Header class
+////////////////////////////////////////////////////////////////////////////////
+
+class Frame::Header::HeaderPrivate
+{
+public:
+ HeaderPrivate() :
+ frameSize(0),
+ version(4),
+ tagAlterPreservation(false),
+ fileAlterPreservation(false),
+ readOnly(false),
+ groupingIdentity(false),
+ compression(false),
+ encryption(false),
+ unsynchronisation(false),
+ dataLengthIndicator(false)
+ {}
+
+ ByteVector frameID;
+ uint frameSize;
+ uint version;
+
+ // flags
+
+ bool tagAlterPreservation;
+ bool fileAlterPreservation;
+ bool readOnly;
+ bool groupingIdentity;
+ bool compression;
+ bool encryption;
+ bool unsynchronisation;
+ bool dataLengthIndicator;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// static members (Frame::Header)
+////////////////////////////////////////////////////////////////////////////////
+
+TagLib::uint Frame::Header::size()
+{
+ return size(4);
+}
+
+TagLib::uint Frame::Header::size(uint version)
+{
+ switch(version) {
+ case 0:
+ case 1:
+ case 2:
+ return 6;
+ case 3:
+ case 4:
+ default:
+ return 10;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// public members (Frame::Header)
+////////////////////////////////////////////////////////////////////////////////
+
+Frame::Header::Header(const ByteVector &data, bool synchSafeInts)
+{
+ d = new HeaderPrivate;
+ setData(data, synchSafeInts);
+}
+
+Frame::Header::Header(const ByteVector &data, uint version)
+{
+ d = new HeaderPrivate;
+ setData(data, version);
+}
+
+Frame::Header::~Header()
+{
+ delete d;
+}
+
+void Frame::Header::setData(const ByteVector &data, bool synchSafeInts)
+{
+ setData(data, uint(synchSafeInts ? 4 : 3));
+}
+
+void Frame::Header::setData(const ByteVector &data, uint version)
+{
+ d->version = version;
+
+ switch(version) {
+ case 0:
+ case 1:
+ case 2:
+ {
+ // ID3v2.2
+
+ if(data.size() < 3) {
+ debug("You must at least specify a frame ID.");
+ return;
+ }
+
+ // Set the frame ID -- the first three bytes
+
+ d->frameID = data.mid(0, 3);
+
+ // If the full header information was not passed in, do not continue to the
+ // steps to parse the frame size and flags.
+
+ if(data.size() < 6) {
+ d->frameSize = 0;
+ return;
+ }
+
+ d->frameSize = data.mid(3, 3).toUInt();
+
+ break;
+ }
+ case 3:
+ {
+ // ID3v2.3
+
+ if(data.size() < 4) {
+ debug("You must at least specify a frame ID.");
+ return;
+ }
+
+ // Set the frame ID -- the first four bytes
+
+ d->frameID = data.mid(0, 4);
+
+ // If the full header information was not passed in, do not continue to the
+ // steps to parse the frame size and flags.
+
+ if(data.size() < 10) {
+ d->frameSize = 0;
+ return;
+ }
+
+ // Set the size -- the frame size is the four bytes starting at byte four in
+ // the frame header (structure 4)
+
+ d->frameSize = data.mid(4, 4).toUInt();
+
+ { // read the first byte of flags
+ std::bitset<8> flags(data[8]);
+ d->tagAlterPreservation = flags[7]; // (structure 3.3.1.a)
+ d->fileAlterPreservation = flags[6]; // (structure 3.3.1.b)
+ d->readOnly = flags[5]; // (structure 3.3.1.c)
+ }
+
+ { // read the second byte of flags
+ std::bitset<8> flags(data[9]);
+ d->compression = flags[7]; // (structure 3.3.1.i)
+ d->encryption = flags[6]; // (structure 3.3.1.j)
+ d->groupingIdentity = flags[5]; // (structure 3.3.1.k)
+ }
+ break;
+ }
+ case 4:
+ default:
+ {
+ // ID3v2.4
+
+ if(data.size() < 4) {
+ debug("You must at least specify a frame ID.");
+ return;
+ }
+
+ // Set the frame ID -- the first four bytes
+
+ d->frameID = data.mid(0, 4);
+
+ // If the full header information was not passed in, do not continue to the
+ // steps to parse the frame size and flags.
+
+ if(data.size() < 10) {
+ d->frameSize = 0;
+ return;
+ }
+
+ // Set the size -- the frame size is the four bytes starting at byte four in
+ // the frame header (structure 4)
+
+ d->frameSize = SynchData::toUInt(data.mid(4, 4));
+#ifndef NO_ITUNES_HACKS
+ // iTunes writes v2.4 tags with v2.3-like frame sizes
+ if(d->frameSize > 127) {
+ if(!isValidFrameID(data.mid(d->frameSize + 10, 4))) {
+ unsigned int uintSize = data.mid(4, 4).toUInt();
+ if(isValidFrameID(data.mid(uintSize + 10, 4))) {
+ d->frameSize = uintSize;
+ }
+ }
+ }
+#endif
+
+ { // read the first byte of flags
+ std::bitset<8> flags(data[8]);
+ d->tagAlterPreservation = flags[6]; // (structure 4.1.1.a)
+ d->fileAlterPreservation = flags[5]; // (structure 4.1.1.b)
+ d->readOnly = flags[4]; // (structure 4.1.1.c)
+ }
+
+ { // read the second byte of flags
+ std::bitset<8> flags(data[9]);
+ d->groupingIdentity = flags[6]; // (structure 4.1.2.h)
+ d->compression = flags[3]; // (structure 4.1.2.k)
+ d->encryption = flags[2]; // (structure 4.1.2.m)
+ d->unsynchronisation = flags[1]; // (structure 4.1.2.n)
+ d->dataLengthIndicator = flags[0]; // (structure 4.1.2.p)
+ }
+ break;
+ }
+ }
+}
+
+ByteVector Frame::Header::frameID() const
+{
+ return d->frameID;
+}
+
+void Frame::Header::setFrameID(const ByteVector &id)
+{
+ d->frameID = id.mid(0, 4);
+}
+
+TagLib::uint Frame::Header::frameSize() const
+{
+ return d->frameSize;
+}
+
+void Frame::Header::setFrameSize(uint size)
+{
+ d->frameSize = size;
+}
+
+TagLib::uint Frame::Header::version() const
+{
+ return d->version;
+}
+
+bool Frame::Header::tagAlterPreservation() const
+{
+ return d->tagAlterPreservation;
+}
+
+void Frame::Header::setTagAlterPreservation(bool preserve)
+{
+ d->tagAlterPreservation = preserve;
+}
+
+bool Frame::Header::fileAlterPreservation() const
+{
+ return d->fileAlterPreservation;
+}
+
+bool Frame::Header::readOnly() const
+{
+ return d->readOnly;
+}
+
+bool Frame::Header::groupingIdentity() const
+{
+ return d->groupingIdentity;
+}
+
+bool Frame::Header::compression() const
+{
+ return d->compression;
+}
+
+bool Frame::Header::encryption() const
+{
+ return d->encryption;
+}
+
+bool Frame::Header::unsycronisation() const
+{
+ return unsynchronisation();
+}
+
+bool Frame::Header::unsynchronisation() const
+{
+ return d->unsynchronisation;
+}
+
+bool Frame::Header::dataLengthIndicator() const
+{
+ return d->dataLengthIndicator;
+}
+
+ByteVector Frame::Header::render() const
+{
+ ByteVector flags(2, char(0)); // just blank for the moment
+
+ ByteVector v = d->frameID + SynchData::fromUInt(d->frameSize) + flags;
+
+ return v;
+}
+
+bool Frame::Header::frameAlterPreservation() const
+{
+ return fileAlterPreservation();
+}