]> granicus.if.org Git - taglib/commitdiff
Support non-UTF8 free-form atoms
authorLukáš Lalinský <lalinsky@gmail.com>
Sun, 8 Jan 2012 12:17:42 +0000 (13:17 +0100)
committerLukáš Lalinský <lalinsky@gmail.com>
Sun, 8 Jan 2012 12:17:42 +0000 (13:17 +0100)
taglib/mp4/mp4atom.h
taglib/mp4/mp4coverart.h
taglib/mp4/mp4item.cpp
taglib/mp4/mp4item.h
taglib/mp4/mp4tag.cpp
taglib/mp4/mp4tag.h

index 2c0de3d2b2d8aef78a8ab6c1397fd4c2eace3300..ea5091a8b43d57f4c56d72373369136bbb0083cd 100644 (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:
index 26c4f9d9e4a845a6cc49f2faaafbc7ca5c235c5e..6ba06e7bfb49b44fd9a62067ca3990fece10bafe 100644 (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);
index 876fac4df0afb086071537b97a7742c8337c95b9..af2cc65c80de9f06e3da1fcf27b50679c2004ba3 100644 (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
 {
index 243a09989426ccde8177274eb0043da261aefd8b..be7aa1a17fc11760f424d8f2fc8bdf9922b367d4 100644 (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;
index 8d2283ab6f4f452c3be819c06d7920e633270b6e..0b427bd3c2f6b84d1b1fa3c36e5992068d94e184 100644 (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);
 }
index 433edf8a11c579dfcaeb7f413de3ef7ee2bbc862..b5ea6ebbc5ef9e7f824fd2fea8474ca6745ce08e 100644 (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);