1 // This file is part of Deark.
2 // Copyright (C) 2018 Jason Summers
3 // See the file COPYING for terms of use.
7 #include <deark-config.h>
8 #include <deark-private.h>
9 #include <deark-fmtutil.h>
10 DE_DECLARE_MODULE(de_module_asf
);
13 #define SID_METADATA 307
14 #define SID_METADATALIB 308
16 typedef struct localctx_struct
{
22 struct handler_params
{
28 const struct uuid_info
*uui
;
30 typedef void (*handler_fn_type
)(deark
*c
, lctx
*d
, struct handler_params
*hp
);
40 static const char *get_uuid_name(const u8
*uuid
);
42 static int do_object_sequence(deark
*c
, lctx
*d
, i64 pos1
, i64 len
, int level
,
43 int known_object_count
, i64 num_objects_expected
);
45 // Returns a copy of the 'buf' param
46 static char *format_date(i64 t_FILETIME
, char *buf
, size_t buf_len
)
48 struct de_timestamp timestamp
;
51 de_strlcpy(buf
, "unknown", buf_len
);
54 de_FILETIME_to_timestamp(t_FILETIME
, ×tamp
, 0x1);
55 de_timestamp_to_string(×tamp
, buf
, buf_len
, 0);
60 // Returns a copy of the 'buf' param
61 static char *format_duration(i64 n
, char *buf
, size_t buf_len
)
63 // TODO: Better formatting
64 de_snprintf(buf
, buf_len
, "%.3f sec", (double)n
/10000000.0);
68 static const char *get_metadata_dtype_name(unsigned int t
)
70 static const char *names
[7] = { "string", "bytes", "BOOL", "DWORD",
71 "QWORD", "WORD", "GUID" };
73 if(t
<DE_ARRAYCOUNT(names
)) {
79 // Read a GUID into the caller's buf[16], and convert it to UUID-style byte order.
80 // Also write a printable form of it to id_string.
81 static void read_and_render_guid(dbuf
*f
, u8
*id
, i64 pos
,
82 char *id_string
, size_t id_string_len
)
84 dbuf_read(f
, id
, pos
, 16);
85 fmtutil_guid_to_uuid(id
);
87 fmtutil_render_uuid(f
->c
, id
, id_string
, id_string_len
);
91 static void handler_Header(deark
*c
, lctx
*d
, struct handler_params
*hp
)
95 if(hp
->dlen
<6) return;
96 numhdrobj
= de_getu32le(hp
->dpos
);
97 de_dbg(c
, "number of header objects: %u", (unsigned int)numhdrobj
);
98 do_object_sequence(c
, d
, hp
->dpos
+6, hp
->dlen
-6, hp
->level
+1, 1, numhdrobj
);
101 static void handler_FileProperties(deark
*c
, lctx
*d
, struct handler_params
*hp
)
108 char guid_string
[50];
111 if(hp
->dlen
<80) return;
113 // Some fields before the 'flags' field depend on it, so look ahead at it.
114 flags
= (unsigned int)de_getu32le(pos
+64);
116 read_and_render_guid(c
->infile
, guid_raw
, pos
, guid_string
, sizeof(guid_string
));
117 de_dbg(c
, "file id: {%s}", guid_string
);
121 x
= de_geti64le(pos
);
122 de_dbg(c
, "file size: %"I64_FMT
, x
);
126 create_date
= de_geti64le(pos
);
127 de_dbg(c
, "creation date: %"I64_FMT
" (%s)", create_date
,
128 format_date(create_date
, buf
, sizeof(buf
)));
132 x
= de_geti64le(pos
);
133 de_dbg(c
, "data packets count: %"I64_FMT
, x
);
138 x
= de_geti64le(pos
);
139 de_dbg(c
, "play duration: %"I64_FMT
" (%s)", x
, format_duration(x
, buf
, sizeof(buf
)));
144 x
= de_geti64le(pos
);
145 de_dbg(c
, "send duration: %"I64_FMT
" (%s)", x
, format_duration(x
, buf
, sizeof(buf
)));
149 x
= de_geti64le(pos
);
150 de_dbg(c
, "preroll: %"I64_FMT
, x
);
153 // Already read, above.
154 de_dbg(c
, "flags: 0x%08x", flags
);
157 x
= de_getu32le_p(&pos
);
158 de_dbg(c
, "min data packet size: %u", (unsigned int)x
);
160 x
= de_getu32le_p(&pos
);
161 de_dbg(c
, "max data packet size: %u", (unsigned int)x
);
163 x
= de_getu32le_p(&pos
);
164 de_dbg(c
, "max bitrate: %u bits/sec", (unsigned int)x
);
167 static void handler_StreamProperties(deark
*c
, lctx
*d
, struct handler_params
*hp
)
175 char stream_type_string
[50];
176 char ec_type_string
[50];
179 if(hp
->dlen
<54) return;
181 read_and_render_guid(c
->infile
, stream_type
, pos
,
182 stream_type_string
, sizeof(stream_type_string
));
183 de_dbg(c
, "stream type: {%s} (%s)", stream_type_string
, get_uuid_name(stream_type
));
186 read_and_render_guid(c
->infile
, ec_type
, pos
, ec_type_string
, sizeof(ec_type_string
));
187 de_dbg(c
, "error correction type: {%s} (%s)", ec_type_string
, get_uuid_name(ec_type
));
190 x
= de_geti64le(pos
);
191 de_dbg(c
, "time offset: %"I64_FMT
" (%s)", x
, format_duration(x
, buf
, sizeof(buf
)));
194 tsdlen
= de_getu32le_p(&pos
);
195 ecdlen
= de_getu32le_p(&pos
);
197 flags
= (unsigned int)de_getu16le_p(&pos
);
198 de_dbg(c
, "flags: 0x%08x", flags
);
200 de_dbg(c
, "stream number: %u", (unsigned int)(flags
&0x7f));
201 de_dbg_indent(c
, -1);
203 pos
+= 4; // reserved
206 de_dbg(c
, "[%d bytes of type-specific data at %"I64_FMT
"]", (int)tsdlen
, pos
);
208 de_dbg_hexdump(c
, c
->infile
, pos
, tsdlen
, 256, NULL
, 0x1);
209 de_dbg_indent(c
, -1);
213 de_dbg(c
, "[%d bytes of error correction data at %"I64_FMT
"]", (int)ecdlen
, pos
);
215 de_dbg_hexdump(c
, c
->infile
, pos
, ecdlen
, 256, NULL
, 0x1);
216 de_dbg_indent(c
, -1);
221 static void handler_HeaderExtension(deark
*c
, lctx
*d
, struct handler_params
*hp
)
225 if(hp
->dlen
<22) return;
226 datasize
= de_getu32le(hp
->dpos
+18);
227 de_dbg(c
, "extension data size: %u", (unsigned int)datasize
);
228 if(datasize
> hp
->dlen
-22) datasize
= hp
->dlen
-22;
229 do_object_sequence(c
, d
, hp
->dpos
+22, datasize
, hp
->level
+1, 0, 0);
232 static void handler_ContentDescr(deark
*c
, lctx
*d
, struct handler_params
*hp
)
235 const char *names
[5] = { "title", "author", "copyright",
236 "description", "rating" };
237 de_ucstring
*s
= NULL
;
241 if(hp
->dlen
<10) return;
244 lengths
[k
] = de_getu16le_p(&pos
);
247 s
= ucstring_create(c
);
249 if(pos
+lengths
[k
] > hp
->dpos
+hp
->dlen
) break;
251 dbuf_read_to_ucstring_n(c
->infile
, pos
, lengths
[k
], DE_DBG_MAX_STRLEN
*2, s
,
252 0, DE_ENCODING_UTF16LE
);
253 ucstring_truncate_at_NUL(s
);
254 de_dbg(c
, "%s: \"%s\"", names
[k
], ucstring_getpsz_d(s
));
261 static void handler_ContentEncr(deark
*c
, lctx
*d
, struct handler_params
*hp
)
265 de_ucstring
*s
= NULL
;
267 xlen
= de_getu32le_p(&pos
);
268 if(pos
+xlen
> hp
->dpos
+hp
->dlen
) goto done
;
270 de_dbg(c
, "[%d bytes of secret data at %"I64_FMT
"]", (int)xlen
, pos
);
272 de_dbg_hexdump(c
, c
->infile
, pos
, xlen
, 256, NULL
, 0x0);
273 de_dbg_indent(c
, -1);
277 s
= ucstring_create(c
);
278 xlen
= de_getu32le_p(&pos
);
279 if(pos
+xlen
> hp
->dpos
+hp
->dlen
) goto done
;
280 dbuf_read_to_ucstring_n(c
->infile
, pos
, xlen
, DE_DBG_MAX_STRLEN
, s
,
281 0, DE_ENCODING_ASCII
);
282 ucstring_truncate_at_NUL(s
);
283 de_dbg(c
, "protection type: \"%s\"", ucstring_getpsz_d(s
));
284 // TODO: What should we do if this is not "DRM"?
288 xlen
= de_getu32le_p(&pos
);
289 if(pos
+xlen
> hp
->dpos
+hp
->dlen
) goto done
;
290 dbuf_read_to_ucstring_n(c
->infile
, pos
, xlen
, DE_DBG_MAX_STRLEN
, s
,
291 0, DE_ENCODING_ASCII
);
292 ucstring_truncate_at_NUL(s
);
293 de_dbg(c
, "key id: \"%s\"", ucstring_getpsz_d(s
));
297 xlen
= de_getu32le_p(&pos
);
298 if(pos
+xlen
> hp
->dpos
+hp
->dlen
) goto done
;
299 dbuf_read_to_ucstring_n(c
->infile
, pos
, xlen
, DE_DBG_MAX_STRLEN
, s
,
300 0, DE_ENCODING_ASCII
);
301 ucstring_truncate_at_NUL(s
);
302 de_dbg(c
, "license url: \"%s\"", ucstring_getpsz_d(s
));
309 // Extended Stream Properties
310 static void handler_ESP(deark
*c
, lctx
*d
, struct handler_params
*hp
)
313 i64 name_count
, pes_count
;
317 int saved_indent_level
;
319 char guid_string
[50];
321 de_dbg_indent_save(c
, &saved_indent_level
);
322 if(hp
->dlen
<64) goto done
;
324 x
= de_geti64le(pos
);
325 de_dbg(c
, "start time: %"I64_FMT
, x
);
327 x
= de_geti64le(pos
);
328 de_dbg(c
, "end time: %"I64_FMT
, x
);
331 x
= de_getu32le_p(&pos
);
332 de_dbg(c
, "data bitrate: %u", (unsigned int)x
);
333 x
= de_getu32le_p(&pos
);
334 de_dbg(c
, "buffer size: %u", (unsigned int)x
);
335 x
= de_getu32le_p(&pos
);
336 de_dbg(c
, "initial buffer fullness: %u", (unsigned int)x
);
337 x
= de_getu32le_p(&pos
);
338 de_dbg(c
, "alt data bitrate: %u", (unsigned int)x
);
339 x
= de_getu32le_p(&pos
);
340 de_dbg(c
, "alt buffer size: %u", (unsigned int)x
);
341 x
= de_getu32le_p(&pos
);
342 de_dbg(c
, "alt initial buffer fullness: %u", (unsigned int)x
);
343 x
= de_getu32le_p(&pos
);
344 de_dbg(c
, "max object size: %u", (unsigned int)x
);
345 x
= de_getu32le_p(&pos
);
346 de_dbg(c
, "flags: 0x%08x", (unsigned int)x
);
348 x
= de_getu16le_p(&pos
);
349 de_dbg(c
, "stream number: %d", (int)x
);
350 x
= de_getu16le_p(&pos
);
351 de_dbg(c
, "language id index: %d", (int)x
);
352 x
= de_geti64le(pos
);
353 de_dbg(c
, "average time per frame: %"I64_FMT
, x
);
356 name_count
= de_getu16le_p(&pos
);
357 de_dbg(c
, "name count: %d", (int)name_count
);
358 pes_count
= de_getu16le_p(&pos
);
359 de_dbg(c
, "payload ext. system count: %d", (int)pes_count
);
361 // Stream names (TODO)
362 for(k
=0; k
<name_count
; k
++) {
363 if(pos
+4 > hp
->dpos
+hp
->dlen
) goto done
;
364 de_dbg(c
, "name[%d] at %"I64_FMT
, (int)k
, pos
);
365 pos
+= 2; // language id index
366 xlen
= de_getu16le_p(&pos
);
370 // Payload extension systems
371 for(k
=0; k
<pes_count
; k
++) {
372 if(pos
+22 > hp
->dpos
+hp
->dlen
) {
375 de_dbg(c
, "payload ext. system[%d] at %"I64_FMT
, (int)k
, pos
);
378 read_and_render_guid(c
->infile
, guid_raw
, pos
, guid_string
, sizeof(guid_string
));
379 de_dbg(c
, "ext. system id: {%s} (%s)", guid_string
, get_uuid_name(guid_raw
));
382 x
= de_getu16le_p(&pos
);
383 de_dbg(c
, "ext. data size: %d", (int)x
);
385 xlen
= de_getu32le_p(&pos
);
386 de_dbg(c
, "payload ext. system info length: %d", (int)xlen
);
388 if(pos
+xlen
> hp
->dpos
+hp
->dlen
) {
392 de_dbg(c
, "[%d bytes of payload ext. system info at %"I64_FMT
, (int)xlen
, pos
);
394 de_dbg_hexdump(c
, c
->infile
, pos
, xlen
, 256, NULL
, 0x1);
395 de_dbg_indent(c
, -1);
398 de_dbg_indent(c
, -1);
401 bytes_remaining
= hp
->dpos
+ hp
->dlen
- pos
;
402 // There is an optional Stream Properties object here, but the spec seems
403 // less than clear about how to tell whether it is present.
404 if(bytes_remaining
>24+54) {
405 do_object_sequence(c
, d
, pos
, bytes_remaining
, hp
->level
+1, 1, 1);
409 de_dbg_indent_restore(c
, saved_indent_level
);
412 static void handler_LanguageList(deark
*c
, lctx
*d
, struct handler_params
*hp
)
417 de_ucstring
*s
= NULL
;
419 if(hp
->dlen
<2) goto done
;
421 nlangs
= de_getu16le_p(&pos
);
422 de_dbg(c
, "language id record count: %d", (int)nlangs
);
424 s
= ucstring_create(c
);
426 for(k
=0; k
<nlangs
; k
++) {
429 if(pos
+1 > hp
->dpos
+hp
->dlen
) goto done
;
430 de_dbg(c
, "language id record[%d] at %"I64_FMT
, (int)k
, pos
);
433 id_len
= (i64
)de_getbyte_p(&pos
);
436 dbuf_read_to_ucstring_n(c
->infile
, pos
, id_len
, DE_DBG_MAX_STRLEN
*2, s
,
437 0, DE_ENCODING_UTF16LE
);
438 ucstring_truncate_at_NUL(s
);
439 de_dbg(c
, "id: \"%s\"", ucstring_getpsz_d(s
));
442 de_dbg_indent(c
, -1);
449 static const char *get_codec_type_name(unsigned int t
)
451 const char *name
= "?";
453 case 0x0001: name
="video"; break;
454 case 0x0002: name
="audio"; break;
455 case 0xffff: name
="unknown"; break;
460 static int do_codec_entry(deark
*c
, lctx
*d
, i64 pos1
, i64 len
, i64
*bytes_consumed
)
462 de_ucstring
*name
= NULL
;
463 de_ucstring
*descr
= NULL
;
465 i64 namelen
, descrlen
, infolen
;
468 int saved_indent_level
;
471 de_dbg_indent_save(c
, &saved_indent_level
);
472 de_dbg(c
, "codec entry at %"I64_FMT
, pos1
);
476 type
= (unsigned int)de_getu16le_p(&pos
);
477 de_dbg(c
, "type: %u (%s)", type
, get_codec_type_name(type
));
479 namelen
= de_getu16le_p(&pos
);
480 name
= ucstring_create(c
);
481 dbuf_read_to_ucstring_n(c
->infile
, pos
, namelen
*2, DE_DBG_MAX_STRLEN
*2, name
,
482 0, DE_ENCODING_UTF16LE
);
483 ucstring_truncate_at_NUL(name
);
484 de_dbg(c
, "name: \"%s\"", ucstring_getpsz(name
));
487 descrlen
= de_getu16le_p(&pos
);
488 descr
= ucstring_create(c
);
489 dbuf_read_to_ucstring_n(c
->infile
, pos
, descrlen
*2, DE_DBG_MAX_STRLEN
*2, descr
,
490 0, DE_ENCODING_UTF16LE
);
491 ucstring_truncate_at_NUL(descr
);
492 de_dbg(c
, "description: \"%s\"", ucstring_getpsz(descr
));
495 infolen
= de_getu16le_p(&pos
);
497 de_dbg(c
, "[%d bytes of codec information at %"I64_FMT
"]", (int)infolen
, pos
);
499 de_dbg_hexdump(c
, c
->infile
, pos
, infolen
, 256, NULL
, 0x1);
500 de_dbg_indent(c
, -1);
504 *bytes_consumed
= pos
-pos1
;
507 de_dbg_indent_restore(c
, saved_indent_level
);
508 ucstring_destroy(name
);
509 ucstring_destroy(descr
);
513 static void handler_CodecList(deark
*c
, lctx
*d
, struct handler_params
*hp
)
519 if(hp
->dlen
<20) return;
520 numentries
= de_getu32le(hp
->dpos
+16);
521 de_dbg(c
, "number of codec entries: %d", (int)numentries
);
524 for(k
=0; k
<numentries
; k
++) {
525 i64 bytes_consumed
= 0;
528 if(pos
>= hp
->dpos
+ hp
->dlen
) break;
529 ret
= do_codec_entry(c
, d
, pos
, hp
->dpos
+hp
->dlen
-pos
, &bytes_consumed
);
530 if(!ret
|| (bytes_consumed
<8)) break;
531 pos
+= bytes_consumed
;
535 static void handler_ScriptCommand(deark
*c
, lctx
*d
, struct handler_params
*hp
)
537 i64 cmd_count
, cmd_type_count
;
540 de_ucstring
*s
= NULL
;
541 int saved_indent_level
;
543 de_dbg_indent_save(c
, &saved_indent_level
);
544 if(hp
->dlen
<20) goto done
;
545 pos
+= 16; // Reserved GUID
547 cmd_count
= de_getu16le_p(&pos
);
548 de_dbg(c
, "commands count: %d", (int)cmd_count
);
550 cmd_type_count
= de_getu16le_p(&pos
);
551 de_dbg(c
, "command types count: %d", (int)cmd_type_count
);
553 s
= ucstring_create(c
);
555 for(k
=0; k
<cmd_type_count
; k
++) {
558 if(pos
+2 > hp
->dpos
+hp
->dlen
) goto done
;
559 de_dbg(c
, "command type[%d] at %"I64_FMT
, (int)k
, pos
);
562 type_name_len
= de_getu16le_p(&pos
);
565 dbuf_read_to_ucstring_n(c
->infile
, pos
, type_name_len
*2, DE_DBG_MAX_STRLEN
*2, s
,
566 0, DE_ENCODING_UTF16LE
);
567 ucstring_truncate_at_NUL(s
);
568 de_dbg(c
, "name: \"%s\"", ucstring_getpsz_d(s
));
569 pos
+= type_name_len
*2;
571 de_dbg_indent(c
, -1);
574 for(k
=0; k
<cmd_count
; k
++) {
578 if(pos
+8 > hp
->dpos
+hp
->dlen
) goto done
;
579 de_dbg(c
, "command[%d] at %"I64_FMT
, (int)k
, pos
);
582 n
= de_getu32le_p(&pos
);
583 de_dbg(c
, "presentation time: %u ms", (unsigned int)n
);
585 n
= de_getu16le_p(&pos
);
586 de_dbg(c
, "type index: %d", (int)n
);
588 cmd_name_len
= de_getu16le_p(&pos
);
591 dbuf_read_to_ucstring_n(c
->infile
, pos
, cmd_name_len
*2, DE_DBG_MAX_STRLEN
*2, s
,
592 0, DE_ENCODING_UTF16LE
);
593 ucstring_truncate_at_NUL(s
);
594 de_dbg(c
, "name: \"%s\"", ucstring_getpsz_d(s
));
595 pos
+= cmd_name_len
*2;
597 de_dbg_indent(c
, -1);
601 de_dbg_indent_restore(c
, saved_indent_level
);
605 static void do_ECD_ID3(deark
*c
, lctx
*d
, i64 pos
, i64 len
)
607 de_dbg(c
, "ID3 data at %"I64_FMT
", len=%"I64_FMT
, pos
, len
);
609 de_run_module_by_id_on_slice2(c
, "id3", "I", c
->infile
, pos
, len
);
610 de_dbg_indent(c
, -1);
613 static void do_ECD_WMPicture(deark
*c
, lctx
*d
, i64 pos
, i64 len
)
615 de_dbg(c
, "WM/Picture data at %"I64_FMT
", len=%"I64_FMT
, pos
, len
);
617 de_run_module_by_id_on_slice2(c
, "id3", "P", c
->infile
, pos
, len
);
618 de_dbg_indent(c
, -1);
621 static void do_WMEncodingTime(deark
*c
, lctx
*d
, i64 t
)
625 de_dbg(c
, "value: %"I64_FMT
" (%s)", t
, format_date(t
, buf
, sizeof(buf
)));
628 static void do_metadata_item(deark
*c
, lctx
*d
, i64 pos
, i64 val_len
,
629 unsigned int val_data_type_ori
, struct de_stringreaderdata
*name_srd
,
632 de_ucstring
*val_str
= NULL
;
635 unsigned int val_data_type
; // adjusted type
637 de_dbg(c
, "value data at %"I64_FMT
", len=%d", pos
, (int)val_len
);
639 val_data_type
= val_data_type_ori
; // default
640 if(val_data_type_ori
==2) {
641 if(object_sid
==SID_ECD
) {
642 val_data_type
= 3; // Pretend a 32-bit BOOL is a DWORD
645 val_data_type
= 5; // Pretend a 16-bit BOOL is a WORD
649 if(val_data_type
==0 && val_len
>=2) { // Unicode string
650 val_str
= ucstring_create(c
);
651 dbuf_read_to_ucstring_n(c
->infile
, pos
, val_len
-2, DE_DBG_MAX_STRLEN
*2, val_str
,
652 0, DE_ENCODING_UTF16LE
);
653 de_dbg(c
, "value: \"%s\"", ucstring_getpsz(val_str
));
656 else if(val_data_type
==3 && val_len
>=4) { // DWORD
657 val_int
= de_getu32le(pos
);
658 de_dbg(c
, "value: %u", (unsigned int)val_int
);
661 else if(val_data_type
==4 && val_len
>=8) {
662 val_int
= de_geti64le(pos
);
663 if(!de_strcmp(name_srd
->sz_utf8
, "WM/EncodingTime")) {
664 do_WMEncodingTime(c
, d
, val_int
);
667 de_dbg(c
, "value: %"I64_FMT
, val_int
);
671 else if(val_data_type
==5 && val_len
>=2) { // WORD
672 val_int
= de_getu16le(pos
);
673 de_dbg(c
, "value: %u", (unsigned int)val_int
);
676 else if(val_data_type
==6 && val_len
>=16) { // GUID
678 char guid_string
[50];
680 read_and_render_guid(c
->infile
, guid_raw
, pos
, guid_string
, sizeof(guid_string
));
681 de_dbg(c
, "value: {%s}", guid_string
);
684 else if(val_data_type
==1) { // binary
685 if(!de_strcmp(name_srd
->sz_utf8
, "ID3")) {
686 do_ECD_ID3(c
, d
, pos
, val_len
);
689 else if(!de_strcmp(name_srd
->sz_utf8
, "WM/Picture")) {
690 do_ECD_WMPicture(c
, d
, pos
, val_len
);
697 de_dbg_hexdump(c
, c
->infile
, pos
, val_len
, 256, NULL
, 0x1);
698 de_dbg_indent(c
, -1);
701 ucstring_destroy(val_str
);
704 static int do_ECD_entry(deark
*c
, lctx
*d
, i64 pos1
, i64 len
, i64
*bytes_consumed
)
707 struct de_stringreaderdata
*name_srd
= NULL
;
710 unsigned int val_data_type
;
713 int saved_indent_level
;
716 de_dbg_indent_save(c
, &saved_indent_level
);
717 de_dbg(c
, "ECD object at %"I64_FMT
, pos1
);
721 namelen
= de_getu16le_p(&pos
); // # of bytes, including the expected 0x00 0x00 terminator
722 namelen_to_keep
= namelen
-2;
723 if(namelen_to_keep
<0) namelen_to_keep
=0;
724 if(namelen_to_keep
>256) namelen_to_keep
=256;
725 name_srd
= dbuf_read_string(c
->infile
, pos
, namelen_to_keep
, namelen_to_keep
,
726 DE_CONVFLAG_WANT_UTF8
, DE_ENCODING_UTF16LE
);
727 de_dbg(c
, "name: \"%s\"", ucstring_getpsz_d(name_srd
->str
));
730 val_data_type
= (unsigned int)de_getu16le_p(&pos
);
731 de_dbg(c
, "value data type: %u (%s)", val_data_type
,
732 get_metadata_dtype_name(val_data_type
));
734 val_len
= de_getu16le_p(&pos
);
736 do_metadata_item(c
, d
, pos
, val_len
, val_data_type
, name_srd
, SID_ECD
);
740 *bytes_consumed
= pos
-pos1
;
743 de_dbg_indent_restore(c
, saved_indent_level
);
744 de_destroy_stringreaderdata(c
, name_srd
);
751 // This is a lot like do_ECD_entry().
752 static int do_metadata_entry(deark
*c
, lctx
*d
, struct handler_params
*hp
,
753 i64 pos1
, i64 len
, i64
*bytes_consumed
)
756 struct de_stringreaderdata
*name_srd
= NULL
;
759 unsigned int val_data_type
;
763 int saved_indent_level
;
766 de_dbg_indent_save(c
, &saved_indent_level
);
767 de_dbg(c
, "metadata object at %"I64_FMT
, pos1
);
770 if(len
<14) goto done
;
772 if(hp
->uui
->short_id
==SID_METADATALIB
) {
774 lang_list_idx
= de_getu16le(pos
);
775 de_dbg(c
, "language list index: %d", (int)lang_list_idx
);
777 pos
+= 2; // Lang list index, or reserved
779 stream_number
= de_getu16le_p(&pos
);
780 de_dbg(c
, "stream number: %d", (int)stream_number
);
782 namelen
= de_getu16le_p(&pos
); // # of bytes, including the expected 0x00 0x00 terminator
784 val_data_type
= (unsigned int)de_getu16le_p(&pos
);
785 de_dbg(c
, "value data type: %u (%s)", val_data_type
,
786 get_metadata_dtype_name(val_data_type
));
788 val_len
= de_getu32le_p(&pos
);
790 namelen_to_keep
= namelen
-2;
791 if(namelen_to_keep
<0) namelen_to_keep
=0;
792 if(namelen_to_keep
>256) namelen_to_keep
=256;
793 name_srd
= dbuf_read_string(c
->infile
, pos
, namelen_to_keep
, namelen_to_keep
,
794 DE_CONVFLAG_WANT_UTF8
, DE_ENCODING_UTF16LE
);
795 de_dbg(c
, "name: \"%s\"", ucstring_getpsz_d(name_srd
->str
));
798 do_metadata_item(c
, d
, pos
, val_len
, val_data_type
, name_srd
, hp
->uui
->short_id
);
802 *bytes_consumed
= pos
-pos1
;
805 de_dbg_indent_restore(c
, saved_indent_level
);
806 de_destroy_stringreaderdata(c
, name_srd
);
811 // Extended Content Description
814 static void handler_ECD_or_metadata(deark
*c
, lctx
*d
, struct handler_params
*hp
)
820 descr_count
= de_getu16le_p(&pos
);
821 de_dbg(c
, "descriptor count: %d", (int)descr_count
);
823 for(k
=0; k
<descr_count
; k
++) {
824 i64 bytes_consumed
= 0;
827 if(pos
>= hp
->dpos
+ hp
->dlen
) {
830 if(hp
->uui
->short_id
==SID_ECD
) {
831 ret
= do_ECD_entry(c
, d
, pos
, hp
->dpos
+hp
->dlen
-pos
, &bytes_consumed
);
833 else if(hp
->uui
->short_id
==SID_METADATA
|| hp
->uui
->short_id
==SID_METADATALIB
) {
834 ret
= do_metadata_entry(c
, d
, hp
, pos
, hp
->dpos
+hp
->dlen
-pos
, &bytes_consumed
);
839 if(!ret
|| (bytes_consumed
<6)) break;
840 pos
+= bytes_consumed
;
844 static const struct uuid_info object_info_arr
[] = {
845 {101, 0, {0x75,0xb2,0x26,0x30,0x66,0x8e,0x11,0xcf,0xa6,0xd9,0x00,0xaa,0x00,0x62,0xce,0x6c}, "Header", handler_Header
},
846 {102, 0, {0x75,0xb2,0x26,0x36,0x66,0x8e,0x11,0xcf,0xa6,0xd9,0x00,0xaa,0x00,0x62,0xce,0x6c}, "Data", NULL
},
847 {103, 0, {0x33,0x00,0x08,0x90,0xe5,0xb1,0x11,0xcf,0x89,0xf4,0x00,0xa0,0xc9,0x03,0x49,0xcb}, "Simple Index", NULL
},
848 {104, 0, {0xd6,0xe2,0x29,0xd3,0x35,0xda,0x11,0xd1,0x90,0x34,0x00,0xa0,0xc9,0x03,0x49,0xbe}, "Index", NULL
},
849 {105, 0, {0xfe,0xb1,0x03,0xf8,0x12,0xad,0x4c,0x64,0x84,0x0f,0x2a,0x1d,0x2f,0x7a,0xd4,0x8c}, "Media Object Index", NULL
},
850 {106, 0, {0x3c,0xb7,0x3f,0xd0,0x0c,0x4a,0x48,0x03,0x95,0x3d,0xed,0xf7,0xb6,0x22,0x8f,0x0c}, "Timecode Index", NULL
},
851 {201, 0, {0x8c,0xab,0xdc,0xa1,0xa9,0x47,0x11,0xcf,0x8e,0xe4,0x00,0xc0,0x0c,0x20,0x53,0x65}, "File Properties", handler_FileProperties
},
852 {202, 0, {0xb7,0xdc,0x07,0x91,0xa9,0xb7,0x11,0xcf,0x8e,0xe6,0x00,0xc0,0x0c,0x20,0x53,0x65}, "Stream Properties", handler_StreamProperties
},
853 {203, 0, {0x5f,0xbf,0x03,0xb5,0xa9,0x2e,0x11,0xcf,0x8e,0xe3,0x00,0xc0,0x0c,0x20,0x53,0x65}, "Header Extension", handler_HeaderExtension
},
854 {204, 0, {0x86,0xd1,0x52,0x40,0x31,0x1d,0x11,0xd0,0xa3,0xa4,0x00,0xa0,0xc9,0x03,0x48,0xf6}, "Codec List", handler_CodecList
},
855 {205, 0, {0x1e,0xfb,0x1a,0x30,0x0b,0x62,0x11,0xd0,0xa3,0x9b,0x00,0xa0,0xc9,0x03,0x48,0xf6}, "Script Command", handler_ScriptCommand
},
856 {206, 0, {0xf4,0x87,0xcd,0x01,0xa9,0x51,0x11,0xcf,0x8e,0xe6,0x00,0xc0,0x0c,0x20,0x53,0x65}, "Marker", NULL
},
857 {207, 0, {0xd6,0xe2,0x29,0xdc,0x35,0xda,0x11,0xd1,0x90,0x34,0x00,0xa0,0xc9,0x03,0x49,0xbe}, "Bitrate Mutual Exclusion", NULL
},
858 {208, 0, {0x75,0xb2,0x26,0x35,0x66,0x8e,0x11,0xcf,0xa6,0xd9,0x00,0xaa,0x00,0x62,0xce,0x6c}, "Error Correction", NULL
},
859 {209, 0, {0x75,0xb2,0x26,0x33,0x66,0x8e,0x11,0xcf,0xa6,0xd9,0x00,0xaa,0x00,0x62,0xce,0x6c}, "Content Description", handler_ContentDescr
},
860 {SID_ECD
, 0, {0xd2,0xd0,0xa4,0x40,0xe3,0x07,0x11,0xd2,0x97,0xf0,0x00,0xa0,0xc9,0x5e,0xa8,0x50},
861 "Extended Content Description", handler_ECD_or_metadata
},
862 {211, 0, {0x22,0x11,0xb3,0xfa,0xbd,0x23,0x11,0xd2,0xb4,0xb7,0x00,0xa0,0xc9,0x55,0xfc,0x6e}, "Content Branding", NULL
},
863 {212, 0, {0x7b,0xf8,0x75,0xce,0x46,0x8d,0x11,0xd1,0x8d,0x82,0x00,0x60,0x97,0xc9,0xa2,0xb2}, "Stream Bitrate Properties", NULL
},
864 {213, 0, {0x22,0x11,0xb3,0xfb,0xbd,0x23,0x11,0xd2,0xb4,0xb7,0x00,0xa0,0xc9,0x55,0xfc,0x6e}, "Content Encryption", handler_ContentEncr
},
865 {214, 0, {0x29,0x8a,0xe6,0x14,0x26,0x22,0x4c,0x17,0xb9,0x35,0xda,0xe0,0x7e,0xe9,0x28,0x9c}, "Extended Content Encryption", NULL
},
866 {215, 0, {0x22,0x11,0xb3,0xfc,0xbd,0x23,0x11,0xd2,0xb4,0xb7,0x00,0xa0,0xc9,0x55,0xfc,0x6e}, "Digital Signature", NULL
},
867 {216, 0, {0x18,0x06,0xd4,0x74,0xca,0xdf,0x45,0x09,0xa4,0xba,0x9a,0xab,0xcb,0x96,0xaa,0xe8}, "Padding", NULL
},
868 {301, 0, {0x14,0xe6,0xa5,0xcb,0xc6,0x72,0x43,0x32,0x83,0x99,0xa9,0x69,0x52,0x06,0x5b,0x5a}, "Extended Stream Properties", handler_ESP
},
869 {302, 0, {0xa0,0x86,0x49,0xcf,0x47,0x75,0x46,0x70,0x8a,0x16,0x6e,0x35,0x35,0x75,0x66,0xcd}, "Advanced Mutual Exclusion", NULL
},
870 {303, 0, {0xd1,0x46,0x5a,0x40,0x5a,0x79,0x43,0x38,0xb7,0x1b,0xe3,0x6b,0x8f,0xd6,0xc2,0x49}, "Group Mutual Exclusion", NULL
},
871 {304, 0, {0xd4,0xfe,0xd1,0x5b,0x88,0xd3,0x45,0x4f,0x81,0xf0,0xed,0x5c,0x45,0x99,0x9e,0x24}, "Stream Prioritization", NULL
},
872 {305, 0, {0xa6,0x96,0x09,0xe6,0x51,0x7b,0x11,0xd2,0xb6,0xaf,0x00,0xc0,0x4f,0xd9,0x08,0xe9}, "Bandwidth Sharing", NULL
},
873 {306, 0, {0x7c,0x43,0x46,0xa9,0xef,0xe0,0x4b,0xfc,0xb2,0x29,0x39,0x3e,0xde,0x41,0x5c,0x85}, "Language List", handler_LanguageList
},
874 {SID_METADATA
, 0, {0xc5,0xf8,0xcb,0xea,0x5b,0xaf,0x48,0x77,0x84,0x67,0xaa,0x8c,0x44,0xfa,0x4c,0xca},
875 "Metadata", handler_ECD_or_metadata
},
876 {SID_METADATALIB
, 0, {0x44,0x23,0x1c,0x94,0x94,0x98,0x49,0xd1,0xa1,0x41,0x1d,0x13,0x4e,0x45,0x70,0x54},
877 "Metadata Library", handler_ECD_or_metadata
},
878 {309, 0, {0xd6,0xe2,0x29,0xdf,0x35,0xda,0x11,0xd1,0x90,0x34,0x00,0xa0,0xc9,0x03,0x49,0xbe}, "Index Parameters", NULL
},
879 {310, 0, {0x6b,0x20,0x3b,0xad,0x3f,0x11,0x48,0xe4,0xac,0xa8,0xd7,0x61,0x3d,0xe2,0xcf,0xa7}, "Media Object Index Parameters", NULL
},
880 {311, 0, {0xf5,0x5e,0x49,0x6d,0x97,0x97,0x4b,0x5d,0x8c,0x8b,0x60,0x4d,0xfe,0x9b,0xfb,0x24}, "Timecode Index Parameters", NULL
},
881 {312, 0, {0x26,0xf1,0x8b,0x5d,0x45,0x84,0x47,0xec,0x9f,0x5f,0x0e,0x65,0x1f,0x04,0x52,0xc9}, "Compatibility", NULL
},
882 {313, 0, {0x43,0x05,0x85,0x33,0x69,0x81,0x49,0xe6,0x9b,0x74,0xad,0x12,0xcb,0x86,0xd5,0x8c}, "Advanced Content Encryption", NULL
},
883 {330, 0, {0xd9,0xaa,0xde,0x20,0x7c,0x17,0x4f,0x9c,0xbc,0x28,0x85,0x55,0xdd,0x98,0xe2,0xa2}, "Index Placeholder", NULL
}
886 // GUIDs used for things other than objects
887 static const struct uuid_info uuid_info_arr
[] = {
888 // Stream properties object stream types
889 {0, 0x01, {0xf8,0x69,0x9e,0x40,0x5b,0x4d,0x11,0xcf,0xa8,0xfd,0x00,0x80,0x5f,0x5c,0x44,0x2b}, "Audio", NULL
},
890 {0, 0x01, {0xbc,0x19,0xef,0xc0,0x5b,0x4d,0x11,0xcf,0xa8,0xfd,0x00,0x80,0x5f,0x5c,0x44,0x2b}, "Video", NULL
},
891 {0, 0x01, {0x59,0xda,0xcf,0xc0,0x59,0xe6,0x11,0xd0,0xa3,0xac,0x00,0xa0,0xc9,0x03,0x48,0xf6}, "Command", NULL
},
892 {0, 0x01, {0xb6,0x1b,0xe1,0x00,0x5b,0x4e,0x11,0xcf,0xa8,0xfd,0x00,0x80,0x5f,0x5c,0x44,0x2b}, "JFIF", NULL
},
893 {0, 0x01, {0x35,0x90,0x7d,0xe0,0xe4,0x15,0x11,0xcf,0xa9,0x17,0x00,0x80,0x5f,0x5c,0x44,0x2b}, "Degradable JPEG", NULL
},
894 {0, 0x01, {0x91,0xbd,0x22,0x2c,0xf2,0x1c,0x49,0x7a,0x8b,0x6d,0x5a,0xa8,0x6b,0xfc,0x01,0x85}, "File transfer", NULL
},
895 {0, 0x01, {0x3a,0xfb,0x65,0xe2,0x47,0xef,0x40,0xf2,0xac,0x2c,0x70,0xa9,0x0d,0x71,0xd3,0x43}, "Binary", NULL
},
896 // Stream properties object error correction types
897 {0, 0x02, {0x20,0xfb,0x57,0x00,0x5b,0x55,0x11,0xcf,0xa8,0xfd,0x00,0x80,0x5f,0x5c,0x44,0x2b}, "No error correction", NULL
},
898 {0, 0x02, {0xbf,0xc3,0xcd,0x50,0x61,0x8f,0x11,0xcf,0x8b,0xb2,0x00,0xaa,0x00,0xb4,0xe2,0x20}, "Audio spread", NULL
},
899 // Payload extension system GUIDs
900 {0, 0x03, {0x39,0x95,0x95,0xec,0x86,0x67,0x4e,0x2d,0x8f,0xdb,0x98,0x81,0x4c,0xe7,0x6c,0x1e}, "Timecode", NULL
},
901 {0, 0x03, {0xe1,0x65,0xec,0x0e,0x19,0xed,0x45,0xd7,0xb4,0xa7,0x25,0xcb,0xd1,0xe2,0x8e,0x9b}, "File name", NULL
},
902 {0, 0x03, {0xd5,0x90,0xdc,0x20,0x07,0xbc,0x43,0x6c,0x9c,0xf7,0xf3,0xbb,0xfb,0xf1,0xa4,0xdc}, "Content type", NULL
},
903 {0, 0x03, {0x1b,0x1e,0xe5,0x54,0xf9,0xea,0x4b,0xc8,0x82,0x1a,0x37,0x6b,0x74,0xe4,0xc4,0xb8}, "Pixel aspect ratio", NULL
},
904 {0, 0x03, {0xc6,0xbd,0x94,0x50,0x86,0x7f,0x49,0x07,0x83,0xa3,0xc7,0x79,0x21,0xb7,0x33,0xad}, "Sample duration", NULL
},
905 {0, 0x03, {0x66,0x98,0xb8,0x4e,0x0a,0xfa,0x43,0x30,0xae,0xb2,0x1c,0x0a,0x98,0xd7,0xa4,0x4d}, "Encryption sample ID", NULL
},
906 {0, 0x03, {0x00,0xe1,0xaf,0x06,0x7b,0xec,0x11,0xd1,0xa5,0x82,0x00,0xc0,0x4f,0xc2,0x9c,0xfb}, "Degradable JPEG", NULL
}
909 static const struct uuid_info
*find_object_info(const u8
*uuid
)
912 for(k
=0; k
<DE_ARRAYCOUNT(object_info_arr
); k
++) {
913 if(!de_memcmp(uuid
, object_info_arr
[k
].uuid
, 16)) {
914 return &object_info_arr
[k
];
920 static const struct uuid_info
*find_uuid_info(const u8
*uuid
)
923 for(k
=0; k
<DE_ARRAYCOUNT(uuid_info_arr
); k
++) {
924 if(!de_memcmp(uuid
, uuid_info_arr
[k
].uuid
, 16)) {
925 return &uuid_info_arr
[k
];
931 static const char *get_uuid_name(const u8
*uuid
)
933 const struct uuid_info
*uui
;
934 uui
= find_uuid_info(uuid
);
935 if(uui
&& uui
->name
) return uui
->name
;
939 static int do_object(deark
*c
, lctx
*d
, i64 pos1
, i64 len
,
940 int level
, i64
*pbytes_consumed
)
944 const char *id_name
= NULL
;
946 int saved_indent_level
;
947 struct handler_params
*hp
= NULL
;
949 de_dbg_indent_save(c
, &saved_indent_level
);
950 *pbytes_consumed
= 0;
951 if(len
<24) goto done
;
953 de_dbg(c
, "object at %"I64_FMT
, pos1
);
956 hp
= de_malloc(c
, sizeof(struct handler_params
));
960 read_and_render_guid(c
->infile
, id
, pos1
, id_string
, sizeof(id_string
));
962 hp
->uui
= find_object_info(id
);
963 if(hp
->uui
) id_name
= hp
->uui
->name
;
964 if(!id_name
) id_name
= "?";
966 de_dbg(c
, "guid: {%s} (%s)", id_string
, id_name
);
968 hp
->objlen
= de_geti64le(pos1
+16);
969 hp
->dpos
= pos1
+ 24;
970 hp
->dlen
= hp
->objlen
- 24;
971 de_dbg(c
, "size: %"I64_FMT
", dpos=%"I64_FMT
", dlen=%"I64_FMT
,
972 hp
->objlen
, hp
->dpos
, hp
->dlen
);
973 if(hp
->objlen
<24) goto done
;
975 if(hp
->objlen
> len
) {
976 // TODO: Handle this differently depending on whether the problem was
977 // an unexpected end of file.
978 de_warn(c
, "Object at %"I64_FMT
" (length %"I64_FMT
") exceeds its parent's bounds",
983 if(hp
->uui
&& hp
->uui
->hfn
) {
984 hp
->uui
->hfn(c
, d
, hp
);
987 *pbytes_consumed
= hp
->objlen
;
990 de_dbg_indent_restore(c
, saved_indent_level
);
995 static int do_object_sequence(deark
*c
, lctx
*d
, i64 pos1
, i64 len
, int level
,
996 int known_object_count
, i64 num_objects_expected
)
1000 int saved_indent_level
;
1001 i64 bytes_remaining
;
1002 i64 objects_found
= 0;
1004 de_dbg_indent_save(c
, &saved_indent_level
);
1006 if(level
>= 16) { // An arbitrary recursion limit
1012 i64 bytes_consumed
= 0;
1014 bytes_remaining
= pos1
+len
-pos
;
1015 if(known_object_count
&& objects_found
>=num_objects_expected
) {
1019 if(bytes_remaining
<24) {
1023 ret
= do_object(c
, d
, pos
, bytes_remaining
, level
, &bytes_consumed
);
1025 if(bytes_consumed
<24) goto done
;
1028 pos
+= bytes_consumed
;
1031 bytes_remaining
= pos1
+len
-pos
;
1032 if(bytes_remaining
>0) {
1033 de_dbg(c
, "[%d extra bytes at %"I64_FMT
"]", (int)bytes_remaining
, pos
);
1036 if(known_object_count
&& objects_found
<num_objects_expected
) {
1037 de_warn(c
, "Expected %d objects at %"I64_FMT
", only found %d", (int)num_objects_expected
,
1038 pos1
, (int)objects_found
);
1042 de_dbg_indent_restore(c
, saved_indent_level
);
1046 static void de_run_asf(deark
*c
, de_module_params
*mparams
)
1050 d
= de_malloc(c
, sizeof(lctx
));
1052 do_object_sequence(c
, d
, 0, c
->infile
->len
, 0, 0, 0);
1057 static int de_identify_asf(deark
*c
)
1059 if(!dbuf_memcmp(c
->infile
, 0,
1060 "\x30\x26\xb2\x75\x8e\x66\xcf\x11\xa6\xd9\x00\xaa\x00\x62\xce\x6c", 16))
1067 void de_module_asf(deark
*c
, struct deark_module_info
*mi
)
1070 mi
->desc
= "ASF, WMV, WMA";
1071 mi
->run_fn
= de_run_asf
;
1072 mi
->identify_fn
= de_identify_asf
;