Methods setElementID of ChapterFrame and TableOfContentsFrame classes now automatically terminates new element ID with null.
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)
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;
}
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;
}
~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()
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);
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
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)
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<TableOfContentsFrame *>(*it);
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<TableOfContentsFrame *>(*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;
}
* 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.
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);
* \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);
#include "frames/popularimeterframe.h"
#include "frames/privateframe.h"
#include "frames/ownershipframe.h"
+#include "frames/chapterframe.h"
+#include "frames/tableofcontentsframe.h"
using namespace TagLib;
using namespace ID3v2;
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);
}