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., 51 Franklin Street, Fifth Floor, Boston, MA         *
19  *   02110-1301  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 #include <tdebug.h>
27 #include <tbytevectorlist.h>
28 #include <tpropertymap.h>
29 #include <tstring.h>
30 #include <tagutils.h>
31 
32 #include "asffile.h"
33 #include "asftag.h"
34 #include "asfproperties.h"
35 #include "asfutils.h"
36 
37 using namespace TagLib;
38 
39 class ASF::File::FilePrivate
40 {
41 public:
42   class BaseObject;
43   class UnknownObject;
44   class FilePropertiesObject;
45   class StreamPropertiesObject;
46   class ContentDescriptionObject;
47   class ExtendedContentDescriptionObject;
48   class HeaderExtensionObject;
49   class CodecListObject;
50   class MetadataObject;
51   class MetadataLibraryObject;
52 
FilePrivate()53   FilePrivate():
54     headerSize(0),
55     tag(0),
56     properties(0),
57     contentDescriptionObject(0),
58     extendedContentDescriptionObject(0),
59     headerExtensionObject(0),
60     metadataObject(0),
61     metadataLibraryObject(0)
62   {
63     objects.setAutoDelete(true);
64   }
65 
~FilePrivate()66   ~FilePrivate()
67   {
68     delete tag;
69     delete properties;
70   }
71 
72   unsigned long long headerSize;
73 
74   ASF::Tag *tag;
75   ASF::Properties *properties;
76 
77   List<BaseObject *> objects;
78 
79   ContentDescriptionObject         *contentDescriptionObject;
80   ExtendedContentDescriptionObject *extendedContentDescriptionObject;
81   HeaderExtensionObject            *headerExtensionObject;
82   MetadataObject                   *metadataObject;
83   MetadataLibraryObject            *metadataLibraryObject;
84 };
85 
86 namespace
87 {
88   const ByteVector headerGuid("\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C", 16);
89   const ByteVector filePropertiesGuid("\xA1\xDC\xAB\x8C\x47\xA9\xCF\x11\x8E\xE4\x00\xC0\x0C\x20\x53\x65", 16);
90   const ByteVector streamPropertiesGuid("\x91\x07\xDC\xB7\xB7\xA9\xCF\x11\x8E\xE6\x00\xC0\x0C\x20\x53\x65", 16);
91   const ByteVector contentDescriptionGuid("\x33\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C", 16);
92   const ByteVector extendedContentDescriptionGuid("\x40\xA4\xD0\xD2\x07\xE3\xD2\x11\x97\xF0\x00\xA0\xC9\x5E\xA8\x50", 16);
93   const ByteVector headerExtensionGuid("\xb5\x03\xbf_.\xa9\xcf\x11\x8e\xe3\x00\xc0\x0c Se", 16);
94   const ByteVector metadataGuid("\xEA\xCB\xF8\xC5\xAF[wH\204g\xAA\214D\xFAL\xCA", 16);
95   const ByteVector metadataLibraryGuid("\224\034#D\230\224\321I\241A\x1d\x13NEpT", 16);
96   const ByteVector codecListGuid("\x40\x52\xd1\x86\x1d\x31\xd0\x11\xa3\xa4\x00\xa0\xc9\x03\x48\xf6", 16);
97   const ByteVector contentEncryptionGuid("\xFB\xB3\x11\x22\x23\xBD\xD2\x11\xB4\xB7\x00\xA0\xC9\x55\xFC\x6E", 16);
98   const ByteVector extendedContentEncryptionGuid("\x14\xE6\x8A\x29\x22\x26 \x17\x4C\xB9\x35\xDA\xE0\x7E\xE9\x28\x9C", 16);
99   const ByteVector advancedContentEncryptionGuid("\xB6\x9B\x07\x7A\xA4\xDA\x12\x4E\xA5\xCA\x91\xD3\x8D\xC1\x1A\x8D", 16);
100 }
101 
102 class ASF::File::FilePrivate::BaseObject
103 {
104 public:
105   ByteVector data;
~BaseObject()106   virtual ~BaseObject() {}
107   virtual ByteVector guid() const = 0;
108   virtual void parse(ASF::File *file, unsigned int size);
109   virtual ByteVector render(ASF::File *file);
110 };
111 
112 class ASF::File::FilePrivate::UnknownObject : public ASF::File::FilePrivate::BaseObject
113 {
114   ByteVector myGuid;
115 public:
116   UnknownObject(const ByteVector &guid);
117   ByteVector guid() const;
118 };
119 
120 class ASF::File::FilePrivate::FilePropertiesObject : public ASF::File::FilePrivate::BaseObject
121 {
122 public:
123   ByteVector guid() const;
124   void parse(ASF::File *file, unsigned int size);
125 };
126 
127 class ASF::File::FilePrivate::StreamPropertiesObject : public ASF::File::FilePrivate::BaseObject
128 {
129 public:
130   ByteVector guid() const;
131   void parse(ASF::File *file, unsigned int size);
132 };
133 
134 class ASF::File::FilePrivate::ContentDescriptionObject : public ASF::File::FilePrivate::BaseObject
135 {
136 public:
137   ByteVector guid() const;
138   void parse(ASF::File *file, unsigned int size);
139   ByteVector render(ASF::File *file);
140 };
141 
142 class ASF::File::FilePrivate::ExtendedContentDescriptionObject : public ASF::File::FilePrivate::BaseObject
143 {
144 public:
145   ByteVectorList attributeData;
146   ByteVector guid() const;
147   void parse(ASF::File *file, unsigned int size);
148   ByteVector render(ASF::File *file);
149 };
150 
151 class ASF::File::FilePrivate::MetadataObject : public ASF::File::FilePrivate::BaseObject
152 {
153 public:
154   ByteVectorList attributeData;
155   ByteVector guid() const;
156   void parse(ASF::File *file, unsigned int size);
157   ByteVector render(ASF::File *file);
158 };
159 
160 class ASF::File::FilePrivate::MetadataLibraryObject : public ASF::File::FilePrivate::BaseObject
161 {
162 public:
163   ByteVectorList attributeData;
164   ByteVector guid() const;
165   void parse(ASF::File *file, unsigned int size);
166   ByteVector render(ASF::File *file);
167 };
168 
169 class ASF::File::FilePrivate::HeaderExtensionObject : public ASF::File::FilePrivate::BaseObject
170 {
171 public:
172   List<ASF::File::FilePrivate::BaseObject *> objects;
173   HeaderExtensionObject();
174   ByteVector guid() const;
175   void parse(ASF::File *file, unsigned int size);
176   ByteVector render(ASF::File *file);
177 };
178 
179 class ASF::File::FilePrivate::CodecListObject : public ASF::File::FilePrivate::BaseObject
180 {
181 public:
182   ByteVector guid() const;
183   void parse(ASF::File *file, unsigned int size);
184 
185 private:
186   enum CodecType
187   {
188     Video   = 0x0001,
189     Audio   = 0x0002,
190     Unknown = 0xFFFF
191   };
192 };
193 
parse(ASF::File * file,unsigned int size)194 void ASF::File::FilePrivate::BaseObject::parse(ASF::File *file, unsigned int size)
195 {
196   data.clear();
197   if(size > 24 && size <= (unsigned int)(file->length()))
198     data = file->readBlock(size - 24);
199   else
200     data = ByteVector();
201 }
202 
render(ASF::File *)203 ByteVector ASF::File::FilePrivate::BaseObject::render(ASF::File * /*file*/)
204 {
205   return guid() + ByteVector::fromLongLong(data.size() + 24, false) + data;
206 }
207 
UnknownObject(const ByteVector & guid)208 ASF::File::FilePrivate::UnknownObject::UnknownObject(const ByteVector &guid) : myGuid(guid)
209 {
210 }
211 
guid() const212 ByteVector ASF::File::FilePrivate::UnknownObject::guid() const
213 {
214   return myGuid;
215 }
216 
guid() const217 ByteVector ASF::File::FilePrivate::FilePropertiesObject::guid() const
218 {
219   return filePropertiesGuid;
220 }
221 
parse(ASF::File * file,unsigned int size)222 void ASF::File::FilePrivate::FilePropertiesObject::parse(ASF::File *file, unsigned int size)
223 {
224   BaseObject::parse(file, size);
225   if(data.size() < 64) {
226     debug("ASF::File::FilePrivate::FilePropertiesObject::parse() -- data is too short.");
227     return;
228   }
229 
230   const long long duration = data.toLongLong(40, false);
231   const long long preroll  = data.toLongLong(56, false);
232   file->d->properties->setLengthInMilliseconds(static_cast<int>(duration / 10000.0 - preroll + 0.5));
233 }
234 
guid() const235 ByteVector ASF::File::FilePrivate::StreamPropertiesObject::guid() const
236 {
237   return streamPropertiesGuid;
238 }
239 
parse(ASF::File * file,unsigned int size)240 void ASF::File::FilePrivate::StreamPropertiesObject::parse(ASF::File *file, unsigned int size)
241 {
242   BaseObject::parse(file, size);
243   if(data.size() < 70) {
244     debug("ASF::File::FilePrivate::StreamPropertiesObject::parse() -- data is too short.");
245     return;
246   }
247 
248   file->d->properties->setCodec(data.toUShort(54, false));
249   file->d->properties->setChannels(data.toUShort(56, false));
250   file->d->properties->setSampleRate(data.toUInt(58, false));
251   file->d->properties->setBitrate(static_cast<int>(data.toUInt(62, false) * 8.0 / 1000.0 + 0.5));
252   file->d->properties->setBitsPerSample(data.toUShort(68, false));
253 }
254 
guid() const255 ByteVector ASF::File::FilePrivate::ContentDescriptionObject::guid() const
256 {
257   return contentDescriptionGuid;
258 }
259 
parse(ASF::File * file,unsigned int)260 void ASF::File::FilePrivate::ContentDescriptionObject::parse(ASF::File *file, unsigned int /*size*/)
261 {
262   const int titleLength     = readWORD(file);
263   const int artistLength    = readWORD(file);
264   const int copyrightLength = readWORD(file);
265   const int commentLength   = readWORD(file);
266   const int ratingLength    = readWORD(file);
267   file->d->tag->setTitle(readString(file,titleLength));
268   file->d->tag->setArtist(readString(file,artistLength));
269   file->d->tag->setCopyright(readString(file,copyrightLength));
270   file->d->tag->setComment(readString(file,commentLength));
271   file->d->tag->setRating(readString(file,ratingLength));
272 }
273 
render(ASF::File * file)274 ByteVector ASF::File::FilePrivate::ContentDescriptionObject::render(ASF::File *file)
275 {
276   const ByteVector v1 = renderString(file->d->tag->title());
277   const ByteVector v2 = renderString(file->d->tag->artist());
278   const ByteVector v3 = renderString(file->d->tag->copyright());
279   const ByteVector v4 = renderString(file->d->tag->comment());
280   const ByteVector v5 = renderString(file->d->tag->rating());
281   data.clear();
282   data.append(ByteVector::fromShort(v1.size(), false));
283   data.append(ByteVector::fromShort(v2.size(), false));
284   data.append(ByteVector::fromShort(v3.size(), false));
285   data.append(ByteVector::fromShort(v4.size(), false));
286   data.append(ByteVector::fromShort(v5.size(), false));
287   data.append(v1);
288   data.append(v2);
289   data.append(v3);
290   data.append(v4);
291   data.append(v5);
292   return BaseObject::render(file);
293 }
294 
guid() const295 ByteVector ASF::File::FilePrivate::ExtendedContentDescriptionObject::guid() const
296 {
297   return extendedContentDescriptionGuid;
298 }
299 
parse(ASF::File * file,unsigned int)300 void ASF::File::FilePrivate::ExtendedContentDescriptionObject::parse(ASF::File *file, unsigned int /*size*/)
301 {
302   int count = readWORD(file);
303   while(count--) {
304     ASF::Attribute attribute;
305     String name = attribute.parse(*file);
306     file->d->tag->addAttribute(name, attribute);
307   }
308 }
309 
render(ASF::File * file)310 ByteVector ASF::File::FilePrivate::ExtendedContentDescriptionObject::render(ASF::File *file)
311 {
312   data.clear();
313   data.append(ByteVector::fromShort(attributeData.size(), false));
314   data.append(attributeData.toByteVector(""));
315   return BaseObject::render(file);
316 }
317 
guid() const318 ByteVector ASF::File::FilePrivate::MetadataObject::guid() const
319 {
320   return metadataGuid;
321 }
322 
parse(ASF::File * file,unsigned int)323 void ASF::File::FilePrivate::MetadataObject::parse(ASF::File *file, unsigned int /*size*/)
324 {
325   int count = readWORD(file);
326   while(count--) {
327     ASF::Attribute attribute;
328     String name = attribute.parse(*file, 1);
329     file->d->tag->addAttribute(name, attribute);
330   }
331 }
332 
render(ASF::File * file)333 ByteVector ASF::File::FilePrivate::MetadataObject::render(ASF::File *file)
334 {
335   data.clear();
336   data.append(ByteVector::fromShort(attributeData.size(), false));
337   data.append(attributeData.toByteVector(""));
338   return BaseObject::render(file);
339 }
340 
guid() const341 ByteVector ASF::File::FilePrivate::MetadataLibraryObject::guid() const
342 {
343   return metadataLibraryGuid;
344 }
345 
parse(ASF::File * file,unsigned int)346 void ASF::File::FilePrivate::MetadataLibraryObject::parse(ASF::File *file, unsigned int /*size*/)
347 {
348   int count = readWORD(file);
349   while(count--) {
350     ASF::Attribute attribute;
351     String name = attribute.parse(*file, 2);
352     file->d->tag->addAttribute(name, attribute);
353   }
354 }
355 
render(ASF::File * file)356 ByteVector ASF::File::FilePrivate::MetadataLibraryObject::render(ASF::File *file)
357 {
358   data.clear();
359   data.append(ByteVector::fromShort(attributeData.size(), false));
360   data.append(attributeData.toByteVector(""));
361   return BaseObject::render(file);
362 }
363 
HeaderExtensionObject()364 ASF::File::FilePrivate::HeaderExtensionObject::HeaderExtensionObject()
365 {
366   objects.setAutoDelete(true);
367 }
368 
guid() const369 ByteVector ASF::File::FilePrivate::HeaderExtensionObject::guid() const
370 {
371   return headerExtensionGuid;
372 }
373 
parse(ASF::File * file,unsigned int)374 void ASF::File::FilePrivate::HeaderExtensionObject::parse(ASF::File *file, unsigned int /*size*/)
375 {
376   file->seek(18, File::Current);
377   long long dataSize = readDWORD(file);
378   long long dataPos = 0;
379   while(dataPos < dataSize) {
380     ByteVector guid = file->readBlock(16);
381     if(guid.size() != 16) {
382       file->setValid(false);
383       break;
384     }
385     bool ok;
386     long long size = readQWORD(file, &ok);
387     if(!ok) {
388       file->setValid(false);
389       break;
390     }
391     BaseObject *obj;
392     if(guid == metadataGuid) {
393       file->d->metadataObject = new MetadataObject();
394       obj = file->d->metadataObject;
395     }
396     else if(guid == metadataLibraryGuid) {
397       file->d->metadataLibraryObject = new MetadataLibraryObject();
398       obj = file->d->metadataLibraryObject;
399     }
400     else {
401       obj = new UnknownObject(guid);
402     }
403     obj->parse(file, (unsigned int)size);
404     objects.append(obj);
405     dataPos += size;
406   }
407 }
408 
render(ASF::File * file)409 ByteVector ASF::File::FilePrivate::HeaderExtensionObject::render(ASF::File *file)
410 {
411   data.clear();
412   for(List<BaseObject *>::ConstIterator it = objects.begin(); it != objects.end(); ++it) {
413     data.append((*it)->render(file));
414   }
415   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;
416   return BaseObject::render(file);
417 }
418 
guid() const419 ByteVector ASF::File::FilePrivate::CodecListObject::guid() const
420 {
421   return codecListGuid;
422 }
423 
parse(ASF::File * file,unsigned int size)424 void ASF::File::FilePrivate::CodecListObject::parse(ASF::File *file, unsigned int size)
425 {
426   BaseObject::parse(file, size);
427   if(data.size() <= 20) {
428     debug("ASF::File::FilePrivate::CodecListObject::parse() -- data is too short.");
429     return;
430   }
431 
432   unsigned int pos = 16;
433 
434   const int count = data.toUInt(pos, false);
435   pos += 4;
436 
437   for(int i = 0; i < count; ++i) {
438 
439     if(pos >= data.size())
440       break;
441 
442     const CodecType type = static_cast<CodecType>(data.toUShort(pos, false));
443     pos += 2;
444 
445     int nameLength = data.toUShort(pos, false);
446     pos += 2;
447 
448     const unsigned int namePos = pos;
449     pos += nameLength * 2;
450 
451     const int descLength = data.toUShort(pos, false);
452     pos += 2;
453 
454     const unsigned int descPos = pos;
455     pos += descLength * 2;
456 
457     const int infoLength = data.toUShort(pos, false);
458     pos += 2 + infoLength * 2;
459 
460     if(type == CodecListObject::Audio) {
461       // First audio codec found.
462 
463       const String name(data.mid(namePos, nameLength * 2), String::UTF16LE);
464       file->d->properties->setCodecName(name.stripWhiteSpace());
465 
466       const String desc(data.mid(descPos, descLength * 2), String::UTF16LE);
467       file->d->properties->setCodecDescription(desc.stripWhiteSpace());
468 
469       break;
470     }
471   }
472 }
473 
474 ////////////////////////////////////////////////////////////////////////////////
475 // static members
476 ////////////////////////////////////////////////////////////////////////////////
477 
isSupported(IOStream * stream)478 bool ASF::File::isSupported(IOStream *stream)
479 {
480   // An ASF file has to start with the designated GUID.
481 
482   const ByteVector id = Utils::readHeader(stream, 16, false);
483   return (id == headerGuid);
484 }
485 
486 ////////////////////////////////////////////////////////////////////////////////
487 // public members
488 ////////////////////////////////////////////////////////////////////////////////
489 
File(FileName file,bool,Properties::ReadStyle)490 ASF::File::File(FileName file, bool, Properties::ReadStyle) :
491   TagLib::File(file),
492   d(new FilePrivate())
493 {
494   if(isOpen())
495     read();
496 }
497 
File(IOStream * stream,bool,Properties::ReadStyle)498 ASF::File::File(IOStream *stream, bool, Properties::ReadStyle) :
499   TagLib::File(stream),
500   d(new FilePrivate())
501 {
502   if(isOpen())
503     read();
504 }
505 
~File()506 ASF::File::~File()
507 {
508   delete d;
509 }
510 
tag() const511 ASF::Tag *ASF::File::tag() const
512 {
513   return d->tag;
514 }
515 
properties() const516 PropertyMap ASF::File::properties() const
517 {
518   return d->tag->properties();
519 }
520 
removeUnsupportedProperties(const StringList & properties)521 void ASF::File::removeUnsupportedProperties(const StringList &properties)
522 {
523   d->tag->removeUnsupportedProperties(properties);
524 }
525 
setProperties(const PropertyMap & properties)526 PropertyMap ASF::File::setProperties(const PropertyMap &properties)
527 {
528   return d->tag->setProperties(properties);
529 }
530 
audioProperties() const531 ASF::Properties *ASF::File::audioProperties() const
532 {
533   return d->properties;
534 }
535 
save()536 bool ASF::File::save()
537 {
538   if(readOnly()) {
539     debug("ASF::File::save() -- File is read only.");
540     return false;
541   }
542 
543   if(!isValid()) {
544     debug("ASF::File::save() -- Trying to save invalid file.");
545     return false;
546   }
547 
548   if(!d->contentDescriptionObject) {
549     d->contentDescriptionObject = new FilePrivate::ContentDescriptionObject();
550     d->objects.append(d->contentDescriptionObject);
551   }
552   if(!d->extendedContentDescriptionObject) {
553     d->extendedContentDescriptionObject = new FilePrivate::ExtendedContentDescriptionObject();
554     d->objects.append(d->extendedContentDescriptionObject);
555   }
556   if(!d->headerExtensionObject) {
557     d->headerExtensionObject = new FilePrivate::HeaderExtensionObject();
558     d->objects.append(d->headerExtensionObject);
559   }
560   if(!d->metadataObject) {
561     d->metadataObject = new FilePrivate::MetadataObject();
562     d->headerExtensionObject->objects.append(d->metadataObject);
563   }
564   if(!d->metadataLibraryObject) {
565     d->metadataLibraryObject = new FilePrivate::MetadataLibraryObject();
566     d->headerExtensionObject->objects.append(d->metadataLibraryObject);
567   }
568 
569   d->extendedContentDescriptionObject->attributeData.clear();
570   d->metadataObject->attributeData.clear();
571   d->metadataLibraryObject->attributeData.clear();
572 
573   const AttributeListMap allAttributes = d->tag->attributeListMap();
574 
575   for(AttributeListMap::ConstIterator it = allAttributes.begin(); it != allAttributes.end(); ++it) {
576 
577     const String &name = it->first;
578     const AttributeList &attributes = it->second;
579 
580     bool inExtendedContentDescriptionObject = false;
581     bool inMetadataObject = false;
582 
583     for(AttributeList::ConstIterator jt = attributes.begin(); jt != attributes.end(); ++jt) {
584 
585       const Attribute &attribute = *jt;
586       const bool largeValue = (attribute.dataSize() > 65535);
587       const bool guid       = (attribute.type() == Attribute::GuidType);
588 
589       if(!inExtendedContentDescriptionObject && !guid && !largeValue && attribute.language() == 0 && attribute.stream() == 0) {
590         d->extendedContentDescriptionObject->attributeData.append(attribute.render(name));
591         inExtendedContentDescriptionObject = true;
592       }
593       else if(!inMetadataObject && !guid && !largeValue && attribute.language() == 0 && attribute.stream() != 0) {
594         d->metadataObject->attributeData.append(attribute.render(name, 1));
595         inMetadataObject = true;
596       }
597       else {
598         d->metadataLibraryObject->attributeData.append(attribute.render(name, 2));
599       }
600     }
601   }
602 
603   ByteVector data;
604   for(List<FilePrivate::BaseObject *>::ConstIterator it = d->objects.begin(); it != d->objects.end(); ++it) {
605     data.append((*it)->render(this));
606   }
607 
608   seek(16);
609   writeBlock(ByteVector::fromLongLong(data.size() + 30, false));
610   writeBlock(ByteVector::fromUInt(d->objects.size(), false));
611   writeBlock(ByteVector("\x01\x02", 2));
612 
613   insert(data, 30, static_cast<unsigned long>(d->headerSize - 30));
614 
615   d->headerSize = data.size() + 30;
616 
617   return true;
618 }
619 
620 ////////////////////////////////////////////////////////////////////////////////
621 // private members
622 ////////////////////////////////////////////////////////////////////////////////
623 
read()624 void ASF::File::read()
625 {
626   if(!isValid())
627     return;
628 
629   if(readBlock(16) != headerGuid) {
630     debug("ASF::File::read(): Not an ASF file.");
631     setValid(false);
632     return;
633   }
634 
635   d->tag = new ASF::Tag();
636   d->properties = new ASF::Properties();
637 
638   bool ok;
639   d->headerSize = readQWORD(this, &ok);
640   if(!ok) {
641     setValid(false);
642     return;
643   }
644   int numObjects = readDWORD(this, &ok);
645   if(!ok) {
646     setValid(false);
647     return;
648   }
649   seek(2, Current);
650 
651   FilePrivate::FilePropertiesObject   *filePropertiesObject   = 0;
652   FilePrivate::StreamPropertiesObject *streamPropertiesObject = 0;
653   for(int i = 0; i < numObjects; i++) {
654     const ByteVector guid = readBlock(16);
655     if(guid.size() != 16) {
656       setValid(false);
657       break;
658     }
659     long size = (long)readQWORD(this, &ok);
660     if(!ok) {
661       setValid(false);
662       break;
663     }
664     FilePrivate::BaseObject *obj;
665     if(guid == filePropertiesGuid) {
666       filePropertiesObject = new FilePrivate::FilePropertiesObject();
667       obj = filePropertiesObject;
668     }
669     else if(guid == streamPropertiesGuid) {
670       streamPropertiesObject = new FilePrivate::StreamPropertiesObject();
671       obj = streamPropertiesObject;
672     }
673     else if(guid == contentDescriptionGuid) {
674       d->contentDescriptionObject = new FilePrivate::ContentDescriptionObject();
675       obj = d->contentDescriptionObject;
676     }
677     else if(guid == extendedContentDescriptionGuid) {
678       d->extendedContentDescriptionObject = new FilePrivate::ExtendedContentDescriptionObject();
679       obj = d->extendedContentDescriptionObject;
680     }
681     else if(guid == headerExtensionGuid) {
682       d->headerExtensionObject = new FilePrivate::HeaderExtensionObject();
683       obj = d->headerExtensionObject;
684     }
685     else if(guid == codecListGuid) {
686       obj = new FilePrivate::CodecListObject();
687     }
688     else {
689       if(guid == contentEncryptionGuid ||
690          guid == extendedContentEncryptionGuid ||
691          guid == advancedContentEncryptionGuid) {
692         d->properties->setEncrypted(true);
693       }
694       obj = new FilePrivate::UnknownObject(guid);
695     }
696     obj->parse(this, size);
697     d->objects.append(obj);
698   }
699 
700   if(!filePropertiesObject || !streamPropertiesObject) {
701     debug("ASF::File::read(): Missing mandatory header objects.");
702     setValid(false);
703     return;
704   }
705 }
706