Finished parseFields, renderFields and asProperty methods of ChapterFrame and TableOfContentsFrame classes.

Methods setElementID of ChapterFrame and TableOfContentsFrame classes now automatically terminates new element ID with null.
This commit is contained in:
Lukáš Krejčí 2013-04-21 16:16:57 +02:00 committed by Lukas Krejci
parent 3a1040d55b
commit c5f9258462
5 changed files with 104 additions and 46 deletions

View File

@ -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;
}

View File

@ -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);

View File

@ -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<TableOfContentsFrame *>(*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<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;
}

View File

@ -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);

View File

@ -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);
}