diff --git a/taglib/mpeg/id3v2/frames/chapterframe.cpp b/taglib/mpeg/id3v2/frames/chapterframe.cpp index 1474db1a..633a6d8b 100644 --- a/taglib/mpeg/id3v2/frames/chapterframe.cpp +++ b/taglib/mpeg/id3v2/frames/chapterframe.cpp @@ -98,6 +98,8 @@ uint ChapterFrame::endOffset() const void ChapterFrame::setElementID(const ByteVector &eID) { d->elementID = eID; + if(eID.at(eID.size() - 1) != char(0)) + d->elementID.append(char(0)); } void ChapterFrame::setStartTime(const uint &sT) @@ -120,21 +122,17 @@ void ChapterFrame::setEndOffset(const uint &eO) d->endOffset = eO; } -String UniqueFileIdentifierFrame::toString() const +String ChapterFrame::toString() const { return String::null; } -PropertyMap ChapterFrame::asProperties() const +PropertyMap UniqueFileIdentifierFrame::asProperties() const { - //DODELAT PropertyMap map; - if(d->owner == "http://musicbrainz.org") { - map.insert("MUSICBRAINZ_TRACKID", String(d->identifier)); - } - else { - map.unsupportedData().append(frameID() + String("/") + d->owner); - } + + map.unsupportedData().append(frameID() + String("/") + d->elementID); + return map; } @@ -156,25 +154,32 @@ ChapterFrame *ChapterFrame::findByElementID(const Tag *tag, const ByteVector &eI void ChapterFrame::parseFields(const ByteVector &data) { - //DODELAT - if(data.size() < 1) { - debug("An UFID frame must contain at least 1 byte."); + if(data.size() < 18) { + debug("An CHAP frame must contain at least 18 bytes (1 byte element ID terminated by null and 4x4 bytes for start and end time and offset)."); return; } int pos = 0; - d->owner = readStringField(data, String::Latin1, &pos); - d->identifier = data.mid(pos); + d->elementID = readStringField(data, String::Latin1, &pos).data(String::Latin1); + d->elementID.append(char(0)); + d->startTime = data.mid(pos, 4).toUInt(true); + pos += 4; + d->endTime = data.mid(pos, 4).toUInt(true); + pos += 4; + d->startOffset = data.mid(pos, 4).toUInt(true); + pos += 4; + d->endOffset = data.mid(pos, 4).toUInt(true); } ByteVector ChapterFrame::renderFields() const { - //DODELAT ByteVector data; - data.append(d->owner.data(String::Latin1)); - data.append(char(0)); - data.append(d->identifier); + data.append(d->elementID); + data.append(ByteVector.fromUInt(d->startTime, true)); + data.append(ByteVector.fromUInt(d->endTime, true)); + data.append(ByteVector.fromUInt(d->startOffset, true)); + data.append(ByteVector.fromUInt(d->endOffset, true)); return data; } diff --git a/taglib/mpeg/id3v2/frames/chapterframe.h b/taglib/mpeg/id3v2/frames/chapterframe.h index ce9096cf..1ecbbbfd 100644 --- a/taglib/mpeg/id3v2/frames/chapterframe.h +++ b/taglib/mpeg/id3v2/frames/chapterframe.h @@ -62,7 +62,7 @@ namespace TagLib { ~ChapterFrame(); /*! - * Returns the elementID of the frame. Element ID + * Returns the element ID of the frame. Element ID * is a null terminated string, however it's not human-readable. * * \see setElementID() @@ -100,9 +100,9 @@ namespace TagLib { uint endOffset() const; /*! - * Sets the elementID of the frame to \a eID. + * Sets the element ID of the frame to \a eID. If \a eID isn't + * null terminated, a null char is appended automatically. * - * \warning Element ID must be null terminated. * \see elementID() */ void setElementID(const ByteVector &eID); diff --git a/taglib/mpeg/id3v2/frames/tableofcontentsframe.cpp b/taglib/mpeg/id3v2/frames/tableofcontentsframe.cpp index 1cb24b50..39df25d6 100644 --- a/taglib/mpeg/id3v2/frames/tableofcontentsframe.cpp +++ b/taglib/mpeg/id3v2/frames/tableofcontentsframe.cpp @@ -81,9 +81,9 @@ bool TableOfContentsFrame::isOrdered() const return d->isOrdered; } -unsigned char TableOfContentsFrame::entryCount() const +uint TableOfContentsFrame::entryCount() const { - return (unsigned char)(d->childElements.size()); + return d->childElements.size(); } ByteVectorList TableOfContentsFrame::childElements const @@ -94,6 +94,8 @@ ByteVectorList TableOfContentsFrame::childElements const void TableOfContentsFrame::setElementID(const ByteVector &eID) { d->elementID = eID; + if(eID.at(eID.size() - 1) != char(0)) + d->elementID.append(char(0)); } void TableOfContentsFrame::setIsTopLevel(const bool &t) @@ -118,23 +120,19 @@ String TableOfContentsFrame::toString() const PropertyMap TableOfContentsFrame::asProperties() const { - //DODELAT PropertyMap map; - if(d->owner == "http://musicbrainz.org") { - map.insert("MUSICBRAINZ_TRACKID", String(d->identifier)); - } - else { - map.unsupportedData().append(frameID() + String("/") + d->owner); - } + + map.unsupportedData().append(frameID() + String("/") + d->elementID); + return map; } TableOfContentsFrame *TableOfContentsFrame::findByElementID(const ID3v2::Tag *tag, const ByteVector &eID) // static { - ID3v2::FrameList comments = tag->frameList("CTOC"); + ID3v2::FrameList tablesOfContents = tag->frameList("CTOC"); - for(ID3v2::FrameList::ConstIterator it = comments.begin(); - it != comments.end(); + for(ID3v2::FrameList::ConstIterator it = tablesOfContents.begin(); + it != tablesOfContents.end(); ++it) { TableOfContentsFrame *frame = dynamic_cast(*it); @@ -145,28 +143,63 @@ TableOfContentsFrame *TableOfContentsFrame::findByElementID(const ID3v2::Tag *ta return 0; } +TableOfContentsFrame *TableOfContentsFrame::findTopLevel(const Tag *tag) // static +{ + ID3v2::FrameList tablesOfContents = tag->frameList("CTOC"); + + for(ID3v2::FrameList::ConstIterator it = tablesOfContents.begin(); + it != tablesOfContents.end(); + ++it) + { + TableOfContentsFrame *frame = dynamic_cast(*it); + if(frame && frame->isTopLevel() == true) + return frame; + } + + return 0; +} + void TableOfContentsFrame::parseFields(const ByteVector &data) { - //DODELAT - if(data.size() < 1) { - debug("An UFID frame must contain at least 1 byte."); + if(data.size() < 6) { + debug("An CTOC frame must contain at least 6 bytes (1 byte element ID terminated by null, 1 byte flags, 1 byte entry count and 1 byte child element ID terminated by null."); return; } int pos = 0; - d->owner = readStringField(data, String::Latin1, &pos); - d->identifier = data.mid(pos); + d->elementID = readStringField(data, String::Latin1, &pos).data(String::Latin1); + d->elementID.append(char(0)); + d->isTopLevel = (data.at(pos++) & 2) > 0; + d->isOrdered = (data.at(pos++) & 1) > 0; + uint entryCount = data.at(pos++); + for(int i = 0; i < entryCount; i++) + { + ByteVector childElementID = readStringField(data, String::Latin1, &pos).data(String::Latin1); + childElementID.append(char(0)); + d->childElements.append(childElementID); + } } ByteVector TableOfContentsFrame::renderFields() const { - //DODELAT ByteVector data; - data.append(d->owner.data(String::Latin1)); + data.append(d->elementID); data.append(char(0)); - data.append(d->identifier); - + char flags = 0; + if(d->isTopLevel) + flags += 2; + if(d->isOrdered) + flags += 1; + data.append(flags); + data.append((char)(entryCount())); + ConstIterator it = d->childElements.begin(); + while(it != d->childElements.end()) { + data.append(*it); + data.append(char(0)); + it++; + } + return data; } diff --git a/taglib/mpeg/id3v2/frames/tableofcontentsframe.h b/taglib/mpeg/id3v2/frames/tableofcontentsframe.h index 0d189dfc..63ab96ee 100644 --- a/taglib/mpeg/id3v2/frames/tableofcontentsframe.h +++ b/taglib/mpeg/id3v2/frames/tableofcontentsframe.h @@ -88,10 +88,9 @@ namespace TagLib { * Returns count of child elements of the frame. It allways * corresponds to size of child elements list. * - * \note Return type should be uint8_t, not unsigned char. * \see childElements() */ - unsigned char entryCount() const; + uint entryCount() const; /*! * Returns list of child elements of the frame. @@ -101,9 +100,9 @@ namespace TagLib { ByteVectorList childElements() const; /*! - * Sets the elementID of the frame to \a eID. + * Sets the elementID of the frame to \a eID. If \a eID isn't + * null terminated, a null char is appended automatically. * - * \warning Element ID must be null terminated. * \see elementID() */ void setElementID(const ByteVector &eID); @@ -143,6 +142,15 @@ namespace TagLib { * \see elementID() */ static TableOfContentsFrame *findByElementID(const Tag *tag, const ByteVector &eID); + + /*! + * CTOC frames each contain a flag that indicates, if CTOC frame is top-level (there isn't + * any frame, which contains this frame in its child elements list). Only a single frame + * within tag can be top-level. This searches for a top-level CTOC frame. + * + * \see isTopLevel() + */ + static TableOfContentsFrame *findTopLevel(const Tag *tag); protected: virtual void parseFields(const ByteVector &data); diff --git a/taglib/mpeg/id3v2/id3v2framefactory.cpp b/taglib/mpeg/id3v2/id3v2framefactory.cpp index b2c32ce4..9055be56 100644 --- a/taglib/mpeg/id3v2/id3v2framefactory.cpp +++ b/taglib/mpeg/id3v2/id3v2framefactory.cpp @@ -47,6 +47,8 @@ #include "frames/ownershipframe.h" #include "frames/synchronizedlyricsframe.h" #include "frames/eventtimingcodesframe.h" +#include "frames/chapterframe.h" +#include "frames/tableofcontentsframe.h" using namespace TagLib; using namespace ID3v2; @@ -274,6 +276,16 @@ Frame *FrameFactory::createFrame(const ByteVector &origData, Header *tagHeader) d->setTextEncoding(f); return f; } + + // Chapter (ID3v2 chapters 1.0) + + if(frameID == "CHAP") + return new ChapterFrame(data, header); + + // Table of contents (ID3v2 chapters 1.0) + + if(frameID == "CTOC") + return new TableOfContentsFrame(data, header); return new UnknownFrame(data, header); }