Support non-UTF8 free-form atoms

This commit is contained in:
Lukáš Lalinský 2012-01-08 13:17:42 +01:00
parent 5ff810e98d
commit c237998983
6 changed files with 143 additions and 29 deletions

View File

@ -1,5 +1,5 @@
/**************************************************************************
copyright : (C) 2007 by Lukáš Lalinský
copyright : (C) 2007,2011 by Lukáš Lalinský
email : lalinsky@gmail.com
**************************************************************************/
@ -40,6 +40,40 @@ namespace TagLib {
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:

View File

@ -29,6 +29,7 @@
#include "tlist.h"
#include "tbytevector.h"
#include "taglib_export.h"
#include "mp4atom.h"
namespace TagLib {
@ -41,8 +42,9 @@ namespace TagLib {
* This describes the image type.
*/
enum Format {
JPEG = 0x0D,
PNG = 0x0E
JPEG = TypeJPEG,
PNG = TypePNG,
BMP = TypeBMP
};
CoverArt(Format format, const ByteVector &data);

View File

@ -36,9 +36,10 @@ using namespace TagLib;
class MP4::Item::ItemPrivate : public RefCounter
{
public:
ItemPrivate() : RefCounter(), valid(true) {}
ItemPrivate() : RefCounter(), valid(true), atomDataType(TypeUndefined) {}
bool valid;
AtomDataType atomDataType;
union {
bool m_bool;
int m_int;
@ -48,6 +49,7 @@ public:
long long m_longlong;
};
StringList m_stringList;
ByteVectorList m_byteVectorList;
MP4::CoverArtList m_coverArtList;
};
@ -117,6 +119,12 @@ MP4::Item::Item(int value1, int value2)
d->m_intPair.second = value2;
}
MP4::Item::Item(const ByteVectorList &value)
{
d = new ItemPrivate;
d->m_byteVectorList = value;
}
MP4::Item::Item(const StringList &value)
{
d = new ItemPrivate;
@ -129,6 +137,16 @@ MP4::Item::Item(const MP4::CoverArtList &value)
d->m_coverArtList = value;
}
void MP4::Item::setAtomDataType(MP4::AtomDataType type)
{
d->atomDataType = type;
}
MP4::AtomDataType MP4::Item::atomDataType() const
{
return d->atomDataType;
}
bool
MP4::Item::toBool() const
{
@ -171,6 +189,12 @@ MP4::Item::toStringList() const
return d->m_stringList;
}
ByteVectorList
MP4::Item::toByteVectorList() const
{
return d->m_byteVectorList;
}
MP4::CoverArtList
MP4::Item::toCoverArtList() const
{

View File

@ -53,8 +53,12 @@ namespace TagLib {
Item(bool value);
Item(int first, int second);
Item(const StringList &value);
Item(const ByteVectorList &value);
Item(const CoverArtList &value);
void setAtomDataType(AtomDataType type);
AtomDataType atomDataType() const;
int toInt() const;
uchar toByte() const;
uint toUInt() const;
@ -62,6 +66,7 @@ namespace TagLib {
bool toBool() const;
IntPair toIntPair() const;
StringList toStringList() const;
ByteVectorList toByteVectorList() const;
CoverArtList toCoverArtList() const;
bool isValid() const;

View File

@ -1,5 +1,5 @@
/**************************************************************************
copyright : (C) 2007 by Lukáš Lalinský
copyright : (C) 2007,2011 by Lukáš Lalinský
email : lalinsky@gmail.com
**************************************************************************/
@ -105,10 +105,10 @@ MP4::Tag::~Tag()
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;
@ -125,7 +125,7 @@ MP4::Tag::parseData(MP4::Atom *atom, TagLib::File *file, int expectedFlags, bool
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") {
@ -133,7 +133,7 @@ MP4::Tag::parseData(MP4::Atom *atom, TagLib::File *file, int expectedFlags, bool
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;
@ -142,6 +142,17 @@ MP4::Tag::parseData(MP4::Atom *atom, TagLib::File *file, int expectedFlags, bool
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)
{
@ -227,14 +238,34 @@ MP4::Tag::parseText(MP4::Atom *atom, TagLib::File *file, int expectedFlags)
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);
}
}
@ -252,7 +283,7 @@ MP4::Tag::parseCovr(MP4::Atom *atom, TagLib::File *file)
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)));
}
@ -292,7 +323,7 @@ MP4::Tag::renderBool(const ByteVector &name, MP4::Item &item)
{
ByteVectorList data;
data.append(ByteVector(1, item.toBool() ? '\1' : '\0'));
return renderData(name, 0x15, data);
return renderData(name, TypeInteger, data);
}
ByteVector
@ -300,7 +331,7 @@ MP4::Tag::renderInt(const ByteVector &name, MP4::Item &item)
{
ByteVectorList data;
data.append(ByteVector::fromShort(item.toInt()));
return renderData(name, 0x15, data);
return renderData(name, TypeInteger, data);
}
ByteVector
@ -308,7 +339,7 @@ MP4::Tag::renderUInt(const ByteVector &name, MP4::Item &item)
{
ByteVectorList data;
data.append(ByteVector::fromUInt(item.toUInt()));
return renderData(name, 0x15, data);
return renderData(name, TypeInteger, data);
}
ByteVector
@ -316,7 +347,7 @@ MP4::Tag::renderLongLong(const ByteVector &name, MP4::Item &item)
{
ByteVectorList data;
data.append(ByteVector::fromLongLong(item.toLongLong()));
return renderData(name, 0x15, data);
return renderData(name, TypeInteger, data);
}
ByteVector
@ -324,7 +355,7 @@ MP4::Tag::renderByte(const ByteVector &name, MP4::Item &item)
{
ByteVectorList data;
data.append(ByteVector(1, item.toByte()));
return renderData(name, 0x15, data);
return renderData(name, TypeInteger, data);
}
ByteVector
@ -335,7 +366,7 @@ MP4::Tag::renderIntPair(const ByteVector &name, MP4::Item &item)
ByteVector::fromShort(item.toIntPair().first) +
ByteVector::fromShort(item.toIntPair().second) +
ByteVector(2, '\0'));
return renderData(name, 0x00, data);
return renderData(name, TypeImplicit, data);
}
ByteVector
@ -345,7 +376,7 @@ MP4::Tag::renderIntPairNoTrailing(const ByteVector &name, MP4::Item &item)
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
@ -382,9 +413,26 @@ MP4::Tag::renderFreeForm(const String &name, MP4::Item &item)
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);
}

View File

@ -1,5 +1,5 @@
/**************************************************************************
copyright : (C) 2007 by Lukáš Lalinský
copyright : (C) 2007,2011 by Lukáš Lalinský
email : lalinsky@gmail.com
**************************************************************************/
@ -68,6 +68,7 @@ namespace TagLib {
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);
@ -83,7 +84,7 @@ namespace TagLib {
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);