diff --git a/taglib/mpeg/id3v2/id3v2framefactory.cpp b/taglib/mpeg/id3v2/id3v2framefactory.cpp index 482c918f..02b6cfaf 100644 --- a/taglib/mpeg/id3v2/id3v2framefactory.cpp +++ b/taglib/mpeg/id3v2/id3v2framefactory.cpp @@ -28,6 +28,7 @@ #include #include "id3v2framefactory.h" +#include "id3v2synchdata.h" #include "frames/attachedpictureframe.h" #include "frames/commentsframe.h" @@ -71,6 +72,15 @@ Frame *FrameFactory::createFrame(const ByteVector &data, bool synchSafeInts) con Frame *FrameFactory::createFrame(const ByteVector &data, uint version) const { + Header tagHeader; + tagHeader.setMajorVersion(version); + return createFrame(data, &tagHeader); +} + +Frame *FrameFactory::createFrame(const ByteVector &origData, Header *tagHeader) const +{ + ByteVector data = origData; + uint version = tagHeader->majorVersion(); Frame::Header *header = new Frame::Header(data, version); ByteVector frameID = header->frameID(); @@ -78,7 +88,7 @@ Frame *FrameFactory::createFrame(const ByteVector &data, uint version) const // characters. Also make sure that there is data in the frame. if(!frameID.size() == (version < 3 ? 3 : 4) || - header->frameSize() <= 0 || + header->frameSize() <= (header->dataLengthIndicator() ? 4 : 0) || header->frameSize() > data.size()) { delete header; @@ -92,6 +102,14 @@ Frame *FrameFactory::createFrame(const ByteVector &data, uint version) const } } + if(version > 3 && (tagHeader->unsynchronisation() || header->unsynchronisation())) { + // Data lengths are not part of the encoded data, but since they are synch-safe + // integers they will be never actually encoded. + ByteVector frameData = data.mid(Frame::Header::size(version), header->frameSize()); + SynchData::decode(frameData); + data = data.mid(0, Frame::Header::size(version)) + frameData; + } + // TagLib doesn't mess with encrypted frames, so just treat them // as unknown frames. diff --git a/taglib/mpeg/id3v2/id3v2framefactory.h b/taglib/mpeg/id3v2/id3v2framefactory.h index 7e51ed7a..1e2c9fa3 100644 --- a/taglib/mpeg/id3v2/id3v2framefactory.h +++ b/taglib/mpeg/id3v2/id3v2framefactory.h @@ -29,6 +29,7 @@ #include "taglib_export.h" #include "tbytevector.h" #include "id3v2frame.h" +#include "id3v2header.h" namespace TagLib { @@ -64,8 +65,8 @@ namespace TagLib { * false if we are parsing an old tag (v2.3 or older) that does not support * synchsafe ints. * - * \deprecated Please use the method below that accepts an ID3 version - * number in new code. + * \deprecated Please use the method below that accepts a ID3v2::Header + * instance in new code. */ Frame *createFrame(const ByteVector &data, bool synchSafeInts) const; @@ -73,9 +74,18 @@ namespace TagLib { * Create a frame based on \a data. \a version should indicate the ID3v2 * version of the tag. As ID3v2.4 is the most current version of the * standard 4 is the default. + * + * \deprecated Please use the method below that accepts a ID3v2::Header + * instance in new code. + */ + Frame *createFrame(const ByteVector &data, uint version = 4) const; + + /*! + * Create a frame based on \a data. \a tagHeader should be a valid + * ID3v2::Header instance. */ // BIC: make virtual - Frame *createFrame(const ByteVector &data, uint version = 4) const; + Frame *createFrame(const ByteVector &data, Header *tagHeader) const; /*! * Returns the default text encoding for text frames. If setTextEncoding() diff --git a/taglib/mpeg/id3v2/id3v2header.cpp b/taglib/mpeg/id3v2/id3v2header.cpp index 57600fde..238c9ae2 100644 --- a/taglib/mpeg/id3v2/id3v2header.cpp +++ b/taglib/mpeg/id3v2/id3v2header.cpp @@ -101,6 +101,11 @@ TagLib::uint Header::majorVersion() const return d->majorVersion; } +void Header::setMajorVersion(TagLib::uint version) +{ + d->majorVersion = version; +} + TagLib::uint Header::revisionNumber() const { return d->revisionNumber; diff --git a/taglib/mpeg/id3v2/id3v2header.h b/taglib/mpeg/id3v2/id3v2header.h index d2d7e6c7..7ab546cd 100644 --- a/taglib/mpeg/id3v2/id3v2header.h +++ b/taglib/mpeg/id3v2/id3v2header.h @@ -69,6 +69,13 @@ namespace TagLib { */ uint majorVersion() const; + /*! + * Set the the major version number to \a version. (Note: This is + * the 4, not the 2 in ID3v2.4.0. The 2 is implied.) + * \see majorVersion() + */ + void setMajorVersion(uint version); + /*! * Returns the revision number. (Note: This is the 0, not the 4 in * ID3v2.4.0. The 2 is implied.) diff --git a/taglib/mpeg/id3v2/id3v2synchdata.cpp b/taglib/mpeg/id3v2/id3v2synchdata.cpp index 47b03383..b174e633 100644 --- a/taglib/mpeg/id3v2/id3v2synchdata.cpp +++ b/taglib/mpeg/id3v2/id3v2synchdata.cpp @@ -50,3 +50,23 @@ ByteVector SynchData::fromUInt(uint value) return v; } + +void SynchData::decode(ByteVector &data) +{ + char *n = data.data(); + const char *o = data.data(); + const char *end = o + data.size(); + + if(data.size() <= 0) + return; + + while(o < end - 1) { + *n++ = *o; + if(o[0] == '\xFF' && o[1] == '\x00') + o++; + o++; + } + *n++ = *o; + + data.resize(n - data.data()); +} diff --git a/taglib/mpeg/id3v2/id3v2synchdata.h b/taglib/mpeg/id3v2/id3v2synchdata.h index 218e057e..ad49b099 100644 --- a/taglib/mpeg/id3v2/id3v2synchdata.h +++ b/taglib/mpeg/id3v2/id3v2synchdata.h @@ -57,6 +57,11 @@ namespace TagLib { * Returns a 4 byte (32 bit) synchsafe integer based on \a value. */ ByteVector fromUInt(uint value); + + /*! + * Deunsynchronize the data (in-place). + */ + void decode(ByteVector &data); } } diff --git a/taglib/mpeg/id3v2/id3v2tag.cpp b/taglib/mpeg/id3v2/id3v2tag.cpp index cca8d94b..0ee64ab6 100644 --- a/taglib/mpeg/id3v2/id3v2tag.cpp +++ b/taglib/mpeg/id3v2/id3v2tag.cpp @@ -30,6 +30,7 @@ #include "id3v2header.h" #include "id3v2extendedheader.h" #include "id3v2footer.h" +#include "id3v2synchdata.h" #include "id3v1genres.h" @@ -381,8 +382,14 @@ void ID3v2::Tag::read() } } -void ID3v2::Tag::parse(const ByteVector &data) +void ID3v2::Tag::parse(const ByteVector &origData) { + ByteVector data = origData; + + if(d->header.unsynchronisation() && d->header.majorVersion() <= 3) { + SynchData::decode(data); + } + uint frameDataPosition = 0; uint frameDataLength = data.size(); @@ -424,7 +431,7 @@ void ID3v2::Tag::parse(const ByteVector &data) } Frame *frame = d->factory->createFrame(data.mid(frameDataPosition), - d->header.majorVersion()); + &d->header); if(!frame) return;