New "videomaster" module
[deark.git] / modules / riff.c
blob837739390cfd0ee0a8cac04cf8edeb8f41af229c
1 // This file is part of Deark.
2 // Copyright (C) 2016 Jason Summers
3 // See the file COPYING for terms of use.
5 // Generic RIFF format
6 // Windows animated cursor format
8 #include <deark-config.h>
9 #include <deark-private.h>
10 #include <deark-fmtutil.h>
11 DE_DECLARE_MODULE(de_module_riff);
13 #define CODE_ACON 0x41434f4eU
14 #define CODE_AVI 0x41564920U
15 #define CODE_CDRX 0x43445258U
16 #define CODE_CMX1 0x434d5831U
17 #define CODE_INFO 0x494e464fU
18 #define CODE_PAL 0x50414c20U
19 #define CODE_RDIB 0x52444942U
20 #define CODE_RMID 0x524d4944U
21 #define CODE_WAVE 0x57415645U
22 #define CODE_WEBP 0x57454250U
23 #define CODE_auds 0x61756473U
24 #define CODE_bmpt 0x626d7074U
25 #define CODE_cmov 0x636d6f76U
26 #define CODE_cmpr 0x636d7072U
27 #define CODE_movi 0x6d6f7669U
28 #define CODE_vids 0x76696473U
30 #define CHUNK_DISP 0x44495350U
31 #define CHUNK_EXIF 0x45584946U
32 #define CHUNK_IART 0x49415254U
33 #define CHUNK_ICOP 0x49434f50U
34 #define CHUNK_ICCP 0x49434350U
35 #define CHUNK_ICMT 0x49434d54U
36 #define CHUNK_IKEY 0x494b4559U
37 #define CHUNK_INAM 0x494e414dU
38 #define CHUNK_ISBJ 0x4953424aU
39 #define CHUNK_JUNK 0x4a554e4bU
40 #define CHUNK_LIST 0x4c495354U
41 #define CHUNK_RIFF 0x52494646U
42 #define CHUNK_RIFX 0x52494658U
43 #define CHUNK_XMP 0x584d5020U
44 #define CHUNK__PMX 0x5f504d58U
45 #define CHUNK_avih 0x61766968U
46 #define CHUNK_bmhd 0x626d6864U
47 #define CHUNK_bmp 0x626d7020U
48 #define CHUNK_data 0x64617461U
49 #define CHUNK_fact 0x66616374U
50 #define CHUNK_fmt 0x666d7420U
51 #define CHUNK_icon 0x69636f6eU
52 #define CHUNK_strf 0x73747266U
53 #define CHUNK_strh 0x73747268U
55 typedef struct localctx_struct {
56 UI top_level_chunk_count;
57 int is_cdr;
58 u32 curr_avi_stream_type;
59 u8 cmx_parse_hack;
60 u8 cmv_parse_hack;
61 u8 in_movi;
62 int in_movi_level;
63 de_ucstring *INAM_data;
64 } lctx;
66 static void do_extract_raw(deark *c, lctx *d, struct de_iffctx *ictx, i64 pos, i64 len, const char *ext,
67 de_finfo *fi, unsigned int createflags)
69 dbuf_create_file_from_slice(ictx->f, pos, len, ext, fi, createflags);
72 static void do_INFO_item(deark *c, lctx *d, struct de_iffctx *ictx, i64 pos, i64 len, u32 chunk_id)
74 de_ucstring *s = NULL;
76 s = ucstring_create(c);
78 // TODO: Decode the chunk_id (e.g. ICRD = Creation date).
80 // TODO: Support the CSET chunk
81 dbuf_read_to_ucstring_n(ictx->f, pos, len, 500, s,
82 DE_CONVFLAG_STOP_AT_NUL, ictx->input_encoding);
83 de_dbg(c, "value: \"%s\"", ucstring_getpsz_d(s));
85 if(chunk_id==CHUNK_INAM) { // Save for possible later use
86 if(d->INAM_data) {
87 ucstring_empty(d->INAM_data);
89 else {
90 d->INAM_data = ucstring_create(c);
92 if(s->len>64) {
93 ucstring_truncate(s, 64);
95 ucstring_append_ucstring(d->INAM_data, s);
98 ucstring_destroy(s);
101 static void extract_ani_frame(deark *c, lctx *d, struct de_iffctx *ictx, i64 pos, i64 len)
103 u8 buf[4];
104 const char *ext;
106 de_dbg(c, "frame at %d, len=%d", (int)pos, (int)len);
108 de_read(buf, pos, 4);
110 // Try to identify the format of this frame.
111 if(!de_memcmp(buf, "\x00\x00\x01\x00", 4)) {
112 ext = "ico";
114 else if(!de_memcmp(buf, "\x00\x00\x02\x00", 4)) {
115 ext = "cur";
117 else {
118 ext = "bin";
121 dbuf_create_file_from_slice(ictx->f, pos, len, ext, NULL, 0);
124 static const char *get_wav_fmt_name(unsigned int n)
126 const char *name = NULL;
127 switch(n) {
128 case 0x0001: name="PCM"; break;
129 case 0x0002: name="ADPCM"; break;
130 case 0x0050: name="MPEG"; break;
131 case 0x0055: name="MPEGLAYER3"; break;
132 case 0xFFFE: name="EXTENSIBLE"; break;
133 // TODO: There are lots more formats.
136 return name?name:"?";
139 static void decode_WAVEFORMATEX(deark *c, lctx *d, struct de_iffctx *ictx, i64 pos1, i64 len)
141 unsigned int formattag;
142 i64 n;
143 i64 pos = pos1;
145 if(!ictx->is_le) goto done;
146 if(len<14) goto done;
148 formattag = (unsigned int)dbuf_getu16le_p(ictx->f, &pos);
149 de_dbg(c, "FormatTag: 0x%04x (%s)", formattag, get_wav_fmt_name(formattag));
150 n = dbuf_getu16le_p(ictx->f, &pos);
151 de_dbg(c, "Channels: %u", (unsigned int)n);
152 n = dbuf_getu32le_p(ictx->f, &pos);
153 de_dbg(c, "SamplesPerSec: %u", (unsigned int)n);
154 n = dbuf_getu32le_p(ictx->f, &pos);
155 de_dbg(c, "AvgBytesPerSec: %u", (unsigned int)n);
156 n = dbuf_getu16le_p(ictx->f, &pos);
157 de_dbg(c, "BlockAlign: %u", (unsigned int)n);
158 if(len<16) goto done;
159 n = dbuf_getu16le_p(ictx->f, &pos);
160 de_dbg(c, "BitsPerSample: %u", (unsigned int)n);
161 if(len<18) goto done;
162 n = dbuf_getu16le_p(ictx->f, &pos);
163 de_dbg(c, "cbSize: %u", (unsigned int)n);
165 done:
169 static void do_wav_fmt(deark *c, lctx *d, struct de_iffctx *ictx, i64 pos, i64 len)
171 decode_WAVEFORMATEX(c, d, ictx, pos, len);
174 static void do_wav_fact(deark *c, lctx *d, struct de_iffctx *ictx, i64 pos, i64 len)
176 i64 n;
178 if(!ictx->is_le) return;
179 if(len<4) return;
180 n = de_getu32le(pos);
181 de_dbg(c, "number of samples: %u", (unsigned int)n);
184 static void do_avi_avih(deark *c, lctx *d, struct de_iffctx *ictx, i64 pos, i64 len)
186 i64 n, n2;
188 if(len<40) return;
189 n = de_getu32le(pos);
190 de_dbg(c, "microseconds/frame: %u", (unsigned int)n);
191 n = de_getu32le(pos+12);
192 de_dbg(c, "flags: 0x%08x", (unsigned int)n);
193 n = de_getu32le(pos+16);
194 de_dbg(c, "number of frames: %u", (unsigned int)n);
195 n = de_getu32le(pos+24);
196 de_dbg(c, "number of streams: %u", (unsigned int)n);
197 n = de_getu32le(pos+32);
198 n2 = de_getu32le(pos+36);
199 de_dbg_dimensions(c, n, n2);
200 // TODO: There are more fields in this chunk.
203 static void do_avi_strh(deark *c, lctx *d, struct de_iffctx *ictx, i64 pos, i64 len)
205 struct de_fourcc type4cc;
206 struct de_fourcc codec4cc;
208 if(len<8) return;
210 dbuf_read_fourcc(ictx->f, pos, &type4cc, 4, 0x0);
211 de_dbg(c, "stream type: '%s'", type4cc.id_dbgstr);
212 // Hack. TODO: Need a better way to track state.
213 d->curr_avi_stream_type = type4cc.id;
215 dbuf_read_fourcc(ictx->f, pos+4, &codec4cc, 4, 0x0);
216 de_dbg(c, "codec: '%s'", codec4cc.id_dbgstr);
218 // TODO: There are more fields here.
221 static void do_avi_strf(deark *c, lctx *d, struct de_iffctx *ictx, i64 pos, i64 len)
223 if(d->curr_avi_stream_type==CODE_vids) {
224 struct de_bmpinfo bi;
225 // For video streams, this is a BITMAPINFO.
226 fmtutil_get_bmpinfo(c, ictx->f, &bi, pos, len, DE_BMPINFO_CMPR_IS_4CC);
227 // This chunk contains just a bitmap header, so we can't extract a bitmap.
229 else if(d->curr_avi_stream_type==CODE_auds) {
230 // For audio streams, this is a WAVEFORMATEX.
231 decode_WAVEFORMATEX(c, d, ictx, pos, len);
235 static void do_cdr_bmp(deark *c, lctx *d, struct de_iffctx *ictx, i64 pos, i64 len)
237 if(len<20) return;
238 // The first 2 bytes are an index, or something. BMP starts at offset 2.
239 dbuf_create_file_from_slice(ictx->f, pos+2, len-2, "bmp", NULL, 0);
242 static void do_palette(deark *c, lctx *d, struct de_iffctx *ictx, i64 pos, i64 len)
244 i64 ver;
245 i64 n;
246 i64 i;
247 u8 r,g,b,flags;
248 u32 clr;
249 char tmps[32];
251 if(!ictx->is_le) return;
252 ver = de_getu16le(pos);
253 de_dbg(c, "version: 0x%04x", (unsigned int)ver);
254 pos += 2;
255 n = de_getu16le(pos);
256 de_dbg(c, "number of entries: %d", (int)n);
257 pos += 2;
258 if(n>(len-4)/4) n=(len-4)/4;
259 if(n>1024) n=1024;
260 if(n<1) return;
262 de_dbg(c, "palette entries at %d", (int)pos);
263 de_dbg_indent(c, 1);
264 for(i=0; i<n; i++) {
265 r = de_getbyte(pos);
266 g = de_getbyte(pos+1);
267 b = de_getbyte(pos+2);
268 flags = de_getbyte(pos+3);
269 pos += 4;
270 clr = DE_MAKE_RGB(r, g, b);
271 de_snprintf(tmps, sizeof(tmps), " flags=0x%02x", (unsigned int)flags);
272 de_dbg_pal_entry2(c, i, clr, NULL, NULL, tmps);
274 de_dbg_indent(c, -1);
277 static void do_DISP_DIB(deark *c, lctx *d, struct de_iffctx *ictx, i64 pos, i64 len)
279 de_module_params *mparams = NULL;
281 if(len<12) return;
283 mparams = de_malloc(c, sizeof(de_module_params));
284 mparams->in_params.codes = "X"; // "auxiliary"
285 mparams->in_params.flags = 0x80; // ".preview.bmp"
286 de_run_module_by_id_on_slice(c, "dib", mparams, ictx->f, pos, len);
287 de_free(c, mparams);
290 static void do_DISP_TEXT(deark *c, lctx *d, struct de_iffctx *ictx, i64 pos, i64 len1)
292 i64 foundpos;
293 i64 len = len1;
295 // Stop at NUL
296 if(dbuf_search_byte(ictx->f, 0x00, pos, len1, &foundpos)) {
297 len = foundpos - pos;
299 de_dbg(c, "text length: %d", (int)len);
300 if(len<1) return;
302 do_extract_raw(c, d, ictx, pos, len, "disp.txt", NULL, DE_CREATEFLAG_IS_AUX);
305 static void do_ICCP(deark *c, lctx *d, struct de_iffctx *ictx, i64 pos, i64 len)
307 dbuf_create_file_from_slice(ictx->f, pos, len, "icc", NULL, DE_CREATEFLAG_IS_AUX);
310 static void do_EXIF(deark *c, lctx *d, struct de_iffctx *ictx, i64 pos, i64 len)
312 fmtutil_handle_exif(c, pos, len);
315 static void do_XMP(deark *c, lctx *d, struct de_iffctx *ictx, i64 pos, i64 len)
317 dbuf_create_file_from_slice(ictx->f, pos, len, "xmp", NULL, DE_CREATEFLAG_IS_AUX);
320 static void do_RDIB_data(deark *c, lctx *d, struct de_iffctx *ictx, i64 pos, i64 len)
322 de_finfo *fi = NULL;
324 if(!ictx->chunkctx->parent) goto done;
325 if(ictx->chunkctx->parent->user_flags & 0x1) goto done; // Extraction suppressed, or already done
326 ictx->chunkctx->parent->user_flags |= 0x1;
328 fi = de_finfo_create(c);
329 if(ucstring_isnonempty(d->INAM_data)) {
330 // In CorelMOVE format, at least, the INAM chunk seems to have a usable
331 // name for the RDIB bitmap.
332 de_finfo_set_name_from_ucstring(c, fi, d->INAM_data, 0);
333 ucstring_empty(d->INAM_data);
335 do_extract_raw(c, d, ictx, pos, len, "bmp", fi, 0);
336 done:
337 de_finfo_destroy(c, fi);
340 static void do_RDIB_bmhd(deark *c, lctx *d, struct de_iffctx *ictx)
342 if(!ictx->chunkctx->parent) return;
343 // AFAICT, a 'bmhd' chunk means we're dealing with "extended RDIB", which we
344 // don't support. There may still be a 'data' chunk after this, but it will presumably
345 // be in a format we can't handle. Set a flag to remember that.
346 ictx->chunkctx->parent->user_flags |= 0x1;
349 static void do_DISP(deark *c, lctx *d, struct de_iffctx *ictx, i64 pos, i64 len)
351 unsigned int ty;
352 i64 dpos, dlen;
354 if(!ictx->is_le) return;
355 if(len<4) return;
356 ty = (unsigned int)de_getu32le(pos);
357 de_dbg(c, "data type: %u (%s)", ty,
358 fmtutil_get_windows_cb_data_type_name(ty));
360 dpos = pos+4;
361 dlen = len-4;
362 switch(ty) {
363 case 1:
364 case 7:
365 do_DISP_TEXT(c, d, ictx, dpos, dlen);
366 break;
367 case 8:
368 case 17:
369 do_DISP_DIB(c, d, ictx, dpos, dlen);
370 break;
374 static int is_fourcc_at(deark *c, struct de_iffctx *ictx, i64 pos)
376 u8 b[4];
377 size_t i;
379 dbuf_read(ictx->f, b, pos, 4);
380 for(i=0; i<4; i++) {
381 if(b[i]<32 || b[i]>126) return 0;
383 return 1;
386 static int do_cmx_parse_hack(deark *c, lctx *d, struct de_iffctx *ictx, i64 pos, i64 *plen)
388 i64 n, n_padded;
390 // Some CMX chunks seem to be followed by a non-RIFF segment starting with either
391 // 04 00 (4 bytes) or 10 00 (16 bytes). I'm just guessing how to parse them.
392 n = dbuf_getu16le(ictx->f, pos);
393 if(n>256 || n==0) return 0;
395 n_padded = de_pad_to_2(n);
396 if(is_fourcc_at(c, ictx, pos + n_padded)) {
397 de_dbg(c, "[%d non-RIFF bytes at %"I64_FMT"]", (int)n_padded, pos);
398 *plen = n_padded;
399 return 1;
401 return 0;
404 // CMV files seem to consist of two RIFF chunks, separated by four 0x00 bytes.
405 // (Maybe some sort of scan-for-the-next-RIFF-chunk logic should happen by
406 // default, but it's hard to be sure we won't break something.)
407 static int do_cmv_parse_hack(deark *c, lctx *d, struct de_iffctx *ictx, i64 pos, i64 *plen)
409 if(ictx->level!=0) return 0;
410 if(dbuf_getu32be(ictx->f, pos)!=0) return 0;
411 if(dbuf_getu32be(ictx->f, pos+4)!=CHUNK_RIFF) return 0;
412 de_dbg(c, "[%d non-RIFF bytes at %"I64_FMT"]", 4, pos);
413 *plen = 4;
414 return 1;
417 static int my_handle_nonchunk_riff_data_fn(struct de_iffctx *ictx,
418 i64 pos, i64 *plen)
420 deark *c = ictx->c;
421 lctx *d = (lctx*)ictx->userdata;
423 if(d->cmx_parse_hack) {
424 return do_cmx_parse_hack(c, d, ictx, pos, plen);
426 else if(d->cmv_parse_hack) {
427 return do_cmv_parse_hack(c, d, ictx, pos, plen);
429 return 0;
432 static int my_on_std_container_start_fn(struct de_iffctx *ictx)
434 deark *c = ictx->c;
435 lctx *d = (lctx*)ictx->userdata;
436 u32 chunktype = ictx->curr_container_fmt4cc.id;
437 u32 formtype = ictx->curr_container_contentstype4cc.id;
438 int suppress_decoding = 0;
440 if(ictx->level==0 && (chunktype==CHUNK_RIFF || chunktype==CHUNK_RIFX) &&
441 d->top_level_chunk_count==0)
443 const char *fmtname = NULL;
445 switch(formtype) {
446 case CODE_ACON: fmtname = "Windows animated cursor"; break;
447 case CODE_AVI: fmtname = "AVI"; break;
448 case CODE_CDRX: fmtname = "Corel CCX"; break;
449 case CODE_CMX1:
450 fmtname = "Corel CMX";
451 ictx->handle_nonchunk_data_fn = my_handle_nonchunk_riff_data_fn;
452 d->cmx_parse_hack = 1;
453 break;
454 case CODE_cmov:
455 fmtname = "CorelMOVE";
456 ictx->handle_nonchunk_data_fn = my_handle_nonchunk_riff_data_fn;
457 d->cmv_parse_hack = 1;
458 break;
459 case CODE_WAVE: fmtname = "WAVE"; break;
460 case CODE_WEBP: fmtname = "WebP"; break;
463 // Special check for CorelDraw formats.
464 if(!fmtname && (formtype>>8 == 0x434452U) /* "CDR" */) {
465 d->is_cdr = 1;
466 fmtname = "CorelDRAW (RIFF-based)";
469 if(fmtname) {
470 de_declare_fmt(c, fmtname);
474 if(d->is_cdr && chunktype==CHUNK_LIST) {
475 // 'cmpr' LISTs in CorelDraw files are not correctly formed.
476 // Tell the parser not to process them.
477 if(formtype==CODE_cmpr) {
478 de_dbg(c, "[not decoding CDR cmpr list]");
479 suppress_decoding = 1;
480 goto done;
484 if(chunktype==CHUNK_RIFF || chunktype==CHUNK_RIFX) {
485 // TODO: INAM data is probably not scoped entirely correctly.
486 if(d->INAM_data) {
487 ucstring_empty(d->INAM_data);
491 if(ictx->main_contentstype4cc.id==CODE_AVI && chunktype==CHUNK_LIST &&
492 formtype==CODE_movi)
494 // There are often a huge number of these chunks, and we can't do
495 // anything interesting with them, so skip them by default.
496 if(c->debug_level<2) {
497 de_dbg(c, "[not decoding movi chunks]");
498 suppress_decoding = 1;
499 goto done;
502 if(!d->in_movi) {
503 // Keep track of when we are inside a 'movi' container.
504 d->in_movi = 1;
505 d->in_movi_level = ictx->level;
509 done:
510 if(ictx->level==0) {
511 d->top_level_chunk_count++;
513 return !suppress_decoding;
516 static int my_on_container_end_fn(struct de_iffctx *ictx)
518 lctx *d = (lctx*)ictx->userdata;
520 if(ictx->curr_container_contentstype4cc.id==CODE_movi &&
521 d->in_movi && ictx->level==d->in_movi_level)
523 d->in_movi = 0;
526 return 1;
529 static int my_preprocess_riff_chunk_fn(struct de_iffctx *ictx)
531 const char *name = NULL;
533 // TODO: Need a better way to do this.
534 switch(ictx->chunkctx->chunk4cc.id) {
535 case CHUNK_DISP: name="display"; break;
536 case CHUNK_IART: name="artist"; break;
537 case CHUNK_ICOP: name="copyright"; break;
538 case CHUNK_ICMT: name="comments"; break;
539 case CHUNK_IKEY: name="keywords"; break;
540 case CHUNK_ISBJ: name="subject"; break;
541 case CHUNK_JUNK: name="filler"; break;
542 case CHUNK_LIST: name="subchunk container"; break;
545 if(name) {
546 ictx->chunkctx->chunk_name = name;
548 return 1;
551 static int my_riff_chunk_handler(struct de_iffctx *ictx)
553 i64 dpos, dlen;
554 u32 list_type;
555 deark *c = ictx->c;
556 lctx *d = (lctx*)ictx->userdata;
558 list_type = ictx->curr_container_contentstype4cc.id;
559 dpos = ictx->chunkctx->dpos;
560 dlen = ictx->chunkctx->dlen;
562 switch(ictx->chunkctx->chunk4cc.id) {
563 case CHUNK_RIFF:
564 case CHUNK_RIFX:
565 case CHUNK_LIST:
566 ictx->is_std_container = 1;
567 return 1;
570 if(list_type==CODE_INFO) {
571 do_INFO_item(c, d, ictx, dpos, dlen, ictx->chunkctx->chunk4cc.id);
572 ictx->handled = 1;
573 goto done;
576 switch(ictx->chunkctx->chunk4cc.id) {
578 case CHUNK_DISP:
579 do_DISP(c, d, ictx, dpos, dlen);
580 ictx->handled = 1;
581 break;
583 case CHUNK_ICCP: // Used by WebP
584 do_ICCP(c, d, ictx, dpos, dlen);
585 ictx->handled = 1;
586 break;
588 case CHUNK_EXIF: // Used by WebP
589 do_EXIF(c, d, ictx, dpos, dlen);
590 ictx->handled = 1;
591 break;
593 case CHUNK_XMP: // Used by WebP
594 case CHUNK__PMX: // Used by WAVE, AVI
595 do_XMP(c, d, ictx, dpos, dlen);
596 ictx->handled = 1;
597 break;
599 case CHUNK_bmhd:
600 if(list_type==CODE_RDIB) {
601 do_RDIB_bmhd(c, d, ictx);
602 ictx->handled = 1;
604 break;
606 case CHUNK_icon:
607 if(ictx->main_contentstype4cc.id==CODE_ACON) {
608 extract_ani_frame(c, d, ictx, dpos, dlen);
609 ictx->handled = 1;
611 break;
613 case CHUNK_data:
614 if(list_type==CODE_RMID) {
615 do_extract_raw(c, d, ictx, dpos, dlen, "mid", NULL, 0);
616 ictx->handled = 1;
618 else if(list_type==CODE_PAL) {
619 do_palette(c, d, ictx, dpos, dlen);
620 ictx->handled = 1;
622 else if(list_type==CODE_RDIB) {
623 do_RDIB_data(c, d, ictx, dpos, dlen);
624 ictx->handled = 1;
626 break;
628 case CHUNK_fmt:
629 if(ictx->main_contentstype4cc.id==CODE_WAVE) {
630 do_wav_fmt(c, d, ictx, dpos, dlen);
631 ictx->handled = 1;
633 break;
635 case CHUNK_fact:
636 if(ictx->main_contentstype4cc.id==CODE_WAVE) {
637 do_wav_fact(c, d, ictx, dpos, dlen);
638 ictx->handled = 1;
640 break;
642 case CHUNK_avih:
643 if(ictx->main_contentstype4cc.id==CODE_AVI) {
644 do_avi_avih(c, d, ictx, dpos, dlen);
645 ictx->handled = 1;
647 break;
649 case CHUNK_strh:
650 if(ictx->main_contentstype4cc.id==CODE_AVI) {
651 do_avi_strh(c, d, ictx, dpos, dlen);
652 ictx->handled = 1;
654 break;
656 case CHUNK_strf:
657 if(ictx->main_contentstype4cc.id==CODE_AVI) {
658 do_avi_strf(c, d, ictx, dpos, dlen);
659 ictx->handled = 1;
661 break;
663 case CHUNK_bmp:
664 if(d->is_cdr && ictx->curr_container_contentstype4cc.id==CODE_bmpt) {
665 do_cdr_bmp(c, d, ictx, dpos, dlen);
666 ictx->handled = 1;
668 break;
671 done:
672 return 1;
675 static void de_run_riff(deark *c, de_module_params *mparams)
677 lctx *d = NULL;
678 struct de_iffctx *ictx = NULL;
679 u8 buf[4];
681 d = de_malloc(c, sizeof(lctx));
683 ictx = fmtutil_create_iff_decoder(c);
684 ictx->userdata = (void*)d;
685 ictx->preprocess_chunk_fn = my_preprocess_riff_chunk_fn;
686 ictx->handle_chunk_fn = my_riff_chunk_handler;
687 ictx->on_std_container_start_fn = my_on_std_container_start_fn;
688 ictx->on_container_end_fn = my_on_container_end_fn;
689 ictx->input_encoding = de_get_input_encoding(c, NULL, DE_ENCODING_WINDOWS1252);
690 ictx->f = c->infile;
692 de_read(buf, 0, 4);
694 if(!de_memcmp(buf, "RIFF", 4)) {
695 ictx->is_le = 1;
696 ictx->reversed_4cc = 0;
698 else if(!de_memcmp(buf, "RIFX", 4)) {
699 ictx->is_le = 0;
700 ictx->reversed_4cc = 0;
702 else if(!de_memcmp(buf, "XFIR", 4)) {
703 ictx->is_le = 1;
704 ictx->reversed_4cc = 1;
706 else {
707 de_warn(c, "This is probably not a RIFF file.");
708 ictx->is_le = 1;
709 ictx->reversed_4cc = 0;
712 fmtutil_read_iff_format(ictx, 0, ictx->f->len);
714 fmtutil_destroy_iff_decoder(ictx);
715 if(d) {
716 ucstring_destroy(d->INAM_data);
717 de_free(c, d);
721 static int de_identify_riff(deark *c)
723 u8 buf[4];
724 int has_sig;
725 i64 dlen;
727 de_read(buf, 0, 4);
728 has_sig = (!de_memcmp(buf, "RIFF", 4)) ||
729 (!de_memcmp(buf, "XFIR", 4)) ||
730 (!de_memcmp(buf, "RIFX", 4));
731 if(!has_sig) return 0;
733 dlen = de_getu32le(4);
734 // This check screens out .AMV format, for example.
735 if(dlen==0 && c->infile->len!=8) return 0;
737 return 50;
740 void de_module_riff(deark *c, struct deark_module_info *mi)
742 mi->id = "riff";
743 mi->desc = "RIFF-based formats";
744 mi->run_fn = de_run_riff;
745 mi->identify_fn = de_identify_riff;