1 /***************************************************************************
2 copyright : (C) 2002 - 2008 by Scott Wheeler
3 email : wheeler@kde.org
4 ***************************************************************************/
6 /***************************************************************************
7 * This library is free software; you can redistribute it and/or modify *
8 * it under the terms of the GNU Lesser General Public License version *
9 * 2.1 as published by the Free Software Foundation. *
11 * This library is distributed in the hope that it will be useful, but *
12 * WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
14 * Lesser General Public License for more details. *
16 * You should have received a copy of the GNU Lesser General Public *
17 * License along with this library; if not, write to the Free Software *
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
21 * Alternatively, this file is available under the Mozilla Public *
22 * License Version 1.1. You may obtain a copy of the License at *
23 * http://www.mozilla.org/MPL/ *
24 ***************************************************************************/
39 #include <tstringlist.h>
41 #include "id3v2frame.h"
42 #include "id3v2synchdata.h"
44 using namespace TagLib;
45 using namespace ID3v2;
47 class Frame::FramePrivate
59 Frame::Header *header;
64 bool isValidFrameID(const ByteVector &frameID)
66 if(frameID.size() != 4)
69 for(ByteVector::ConstIterator it = frameID.begin(); it != frameID.end(); it++) {
70 if( (*it < 'A' || *it > 'Z') && (*it < '1' || *it > '9') ) {
78 ////////////////////////////////////////////////////////////////////////////////
80 ////////////////////////////////////////////////////////////////////////////////
82 TagLib::uint Frame::headerSize()
84 return Header::size();
87 TagLib::uint Frame::headerSize(uint version)
89 return Header::size(version);
92 ByteVector Frame::textDelimiter(String::Type t)
94 ByteVector d = char(0);
95 if(t == String::UTF16 || t == String::UTF16BE || t == String::UTF16LE)
100 ////////////////////////////////////////////////////////////////////////////////
102 ////////////////////////////////////////////////////////////////////////////////
109 ByteVector Frame::frameID() const
112 return d->header->frameID();
114 return ByteVector::null;
117 TagLib::uint Frame::size() const
120 return d->header->frameSize();
125 void Frame::setData(const ByteVector &data)
130 void Frame::setText(const String &)
135 ByteVector Frame::render() const
137 ByteVector fieldData = renderFields();
138 d->header->setFrameSize(fieldData.size());
139 ByteVector headerData = d->header->render();
141 return headerData + fieldData;
144 ////////////////////////////////////////////////////////////////////////////////
146 ////////////////////////////////////////////////////////////////////////////////
148 Frame::Frame(const ByteVector &data)
150 d = new FramePrivate;
151 d->header = new Header(data);
154 Frame::Frame(Header *h)
156 d = new FramePrivate;
160 Frame::Header *Frame::header() const
165 void Frame::setHeader(Header *h, bool deleteCurrent)
173 void Frame::parse(const ByteVector &data)
176 d->header->setData(data);
178 d->header = new Header(data);
180 parseFields(fieldData(data));
183 ByteVector Frame::fieldData(const ByteVector &frameData) const
185 uint headerSize = Header::size(d->header->version());
187 uint frameDataOffset = headerSize;
188 uint frameDataLength = size();
190 if(d->header->compression() || d->header->dataLengthIndicator()) {
191 frameDataLength = SynchData::toUInt(frameData.mid(headerSize, 4));
192 frameDataOffset += 4;
196 if(d->header->compression() &&
197 !d->header->encryption())
199 ByteVector data(frameDataLength);
200 uLongf uLongTmp = frameDataLength;
201 ::uncompress((Bytef *) data.data(),
202 (uLongf *) &uLongTmp,
203 (Bytef *) frameData.data() + frameDataOffset,
209 return frameData.mid(frameDataOffset, frameDataLength);
212 String Frame::readStringField(const ByteVector &data, String::Type encoding, int *position)
219 ByteVector delimiter = textDelimiter(encoding);
221 int end = data.find(delimiter, *position, delimiter.size());
226 String str = String(data.mid(*position, end - *position), encoding);
228 *position = end + delimiter.size();
233 String::Type Frame::checkEncoding(const StringList &fields, String::Type encoding) // static
235 if(encoding != String::Latin1)
238 for(StringList::ConstIterator it = fields.begin(); it != fields.end(); ++it) {
239 if(!(*it).isLatin1()) {
240 debug("Frame::checkEncoding() -- Rendering using UTF8.");
245 return String::Latin1;
248 ////////////////////////////////////////////////////////////////////////////////
249 // Frame::Header class
250 ////////////////////////////////////////////////////////////////////////////////
252 class Frame::Header::HeaderPrivate
258 tagAlterPreservation(false),
259 fileAlterPreservation(false),
261 groupingIdentity(false),
264 unsynchronisation(false),
265 dataLengthIndicator(false)
274 bool tagAlterPreservation;
275 bool fileAlterPreservation;
277 bool groupingIdentity;
280 bool unsynchronisation;
281 bool dataLengthIndicator;
284 ////////////////////////////////////////////////////////////////////////////////
285 // static members (Frame::Header)
286 ////////////////////////////////////////////////////////////////////////////////
288 TagLib::uint Frame::Header::size()
293 TagLib::uint Frame::Header::size(uint version)
307 ////////////////////////////////////////////////////////////////////////////////
308 // public members (Frame::Header)
309 ////////////////////////////////////////////////////////////////////////////////
311 Frame::Header::Header(const ByteVector &data, bool synchSafeInts)
313 d = new HeaderPrivate;
314 setData(data, synchSafeInts);
317 Frame::Header::Header(const ByteVector &data, uint version)
319 d = new HeaderPrivate;
320 setData(data, version);
323 Frame::Header::~Header()
328 void Frame::Header::setData(const ByteVector &data, bool synchSafeInts)
330 setData(data, uint(synchSafeInts ? 4 : 3));
333 void Frame::Header::setData(const ByteVector &data, uint version)
335 d->version = version;
344 if(data.size() < 3) {
345 debug("You must at least specify a frame ID.");
349 // Set the frame ID -- the first three bytes
351 d->frameID = data.mid(0, 3);
353 // If the full header information was not passed in, do not continue to the
354 // steps to parse the frame size and flags.
356 if(data.size() < 6) {
361 d->frameSize = data.mid(3, 3).toUInt();
369 if(data.size() < 4) {
370 debug("You must at least specify a frame ID.");
374 // Set the frame ID -- the first four bytes
376 d->frameID = data.mid(0, 4);
378 // If the full header information was not passed in, do not continue to the
379 // steps to parse the frame size and flags.
381 if(data.size() < 10) {
386 // Set the size -- the frame size is the four bytes starting at byte four in
387 // the frame header (structure 4)
389 d->frameSize = data.mid(4, 4).toUInt();
391 { // read the first byte of flags
392 std::bitset<8> flags(data[8]);
393 d->tagAlterPreservation = flags[7]; // (structure 3.3.1.a)
394 d->fileAlterPreservation = flags[6]; // (structure 3.3.1.b)
395 d->readOnly = flags[5]; // (structure 3.3.1.c)
398 { // read the second byte of flags
399 std::bitset<8> flags(data[9]);
400 d->compression = flags[7]; // (structure 3.3.1.i)
401 d->encryption = flags[6]; // (structure 3.3.1.j)
402 d->groupingIdentity = flags[5]; // (structure 3.3.1.k)
411 if(data.size() < 4) {
412 debug("You must at least specify a frame ID.");
416 // Set the frame ID -- the first four bytes
418 d->frameID = data.mid(0, 4);
420 // If the full header information was not passed in, do not continue to the
421 // steps to parse the frame size and flags.
423 if(data.size() < 10) {
428 // Set the size -- the frame size is the four bytes starting at byte four in
429 // the frame header (structure 4)
431 d->frameSize = SynchData::toUInt(data.mid(4, 4));
432 #ifndef NO_ITUNES_HACKS
433 // iTunes writes v2.4 tags with v2.3-like frame sizes
434 if(d->frameSize > 127) {
435 if(!isValidFrameID(data.mid(d->frameSize + 10, 4))) {
436 unsigned int uintSize = data.mid(4, 4).toUInt();
437 if(isValidFrameID(data.mid(uintSize + 10, 4))) {
438 d->frameSize = uintSize;
444 { // read the first byte of flags
445 std::bitset<8> flags(data[8]);
446 d->tagAlterPreservation = flags[6]; // (structure 4.1.1.a)
447 d->fileAlterPreservation = flags[5]; // (structure 4.1.1.b)
448 d->readOnly = flags[4]; // (structure 4.1.1.c)
451 { // read the second byte of flags
452 std::bitset<8> flags(data[9]);
453 d->groupingIdentity = flags[6]; // (structure 4.1.2.h)
454 d->compression = flags[3]; // (structure 4.1.2.k)
455 d->encryption = flags[2]; // (structure 4.1.2.m)
456 d->unsynchronisation = flags[1]; // (structure 4.1.2.n)
457 d->dataLengthIndicator = flags[0]; // (structure 4.1.2.p)
464 ByteVector Frame::Header::frameID() const
469 void Frame::Header::setFrameID(const ByteVector &id)
471 d->frameID = id.mid(0, 4);
474 TagLib::uint Frame::Header::frameSize() const
479 void Frame::Header::setFrameSize(uint size)
484 TagLib::uint Frame::Header::version() const
489 bool Frame::Header::tagAlterPreservation() const
491 return d->tagAlterPreservation;
494 void Frame::Header::setTagAlterPreservation(bool preserve)
496 d->tagAlterPreservation = preserve;
499 bool Frame::Header::fileAlterPreservation() const
501 return d->fileAlterPreservation;
504 bool Frame::Header::readOnly() const
509 bool Frame::Header::groupingIdentity() const
511 return d->groupingIdentity;
514 bool Frame::Header::compression() const
516 return d->compression;
519 bool Frame::Header::encryption() const
521 return d->encryption;
524 bool Frame::Header::unsycronisation() const
526 return unsynchronisation();
529 bool Frame::Header::unsynchronisation() const
531 return d->unsynchronisation;
534 bool Frame::Header::dataLengthIndicator() const
536 return d->dataLengthIndicator;
539 ByteVector Frame::Header::render() const
541 ByteVector flags(2, char(0)); // just blank for the moment
543 ByteVector v = d->frameID + SynchData::fromUInt(d->frameSize) + flags;
548 bool Frame::Header::frameAlterPreservation() const
550 return fileAlterPreservation();