/**************************************************************************
- copyright : (C) 2007 by Lukáš Lalinský
+ copyright : (C) 2007,2011 by Lukáš Lalinský
email : lalinsky@gmail.com
**************************************************************************/
class Atom;
typedef TagLib::List<Atom *> AtomList;
+ enum AtomDataType
+ {
+ TypeImplicit = 0, // for use with tags for which no type needs to be indicated because only one type is allowed
+ TypeUTF8 = 1, // without any count or null terminator
+ TypeUTF16 = 2, // also known as UTF-16BE
+ TypeSJIS = 3, // deprecated unless it is needed for special Japanese characters
+ TypeHTML = 6, // the HTML file header specifies which HTML version
+ TypeXML = 7, // the XML header must identify the DTD or schemas
+ TypeUUID = 8, // also known as GUID; stored as 16 bytes in binary (valid as an ID)
+ TypeISRC = 9, // stored as UTF-8 text (valid as an ID)
+ TypeMI3P = 10, // stored as UTF-8 text (valid as an ID)
+ TypeGIF = 12, // (deprecated) a GIF image
+ TypeJPEG = 13, // a JPEG image
+ TypePNG = 14, // a PNG image
+ TypeURL = 15, // absolute, in UTF-8 characters
+ TypeDuration = 16, // in milliseconds, 32-bit integer
+ TypeDateTime = 17, // in UTC, counting seconds since midnight, January 1, 1904; 32 or 64-bits
+ TypeGenred = 18, // a list of enumerated values
+ TypeInteger = 21, // a signed big-endian integer with length one of { 1,2,3,4,8 } bytes
+ TypeRIAAPA = 24, // RIAA parental advisory; { -1=no, 1=yes, 0=unspecified }, 8-bit ingteger
+ TypeUPC = 25, // Universal Product Code, in text UTF-8 format (valid as an ID)
+ TypeBMP = 27, // Windows bitmap image
+ TypeUndefined = 255 // undefined
+ };
+
+ struct AtomData {
+ AtomData(AtomDataType type, ByteVector data) : type(type), locale(0), data(data) {}
+ AtomDataType type;
+ int locale;
+ ByteVector data;
+ };
+
+ typedef TagLib::List<AtomData> AtomDataList;
+
class Atom
{
public:
/**************************************************************************
- copyright : (C) 2007 by Lukáš Lalinský
+ copyright : (C) 2007,2011 by Lukáš Lalinský
email : lalinsky@gmail.com
**************************************************************************/
delete d;
}
-ByteVectorList
-MP4::Tag::parseData(MP4::Atom *atom, TagLib::File *file, int expectedFlags, bool freeForm)
+MP4::AtomDataList
+MP4::Tag::parseData2(MP4::Atom *atom, TagLib::File *file, int expectedFlags, bool freeForm)
{
- ByteVectorList result;
+ AtomDataList result;
ByteVector data = file->readBlock(atom->length - 8);
int i = 0;
unsigned int pos = 0;
debug("MP4: Unexpected atom \"" + name + "\", expecting \"name\"");
return result;
}
- result.append(data.mid(pos + 12, length - 12));
+ result.append(AtomData(AtomDataType(flags), data.mid(pos + 12, length - 12)));
}
else {
if(name != "data") {
return result;
}
if(expectedFlags == -1 || flags == expectedFlags) {
- result.append(data.mid(pos + 16, length - 16));
+ result.append(AtomData(AtomDataType(flags), data.mid(pos + 16, length - 16)));
}
}
pos += length;
return result;
}
+ByteVectorList
+MP4::Tag::parseData(MP4::Atom *atom, TagLib::File *file, int expectedFlags, bool freeForm)
+{
+ AtomDataList data = parseData2(atom, file, expectedFlags, freeForm);
+ ByteVectorList result;
+ for(uint i = 0; i < data.size(); i++) {
+ result.append(data[i].data);
+ }
+ return result;
+}
+
void
MP4::Tag::parseInt(MP4::Atom *atom, TagLib::File *file)
{
void
MP4::Tag::parseFreeForm(MP4::Atom *atom, TagLib::File *file)
{
- ByteVectorList data = parseData(atom, file, 1, true);
+ AtomDataList data = parseData2(atom, file, -1, true);
if(data.size() > 2) {
- StringList value;
- for(unsigned int i = 2; i < data.size(); i++) {
- value.append(String(data[i], String::UTF8));
+ String name = "----:" + String(data[0].data, String::UTF8) + ':' + String(data[1].data, String::UTF8);
+ AtomDataType type = data[2].type;
+ for(uint i = 2; i < data.size(); i++) {
+ if(data[i].type != type) {
+ debug("MP4: We currently don't support values with multiple types");
+ break;
+ }
+ }
+ if(type == TypeUTF8) {
+ StringList value;
+ for(uint i = 2; i < data.size(); i++) {
+ value.append(String(data[i].data, String::UTF8));
+ }
+ Item item(value);
+ item.setAtomDataType(type);
+ d->items.insert(name, item);
+ }
+ else {
+ ByteVectorList value;
+ for(uint i = 2; i < data.size(); i++) {
+ value.append(data[i].data);
+ }
+ Item item(value);
+ item.setAtomDataType(type);
+ d->items.insert(name, item);
}
- String name = "----:" + data[0] + ':' + data[1];
- d->items.insert(name, value);
}
}
debug("MP4: Unexpected atom \"" + name + "\", expecting \"data\"");
break;
}
- if(flags == MP4::CoverArt::PNG || flags == MP4::CoverArt::JPEG) {
+ if(flags == TypeJPEG || flags == TypePNG || flags == TypeBMP) {
value.append(MP4::CoverArt(MP4::CoverArt::Format(flags),
data.mid(pos + 16, length - 16)));
}
{
ByteVectorList data;
data.append(ByteVector(1, item.toBool() ? '\1' : '\0'));
- return renderData(name, 0x15, data);
+ return renderData(name, TypeInteger, data);
}
ByteVector
{
ByteVectorList data;
data.append(ByteVector::fromShort(item.toInt()));
- return renderData(name, 0x15, data);
+ return renderData(name, TypeInteger, data);
}
ByteVector
{
ByteVectorList data;
data.append(ByteVector::fromUInt(item.toUInt()));
- return renderData(name, 0x15, data);
+ return renderData(name, TypeInteger, data);
}
ByteVector
{
ByteVectorList data;
data.append(ByteVector::fromLongLong(item.toLongLong()));
- return renderData(name, 0x15, data);
+ return renderData(name, TypeInteger, data);
}
ByteVector
{
ByteVectorList data;
data.append(ByteVector(1, item.toByte()));
- return renderData(name, 0x15, data);
+ return renderData(name, TypeInteger, data);
}
ByteVector
ByteVector::fromShort(item.toIntPair().first) +
ByteVector::fromShort(item.toIntPair().second) +
ByteVector(2, '\0'));
- return renderData(name, 0x00, data);
+ return renderData(name, TypeImplicit, data);
}
ByteVector
data.append(ByteVector(2, '\0') +
ByteVector::fromShort(item.toIntPair().first) +
ByteVector::fromShort(item.toIntPair().second));
- return renderData(name, 0x00, data);
+ return renderData(name, TypeImplicit, data);
}
ByteVector
ByteVector data;
data.append(renderAtom("mean", ByteVector::fromUInt(0) + header[1].data(String::UTF8)));
data.append(renderAtom("name", ByteVector::fromUInt(0) + header[2].data(String::UTF8)));
- StringList value = item.toStringList();
- for(unsigned int i = 0; i < value.size(); i++) {
- data.append(renderAtom("data", ByteVector::fromUInt(1) + ByteVector(4, '\0') + value[i].data(String::UTF8)));
+ AtomDataType type = item.atomDataType();
+ if(type == TypeUndefined) {
+ if(!item.toStringList().isEmpty()) {
+ type = TypeUTF8;
+ }
+ else {
+ type = TypeImplicit;
+ }
+ }
+ if(type == TypeUTF8) {
+ StringList value = item.toStringList();
+ for(unsigned int i = 0; i < value.size(); i++) {
+ data.append(renderAtom("data", ByteVector::fromUInt(type) + ByteVector(4, '\0') + value[i].data(String::UTF8)));
+ }
+ }
+ else {
+ ByteVectorList value = item.toByteVectorList();
+ for(unsigned int i = 0; i < value.size(); i++) {
+ data.append(renderAtom("data", ByteVector::fromUInt(type) + ByteVector(4, '\0') + value[i]));
+ }
}
return renderAtom("----", data);
}
/**************************************************************************
- copyright : (C) 2007 by Lukáš Lalinský
+ copyright : (C) 2007,2011 by Lukáš Lalinský
email : lalinsky@gmail.com
**************************************************************************/
ItemListMap &itemListMap();
private:
+ AtomDataList parseData2(Atom *atom, TagLib::File *file, int expectedFlags = -1, bool freeForm = false);
TagLib::ByteVectorList parseData(Atom *atom, TagLib::File *file, int expectedFlags = -1, bool freeForm = false);
void parseText(Atom *atom, TagLib::File *file, int expectedFlags = 1);
void parseFreeForm(Atom *atom, TagLib::File *file);
TagLib::ByteVector padIlst(const ByteVector &data, int length = -1);
TagLib::ByteVector renderAtom(const ByteVector &name, const TagLib::ByteVector &data);
TagLib::ByteVector renderData(const ByteVector &name, int flags, const TagLib::ByteVectorList &data);
- TagLib::ByteVector renderText(const ByteVector &name, Item &item, int flags = 1);
+ TagLib::ByteVector renderText(const ByteVector &name, Item &item, int flags = TypeUTF8);
TagLib::ByteVector renderFreeForm(const String &name, Item &item);
TagLib::ByteVector renderBool(const ByteVector &name, Item &item);
TagLib::ByteVector renderInt(const ByteVector &name, Item &item);