Add M4A to files pattern
[someplayer] / src / taglib / asf / asffile.cpp
1 /**************************************************************************
2     copyright            : (C) 2005-2007 by Lukáš Lalinský
3     email                : lalinsky@gmail.com
4  **************************************************************************/
5
6 /***************************************************************************
7  *   This library is free software; you can redistribute it and/or modify  *
8  *   it under the terms of the GNU Lesser General Public License version   *
9  *   2.1 as published by the Free Software Foundation.                     *
10  *                                                                         *
11  *   This library is distributed in the hope that it will be useful, but   *
12  *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
14  *   Lesser General Public License for more details.                       *
15  *                                                                         *
16  *   You should have received a copy of the GNU Lesser General Public      *
17  *   License along with this library; if not, write to the Free Software   *
18  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
19  *   USA                                                                   *
20  *                                                                         *
21  *   Alternatively, this file is available under the Mozilla Public        *
22  *   License Version 1.1.  You may obtain a copy of the License at         *
23  *   http://www.mozilla.org/MPL/                                           *
24  ***************************************************************************/
25
26 #ifdef HAVE_CONFIG_H
27 #include <config.h>
28 #endif
29
30
31 #include <tdebug.h>
32 #include <tbytevectorlist.h>
33 #include <tstring.h>
34 #include "asffile.h"
35 #include "asftag.h"
36 #include "asfproperties.h"
37
38 using namespace TagLib;
39
40 class ASF::File::FilePrivate
41 {
42 public:
43   FilePrivate():
44     size(0),
45     tag(0),
46     properties(0),
47     contentDescriptionObject(0),
48     extendedContentDescriptionObject(0),
49     headerExtensionObject(0),
50     metadataObject(0),
51     metadataLibraryObject(0) {}
52   unsigned long long size;
53   ASF::Tag *tag;
54   ASF::Properties *properties;
55   List<ASF::File::BaseObject *> objects;
56   ASF::File::ContentDescriptionObject *contentDescriptionObject;
57   ASF::File::ExtendedContentDescriptionObject *extendedContentDescriptionObject;
58   ASF::File::HeaderExtensionObject *headerExtensionObject;
59   ASF::File::MetadataObject *metadataObject;
60   ASF::File::MetadataLibraryObject *metadataLibraryObject;
61 };
62
63 static ByteVector headerGuid("\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C", 16);
64 static ByteVector filePropertiesGuid("\xA1\xDC\xAB\x8C\x47\xA9\xCF\x11\x8E\xE4\x00\xC0\x0C\x20\x53\x65", 16);
65 static ByteVector streamPropertiesGuid("\x91\x07\xDC\xB7\xB7\xA9\xCF\x11\x8E\xE6\x00\xC0\x0C\x20\x53\x65", 16);
66 static ByteVector contentDescriptionGuid("\x33\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C", 16);
67 static ByteVector extendedContentDescriptionGuid("\x40\xA4\xD0\xD2\x07\xE3\xD2\x11\x97\xF0\x00\xA0\xC9\x5E\xA8\x50", 16);
68 static ByteVector headerExtensionGuid("\xb5\x03\xbf_.\xa9\xcf\x11\x8e\xe3\x00\xc0\x0c Se", 16);
69 static ByteVector metadataGuid("\xEA\xCB\xF8\xC5\xAF[wH\204g\xAA\214D\xFAL\xCA", 16);
70 static ByteVector metadataLibraryGuid("\224\034#D\230\224\321I\241A\x1d\x13NEpT", 16);
71
72 class ASF::File::BaseObject
73 {
74 public:
75   ByteVector data;
76   virtual ~BaseObject() {}
77   virtual ByteVector guid() = 0;
78   virtual void parse(ASF::File *file, unsigned int size);
79   virtual ByteVector render(ASF::File *file);
80 };
81
82 class ASF::File::UnknownObject : public ASF::File::BaseObject
83 {
84   ByteVector myGuid;
85 public:
86   UnknownObject(const ByteVector &guid);
87   ByteVector guid();
88 };
89
90 class ASF::File::FilePropertiesObject : public ASF::File::BaseObject
91 {
92 public:
93   ByteVector guid();
94   void parse(ASF::File *file, uint size);
95 };
96
97 class ASF::File::StreamPropertiesObject : public ASF::File::BaseObject
98 {
99 public:
100   ByteVector guid();
101   void parse(ASF::File *file, uint size);
102 };
103
104 class ASF::File::ContentDescriptionObject : public ASF::File::BaseObject
105 {
106 public:
107   ByteVector guid();
108   void parse(ASF::File *file, uint size);
109   ByteVector render(ASF::File *file);
110 };
111
112 class ASF::File::ExtendedContentDescriptionObject : public ASF::File::BaseObject
113 {
114 public:
115   ByteVectorList attributeData;
116   ByteVector guid();
117   void parse(ASF::File *file, uint size);
118   ByteVector render(ASF::File *file);
119 };
120
121 class ASF::File::MetadataObject : public ASF::File::BaseObject
122 {
123 public:
124   ByteVectorList attributeData;
125   ByteVector guid();
126   void parse(ASF::File *file, uint size);
127   ByteVector render(ASF::File *file);
128 };
129
130 class ASF::File::MetadataLibraryObject : public ASF::File::BaseObject
131 {
132 public:
133   ByteVectorList attributeData;
134   ByteVector guid();
135   void parse(ASF::File *file, uint size);
136   ByteVector render(ASF::File *file);
137 };
138
139 class ASF::File::HeaderExtensionObject : public ASF::File::BaseObject
140 {
141 public:
142   List<ASF::File::BaseObject *> objects;
143   ByteVector guid();
144   void parse(ASF::File *file, uint size);
145   ByteVector render(ASF::File *file);
146 };
147
148 void
149 ASF::File::BaseObject::parse(ASF::File *file, unsigned int size)
150 {
151   data = file->readBlock(size - 24);
152 }
153
154 ByteVector
155 ASF::File::BaseObject::render(ASF::File * /*file*/)
156 {
157   return guid() + ByteVector::fromLongLong(data.size() + 24, false) + data;
158 }
159
160 ASF::File::UnknownObject::UnknownObject(const ByteVector &guid) : myGuid(guid)
161 {
162 }
163
164 ByteVector
165 ASF::File::UnknownObject::guid()
166 {
167   return myGuid;
168 }
169
170 ByteVector
171 ASF::File::FilePropertiesObject::guid()
172 {
173   return filePropertiesGuid;
174 }
175
176 void
177 ASF::File::FilePropertiesObject::parse(ASF::File *file, uint size)
178 {
179   BaseObject::parse(file, size);
180   file->d->properties->setLength((int)(data.mid(40, 8).toLongLong(false) / 10000000L - data.mid(56, 8).toLongLong(false) / 1000L));
181 }
182
183 ByteVector
184 ASF::File::StreamPropertiesObject::guid()
185 {
186   return streamPropertiesGuid;
187 }
188
189 void
190 ASF::File::StreamPropertiesObject::parse(ASF::File *file, uint size)
191 {
192   BaseObject::parse(file, size);
193   file->d->properties->setChannels(data.mid(56, 2).toShort(false));
194   file->d->properties->setSampleRate(data.mid(58, 4).toUInt(false));
195   file->d->properties->setBitrate(data.mid(62, 4).toUInt(false) * 8 / 1000);
196 }
197
198 ByteVector
199 ASF::File::ContentDescriptionObject::guid()
200 {
201   return contentDescriptionGuid;
202 }
203
204 void
205 ASF::File::ContentDescriptionObject::parse(ASF::File *file, uint /*size*/)
206 {
207   file->d->contentDescriptionObject = this;
208   int titleLength = file->readWORD();
209   int artistLength = file->readWORD();
210   int copyrightLength = file->readWORD();
211   int commentLength = file->readWORD();
212   int ratingLength = file->readWORD();
213   file->d->tag->setTitle(file->readString(titleLength));
214   file->d->tag->setArtist(file->readString(artistLength));
215   file->d->tag->setCopyright(file->readString(copyrightLength));
216   file->d->tag->setComment(file->readString(commentLength));
217   file->d->tag->setRating(file->readString(ratingLength));
218 }
219
220 ByteVector
221 ASF::File::ContentDescriptionObject::render(ASF::File *file)
222 {
223   ByteVector v1 = file->renderString(file->d->tag->title());
224   ByteVector v2 = file->renderString(file->d->tag->artist());
225   ByteVector v3 = file->renderString(file->d->tag->copyright());
226   ByteVector v4 = file->renderString(file->d->tag->comment());
227   ByteVector v5 = file->renderString(file->d->tag->rating());
228   data.clear();
229   data.append(ByteVector::fromShort(v1.size(), false));
230   data.append(ByteVector::fromShort(v2.size(), false));
231   data.append(ByteVector::fromShort(v3.size(), false));
232   data.append(ByteVector::fromShort(v4.size(), false));
233   data.append(ByteVector::fromShort(v5.size(), false));
234   data.append(v1);
235   data.append(v2);
236   data.append(v3);
237   data.append(v4);
238   data.append(v5);
239   return BaseObject::render(file);
240 }
241
242 ByteVector
243 ASF::File::ExtendedContentDescriptionObject::guid()
244 {
245   return extendedContentDescriptionGuid;
246 }
247
248 void
249 ASF::File::ExtendedContentDescriptionObject::parse(ASF::File *file, uint /*size*/)
250 {
251   file->d->extendedContentDescriptionObject = this;
252   int count = file->readWORD();
253   while(count--) {
254     ASF::Attribute attribute;
255     String name = attribute.parse(*file);
256     file->d->tag->addAttribute(name, attribute);
257   }
258 }
259
260 ByteVector
261 ASF::File::ExtendedContentDescriptionObject::render(ASF::File *file)
262 {
263   data.clear();
264   data.append(ByteVector::fromShort(attributeData.size(), false));
265   data.append(attributeData.toByteVector(ByteVector::null));
266   return BaseObject::render(file);
267 }
268
269 ByteVector
270 ASF::File::MetadataObject::guid()
271 {
272   return metadataGuid;
273 }
274
275 void
276 ASF::File::MetadataObject::parse(ASF::File *file, uint /*size*/)
277 {
278   file->d->metadataObject = this;
279   int count = file->readWORD();
280   while(count--) {
281     ASF::Attribute attribute;
282     String name = attribute.parse(*file, 1);
283     file->d->tag->addAttribute(name, attribute);
284   }
285 }
286
287 ByteVector
288 ASF::File::MetadataObject::render(ASF::File *file)
289 {
290   data.clear();
291   data.append(ByteVector::fromShort(attributeData.size(), false));
292   data.append(attributeData.toByteVector(ByteVector::null));
293   return BaseObject::render(file);
294 }
295
296 ByteVector
297 ASF::File::MetadataLibraryObject::guid()
298 {
299   return metadataLibraryGuid;
300 }
301
302 void
303 ASF::File::MetadataLibraryObject::parse(ASF::File *file, uint /*size*/)
304 {
305   file->d->metadataLibraryObject = this;
306   int count = file->readWORD();
307   while(count--) {
308     ASF::Attribute attribute;
309     String name = attribute.parse(*file, 2);
310     file->d->tag->addAttribute(name, attribute);
311   }
312 }
313
314 ByteVector
315 ASF::File::MetadataLibraryObject::render(ASF::File *file)
316 {
317   data.clear();
318   data.append(ByteVector::fromShort(attributeData.size(), false));
319   data.append(attributeData.toByteVector(ByteVector::null));
320   return BaseObject::render(file);
321 }
322
323 ByteVector
324 ASF::File::HeaderExtensionObject::guid()
325 {
326   return headerExtensionGuid;
327 }
328
329 void
330 ASF::File::HeaderExtensionObject::parse(ASF::File *file, uint /*size*/)
331 {
332   file->d->headerExtensionObject = this;
333   file->seek(18, File::Current);
334   long long dataSize = file->readDWORD();
335   long long dataPos = 0;
336   while(dataPos < dataSize) {
337     ByteVector guid = file->readBlock(16);
338     long long size = file->readQWORD();
339     BaseObject *obj;
340     if(guid == metadataGuid) {
341       obj = new MetadataObject();
342     }
343     else if(guid == metadataLibraryGuid) {
344       obj = new MetadataLibraryObject();
345     }
346     else {
347       obj = new UnknownObject(guid);
348     }
349     obj->parse(file, size);
350     objects.append(obj);
351     dataPos += size;
352   }
353 }
354
355 ByteVector
356 ASF::File::HeaderExtensionObject::render(ASF::File *file)
357 {
358   data.clear();
359   for(unsigned int i = 0; i < objects.size(); i++) {
360     data.append(objects[i]->render(file));
361   }
362   data = ByteVector("\x11\xD2\xD3\xAB\xBA\xA9\xcf\x11\x8E\xE6\x00\xC0\x0C\x20\x53\x65\x06\x00", 18) + ByteVector::fromUInt(data.size(), false) + data;
363   return BaseObject::render(file);
364 }
365
366 ////////////////////////////////////////////////////////////////////////////////
367 // public members
368 ////////////////////////////////////////////////////////////////////////////////
369
370 ASF::File::File(FileName file, bool readProperties, Properties::ReadStyle propertiesStyle) 
371   : TagLib::File(file)
372 {
373   d = new FilePrivate;
374   read(readProperties, propertiesStyle);
375 }
376
377 ASF::File::~File()
378 {
379   for(unsigned int i = 0; i < d->objects.size(); i++) {
380     delete d->objects[i];
381   }
382   if(d->tag) {
383     delete d->tag;
384   }
385   if(d->properties) {
386     delete d->properties;
387   }
388   delete d;
389 }
390
391 ASF::Tag *ASF::File::tag() const
392 {
393   return d->tag;
394 }
395
396 ASF::Properties *ASF::File::audioProperties() const
397 {
398   return d->properties;
399 }
400
401 void ASF::File::read(bool /*readProperties*/, Properties::ReadStyle /*propertiesStyle*/)
402 {
403   if(!isValid())
404     return;
405
406   ByteVector guid = readBlock(16);
407   if(guid != headerGuid) {
408     debug("ASF: Not an ASF file.");
409     return;
410   }
411
412   d->tag = new ASF::Tag();
413   d->properties = new ASF::Properties();
414
415   d->size = readQWORD();
416   int numObjects = readDWORD();
417   seek(2, Current);
418
419   for(int i = 0; i < numObjects; i++) {
420     ByteVector guid = readBlock(16);
421     long size = (long)readQWORD();
422     BaseObject *obj;
423     if(guid == filePropertiesGuid) {
424       obj = new FilePropertiesObject();
425     }
426     else if(guid == streamPropertiesGuid) {
427       obj = new StreamPropertiesObject();
428     }
429     else if(guid == contentDescriptionGuid) {
430       obj = new ContentDescriptionObject();
431     }
432     else if(guid == extendedContentDescriptionGuid) {
433       obj = new ExtendedContentDescriptionObject();
434     }
435     else if(guid == headerExtensionGuid) {
436       obj = new HeaderExtensionObject();
437     }
438     else {
439       obj = new UnknownObject(guid);
440     }
441     obj->parse(this, size);
442     d->objects.append(obj);
443   }
444 }
445
446 bool ASF::File::save()
447 {
448   if(readOnly()) {
449     debug("ASF: File is read-only.");
450     return false;
451   }
452
453   if(!d->contentDescriptionObject) {
454     d->contentDescriptionObject = new ContentDescriptionObject();
455     d->objects.append(d->contentDescriptionObject);
456   }
457   if(!d->extendedContentDescriptionObject) {
458     d->extendedContentDescriptionObject = new ExtendedContentDescriptionObject();
459     d->objects.append(d->extendedContentDescriptionObject);
460   }
461   if(!d->headerExtensionObject) {
462     d->headerExtensionObject = new HeaderExtensionObject();
463     d->objects.append(d->headerExtensionObject);
464   }
465   if(!d->metadataObject) {
466     d->metadataObject = new MetadataObject();
467     d->headerExtensionObject->objects.append(d->metadataObject);
468   }
469   if(!d->metadataLibraryObject) {
470     d->metadataLibraryObject = new MetadataLibraryObject();
471     d->headerExtensionObject->objects.append(d->metadataLibraryObject);
472   }
473
474   ASF::AttributeListMap::ConstIterator it = d->tag->attributeListMap().begin();
475   for(; it != d->tag->attributeListMap().end(); it++) {
476     const String &name = it->first;
477     const AttributeList &attributes = it->second;
478     bool inExtendedContentDescriptionObject = false;
479     bool inMetadataObject = false;
480     for(unsigned int j = 0; j < attributes.size(); j++) {
481       const Attribute &attribute = attributes[j];
482       bool largeValue = attribute.dataSize() > 65535;
483       if(!inExtendedContentDescriptionObject && !largeValue && attribute.language() == 0 && attribute.stream() == 0) {
484         d->extendedContentDescriptionObject->attributeData.append(attribute.render(name));
485         inExtendedContentDescriptionObject = true;
486       }
487       else if(!inMetadataObject && !largeValue && attribute.language() == 0 && attribute.stream() != 0) {
488         d->metadataObject->attributeData.append(attribute.render(name, 1));
489         inMetadataObject = true;
490       }
491       else {
492         d->metadataLibraryObject->attributeData.append(attribute.render(name, 2));
493       }
494     }
495   }
496
497   ByteVector data;
498   for(unsigned int i = 0; i < d->objects.size(); i++) {
499     data.append(d->objects[i]->render(this));
500   }
501   data = headerGuid + ByteVector::fromLongLong(data.size() + 30, false) + ByteVector::fromUInt(d->objects.size(), false) + ByteVector("\x01\x02", 2) + data;
502   insert(data, 0, d->size);
503
504   return true;
505 }
506
507 ////////////////////////////////////////////////////////////////////////////////
508 // protected members
509 ////////////////////////////////////////////////////////////////////////////////
510
511 int ASF::File::readBYTE()
512 {
513   ByteVector v = readBlock(1);
514   return v[0];
515 }
516
517 int ASF::File::readWORD()
518 {
519   ByteVector v = readBlock(2);
520   return v.toShort(false);
521 }
522
523 unsigned int ASF::File::readDWORD()
524 {
525   ByteVector v = readBlock(4);
526   return v.toUInt(false);
527 }
528
529 long long ASF::File::readQWORD()
530 {
531   ByteVector v = readBlock(8);
532   return v.toLongLong(false);
533 }
534
535 String
536 ASF::File::readString(int length)
537 {
538   ByteVector data = readBlock(length);
539   unsigned int size = data.size();
540   while (size >= 2) {
541     if(data[size - 1] != '\0' || data[size - 2] != '\0') {
542       break;
543     }
544     size -= 2;
545   }
546   if(size != data.size()) {
547     data.resize(size);
548   }
549   return String(data, String::UTF16LE);
550 }
551
552 ByteVector
553 ASF::File::renderString(const String &str, bool includeLength)
554 {
555   ByteVector data = str.data(String::UTF16LE) + ByteVector::fromShort(0, false);
556   if(includeLength) {
557     data = ByteVector::fromShort(data.size(), false) + data;
558   }
559   return data;
560 }