riff: Basic support for extracting RDIB images
[deark.git] / modules / asf.c
blob5af8d6c702fcdcfb3b70f9f17d4559bc00674f65
1 // This file is part of Deark.
2 // Copyright (C) 2018 Jason Summers
3 // See the file COPYING for terms of use.
5 // Microsoft ASF
7 #include <deark-config.h>
8 #include <deark-private.h>
9 #include <deark-fmtutil.h>
10 DE_DECLARE_MODULE(de_module_asf);
12 #define SID_ECD 210
13 #define SID_METADATA 307
14 #define SID_METADATALIB 308
16 typedef struct localctx_struct {
17 int reserved;
18 } lctx;
20 struct uuid_info;
22 struct handler_params {
23 i64 objpos;
24 i64 objlen;
25 i64 dpos;
26 i64 dlen;
27 int level;
28 const struct uuid_info *uui;
30 typedef void (*handler_fn_type)(deark *c, lctx *d, struct handler_params *hp);
32 struct uuid_info {
33 u32 short_id;
34 u32 flags;
35 const u8 uuid[16];
36 const char *name;
37 handler_fn_type hfn;
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;
50 if(t_FILETIME==0) {
51 de_strlcpy(buf, "unknown", buf_len);
53 else {
54 de_FILETIME_to_timestamp(t_FILETIME, &timestamp, 0x1);
55 de_timestamp_to_string(&timestamp, buf, buf_len, 0);
57 return buf;
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);
65 return buf;
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)) {
74 return names[t];
76 return "?";
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);
86 if(id_string) {
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)
93 i64 numhdrobj;
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)
103 i64 pos = hp->dpos;
104 i64 create_date;
105 i64 x;
106 unsigned int flags;
107 u8 guid_raw[16];
108 char guid_string[50];
109 char buf[64];
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);
118 pos += 16;
120 if(!(flags&0x1)) {
121 x = de_geti64le(pos);
122 de_dbg(c, "file size: %"I64_FMT, x);
124 pos += 8;
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)));
129 pos += 8;
131 if(!(flags&0x1)) {
132 x = de_geti64le(pos);
133 de_dbg(c, "data packets count: %"I64_FMT, x);
135 pos += 8;
137 if(!(flags&0x1)) {
138 x = de_geti64le(pos);
139 de_dbg(c, "play duration: %"I64_FMT" (%s)", x, format_duration(x, buf, sizeof(buf)));
141 pos += 8;
143 if(!(flags&0x1)) {
144 x = de_geti64le(pos);
145 de_dbg(c, "send duration: %"I64_FMT" (%s)", x, format_duration(x, buf, sizeof(buf)));
147 pos += 8;
149 x = de_geti64le(pos);
150 de_dbg(c, "preroll: %"I64_FMT, x);
151 pos += 8;
153 // Already read, above.
154 de_dbg(c, "flags: 0x%08x", flags);
155 pos += 4;
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)
169 i64 pos = hp->dpos;
170 i64 x;
171 i64 tsdlen, ecdlen;
172 unsigned int flags;
173 u8 stream_type[16];
174 u8 ec_type[16];
175 char stream_type_string[50];
176 char ec_type_string[50];
177 char buf[64];
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));
184 pos += 16;
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));
188 pos += 16;
190 x = de_geti64le(pos);
191 de_dbg(c, "time offset: %"I64_FMT" (%s)", x, format_duration(x, buf, sizeof(buf)));
192 pos += 8;
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);
199 de_dbg_indent(c, 1);
200 de_dbg(c, "stream number: %u", (unsigned int)(flags&0x7f));
201 de_dbg_indent(c, -1);
203 pos += 4; // reserved
205 if(tsdlen) {
206 de_dbg(c, "[%d bytes of type-specific data at %"I64_FMT"]", (int)tsdlen, pos);
207 de_dbg_indent(c, 1);
208 de_dbg_hexdump(c, c->infile, pos, tsdlen, 256, NULL, 0x1);
209 de_dbg_indent(c, -1);
210 pos += tsdlen;
212 if(ecdlen) {
213 de_dbg(c, "[%d bytes of error correction data at %"I64_FMT"]", (int)ecdlen, pos);
214 de_dbg_indent(c, 1);
215 de_dbg_hexdump(c, c->infile, pos, ecdlen, 256, NULL, 0x1);
216 de_dbg_indent(c, -1);
217 pos += ecdlen;
221 static void handler_HeaderExtension(deark *c, lctx *d, struct handler_params *hp)
223 i64 datasize;
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)
234 i64 lengths[5];
235 const char *names[5] = { "title", "author", "copyright",
236 "description", "rating" };
237 de_ucstring *s = NULL;
238 i64 pos = hp->dpos;
239 size_t k;
241 if(hp->dlen<10) return;
243 for(k=0; k<5; k++) {
244 lengths[k] = de_getu16le_p(&pos);
247 s = ucstring_create(c);
248 for(k=0; k<5; k++) {
249 if(pos+lengths[k] > hp->dpos+hp->dlen) break;
250 ucstring_empty(s);
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));
255 pos += lengths[k];
258 ucstring_destroy(s);
261 static void handler_ContentEncr(deark *c, lctx *d, struct handler_params *hp)
263 i64 pos = hp->dpos;
264 i64 xlen;
265 de_ucstring *s = NULL;
267 xlen = de_getu32le_p(&pos);
268 if(pos+xlen > hp->dpos+hp->dlen) goto done;
269 if(xlen>0) {
270 de_dbg(c, "[%d bytes of secret data at %"I64_FMT"]", (int)xlen, pos);
271 de_dbg_indent(c, 1);
272 de_dbg_hexdump(c, c->infile, pos, xlen, 256, NULL, 0x0);
273 de_dbg_indent(c, -1);
275 pos += xlen;
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"?
285 pos += xlen;
287 ucstring_empty(s);
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));
294 pos += xlen;
296 ucstring_empty(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));
303 pos += xlen;
305 done:
306 ucstring_destroy(s);
309 // Extended Stream Properties
310 static void handler_ESP(deark *c, lctx *d, struct handler_params *hp)
312 i64 pos = hp->dpos;
313 i64 name_count, pes_count;
314 i64 k;
315 i64 x, xlen;
316 i64 bytes_remaining;
317 int saved_indent_level;
318 u8 guid_raw[16];
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);
326 pos += 8;
327 x = de_geti64le(pos);
328 de_dbg(c, "end time: %"I64_FMT, x);
329 pos += 8;
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);
354 pos += 8;
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);
367 pos += xlen;
370 // Payload extension systems
371 for(k=0; k<pes_count; k++) {
372 if(pos+22 > hp->dpos+hp->dlen) {
373 goto done;
375 de_dbg(c, "payload ext. system[%d] at %"I64_FMT, (int)k, pos);
376 de_dbg_indent(c, 1);
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));
380 pos += 16;
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) {
389 goto done;
391 if(xlen>0) {
392 de_dbg(c, "[%d bytes of payload ext. system info at %"I64_FMT, (int)xlen, pos);
393 de_dbg_indent(c, 1);
394 de_dbg_hexdump(c, c->infile, pos, xlen, 256, NULL, 0x1);
395 de_dbg_indent(c, -1);
397 pos += xlen;
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);
408 done:
409 de_dbg_indent_restore(c, saved_indent_level);
412 static void handler_LanguageList(deark *c, lctx *d, struct handler_params *hp)
414 i64 pos = hp->dpos;
415 i64 nlangs;
416 i64 k;
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++) {
427 i64 id_len;
429 if(pos+1 > hp->dpos+hp->dlen) goto done;
430 de_dbg(c, "language id record[%d] at %"I64_FMT, (int)k, pos);
431 de_dbg_indent(c, 1);
433 id_len = (i64)de_getbyte_p(&pos);
435 ucstring_empty(s);
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));
440 pos += id_len;
442 de_dbg_indent(c, -1);
445 done:
446 ucstring_destroy(s);
449 static const char *get_codec_type_name(unsigned int t)
451 const char *name = "?";
452 switch(t) {
453 case 0x0001: name="video"; break;
454 case 0x0002: name="audio"; break;
455 case 0xffff: name="unknown"; break;
457 return name;
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;
464 unsigned int type;
465 i64 namelen, descrlen, infolen;
466 i64 pos = pos1;
467 int retval = 0;
468 int saved_indent_level;
470 *bytes_consumed = 0;
471 de_dbg_indent_save(c, &saved_indent_level);
472 de_dbg(c, "codec entry at %"I64_FMT, pos1);
473 de_dbg_indent(c, 1);
475 if(len<8) goto done;
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));
485 pos += namelen*2;
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));
493 pos += descrlen*2;
495 infolen = de_getu16le_p(&pos);
496 if(infolen>0) {
497 de_dbg(c, "[%d bytes of codec information at %"I64_FMT"]", (int)infolen, pos);
498 de_dbg_indent(c, 1);
499 de_dbg_hexdump(c, c->infile, pos, infolen, 256, NULL, 0x1);
500 de_dbg_indent(c, -1);
502 pos += infolen;
504 *bytes_consumed = pos-pos1;
505 retval = 1;
506 done:
507 de_dbg_indent_restore(c, saved_indent_level);
508 ucstring_destroy(name);
509 ucstring_destroy(descr);
510 return retval;
513 static void handler_CodecList(deark *c, lctx *d, struct handler_params *hp)
515 i64 numentries;
516 i64 k;
517 i64 pos;
519 if(hp->dlen<20) return;
520 numentries = de_getu32le(hp->dpos+16);
521 de_dbg(c, "number of codec entries: %d", (int)numentries);
523 pos = hp->dpos+20;
524 for(k=0; k<numentries; k++) {
525 i64 bytes_consumed = 0;
526 int ret;
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;
538 i64 pos = hp->dpos;
539 i64 k;
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++) {
556 i64 type_name_len;
558 if(pos+2 > hp->dpos+hp->dlen) goto done;
559 de_dbg(c, "command type[%d] at %"I64_FMT, (int)k, pos);
560 de_dbg_indent(c, 1);
562 type_name_len = de_getu16le_p(&pos);
564 ucstring_empty(s);
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++) {
575 i64 cmd_name_len;
576 i64 n;
578 if(pos+8 > hp->dpos+hp->dlen) goto done;
579 de_dbg(c, "command[%d] at %"I64_FMT, (int)k, pos);
580 de_dbg_indent(c, 1);
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);
590 ucstring_empty(s);
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);
600 done:
601 de_dbg_indent_restore(c, saved_indent_level);
602 ucstring_destroy(s);
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);
608 de_dbg_indent(c, 1);
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);
616 de_dbg_indent(c, 1);
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)
623 char buf[64];
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,
630 u32 object_sid)
632 de_ucstring *val_str = NULL;
633 i64 val_int;
634 int handled = 0;
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
644 else {
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));
654 handled = 1;
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);
659 handled = 1;
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);
666 else {
667 de_dbg(c, "value: %"I64_FMT, val_int);
669 handled = 1;
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);
674 handled = 1;
676 else if(val_data_type==6 && val_len>=16) { // GUID
677 u8 guid_raw[16];
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);
682 handled = 1;
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);
687 handled = 1;
689 else if(!de_strcmp(name_srd->sz_utf8, "WM/Picture")) {
690 do_ECD_WMPicture(c, d, pos, val_len);
691 handled = 1;
695 if(!handled) {
696 de_dbg_indent(c, 1);
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)
706 i64 pos = pos1;
707 struct de_stringreaderdata *name_srd = NULL;
708 i64 namelen;
709 i64 namelen_to_keep;
710 unsigned int val_data_type;
711 i64 val_len;
712 int retval = 0;
713 int saved_indent_level;
715 *bytes_consumed = 0;
716 de_dbg_indent_save(c, &saved_indent_level);
717 de_dbg(c, "ECD object at %"I64_FMT, pos1);
718 de_dbg_indent(c, 1);
720 if(len<6) goto done;
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));
728 pos += namelen;
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);
738 pos += val_len;
740 *bytes_consumed = pos-pos1;
741 retval = 1;
742 done:
743 de_dbg_indent_restore(c, saved_indent_level);
744 de_destroy_stringreaderdata(c, name_srd);
745 return retval;
748 // Supports:
749 // Metadata
750 // Metadata Library
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)
755 i64 pos = pos1;
756 struct de_stringreaderdata *name_srd = NULL;
757 i64 namelen;
758 i64 namelen_to_keep;
759 unsigned int val_data_type;
760 i64 val_len;
761 i64 stream_number;
762 int retval = 0;
763 int saved_indent_level;
765 *bytes_consumed = 0;
766 de_dbg_indent_save(c, &saved_indent_level);
767 de_dbg(c, "metadata object at %"I64_FMT, pos1);
768 de_dbg_indent(c, 1);
770 if(len<14) goto done;
772 if(hp->uui->short_id==SID_METADATALIB) {
773 i64 lang_list_idx;
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));
796 pos += namelen;
798 do_metadata_item(c, d, pos, val_len, val_data_type, name_srd, hp->uui->short_id);
800 pos += val_len;
802 *bytes_consumed = pos-pos1;
803 retval = 1;
804 done:
805 de_dbg_indent_restore(c, saved_indent_level);
806 de_destroy_stringreaderdata(c, name_srd);
807 return retval;
810 // Supports:
811 // Extended Content Description
812 // Metadata
813 // Metadata Library
814 static void handler_ECD_or_metadata(deark *c, lctx *d, struct handler_params *hp)
816 i64 descr_count;
817 i64 k;
818 i64 pos = hp->dpos;
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;
825 int ret;
827 if(pos >= hp->dpos + hp->dlen) {
828 break;
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);
836 else {
837 break;
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)
911 size_t k;
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];
917 return NULL;
920 static const struct uuid_info *find_uuid_info(const u8 *uuid)
922 size_t k;
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];
928 return NULL;
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;
936 return "?";
939 static int do_object(deark *c, lctx *d, i64 pos1, i64 len,
940 int level, i64 *pbytes_consumed)
942 u8 id[16];
943 char id_string[50];
944 const char *id_name = NULL;
945 int retval = 0;
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);
954 de_dbg_indent(c, 1);
956 hp = de_malloc(c, sizeof(struct handler_params));
957 hp->objpos = pos1;
958 hp->level = level;
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",
979 pos1, hp->objlen);
980 goto done;
983 if(hp->uui && hp->uui->hfn) {
984 hp->uui->hfn(c, d, hp);
987 *pbytes_consumed = hp->objlen;
988 retval = 1;
989 done:
990 de_dbg_indent_restore(c, saved_indent_level);
991 de_free(c, hp);
992 return retval;
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)
998 int retval = 0;
999 i64 pos = pos1;
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
1007 goto done;
1010 while(1) {
1011 int ret;
1012 i64 bytes_consumed = 0;
1014 bytes_remaining = pos1+len-pos;
1015 if(known_object_count && objects_found>=num_objects_expected) {
1016 break;
1019 if(bytes_remaining<24) {
1020 break;
1023 ret = do_object(c, d, pos, bytes_remaining, level, &bytes_consumed);
1024 if(!ret) goto done;
1025 if(bytes_consumed<24) goto done;
1027 objects_found++;
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);
1041 done:
1042 de_dbg_indent_restore(c, saved_indent_level);
1043 return retval;
1046 static void de_run_asf(deark *c, de_module_params *mparams)
1048 lctx *d = NULL;
1050 d = de_malloc(c, sizeof(lctx));
1052 do_object_sequence(c, d, 0, c->infile->len, 0, 0, 0);
1054 de_free(c, d);
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))
1062 return 100;
1064 return 0;
1067 void de_module_asf(deark *c, struct deark_module_info *mi)
1069 mi->id = "asf";
1070 mi->desc = "ASF, WMV, WMA";
1071 mi->run_fn = de_run_asf;
1072 mi->identify_fn = de_identify_asf;