bmp: Rewrote the RLE decompressor
[deark.git] / modules / riff.c
blobdea90a0a6c71e88d40549e0d0a47cd47e3fad122
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,
227 DE_BMPINFO_CMPR_IS_4CC | DE_BMPINFO_NOERR);
228 // This chunk contains just a bitmap header, so we can't extract a bitmap.
230 else if(d->curr_avi_stream_type==CODE_auds) {
231 // For audio streams, this is a WAVEFORMATEX.
232 decode_WAVEFORMATEX(c, d, ictx, pos, len);
236 static void do_cdr_bmp(deark *c, lctx *d, struct de_iffctx *ictx, i64 pos, i64 len)
238 if(len<20) return;
239 // The first 2 bytes are an index, or something. BMP starts at offset 2.
240 dbuf_create_file_from_slice(ictx->f, pos+2, len-2, "bmp", NULL, 0);
243 static void do_palette(deark *c, lctx *d, struct de_iffctx *ictx, i64 pos, i64 len)
245 i64 ver;
246 i64 n;
247 i64 i;
248 u8 r,g,b,flags;
249 u32 clr;
250 char tmps[32];
252 if(!ictx->is_le) return;
253 ver = de_getu16le(pos);
254 de_dbg(c, "version: 0x%04x", (unsigned int)ver);
255 pos += 2;
256 n = de_getu16le(pos);
257 de_dbg(c, "number of entries: %d", (int)n);
258 pos += 2;
259 if(n>(len-4)/4) n=(len-4)/4;
260 if(n>1024) n=1024;
261 if(n<1) return;
263 de_dbg(c, "palette entries at %d", (int)pos);
264 de_dbg_indent(c, 1);
265 for(i=0; i<n; i++) {
266 r = de_getbyte(pos);
267 g = de_getbyte(pos+1);
268 b = de_getbyte(pos+2);
269 flags = de_getbyte(pos+3);
270 pos += 4;
271 clr = DE_MAKE_RGB(r, g, b);
272 de_snprintf(tmps, sizeof(tmps), " flags=0x%02x", (unsigned int)flags);
273 de_dbg_pal_entry2(c, i, clr, NULL, NULL, tmps);
275 de_dbg_indent(c, -1);
278 static void do_DISP_DIB(deark *c, lctx *d, struct de_iffctx *ictx, i64 pos, i64 len)
280 de_module_params *mparams = NULL;
282 if(len<12) return;
284 mparams = de_malloc(c, sizeof(de_module_params));
285 mparams->in_params.codes = "X"; // "auxiliary"
286 mparams->in_params.flags = 0x80; // ".preview.bmp"
287 de_run_module_by_id_on_slice(c, "dib", mparams, ictx->f, pos, len);
288 de_free(c, mparams);
291 static void do_DISP_TEXT(deark *c, lctx *d, struct de_iffctx *ictx, i64 pos, i64 len1)
293 i64 foundpos;
294 i64 len = len1;
296 // Stop at NUL
297 if(dbuf_search_byte(ictx->f, 0x00, pos, len1, &foundpos)) {
298 len = foundpos - pos;
300 de_dbg(c, "text length: %d", (int)len);
301 if(len<1) return;
303 do_extract_raw(c, d, ictx, pos, len, "disp.txt", NULL, DE_CREATEFLAG_IS_AUX);
306 static void do_ICCP(deark *c, lctx *d, struct de_iffctx *ictx, i64 pos, i64 len)
308 dbuf_create_file_from_slice(ictx->f, pos, len, "icc", NULL, DE_CREATEFLAG_IS_AUX);
311 static void do_EXIF(deark *c, lctx *d, struct de_iffctx *ictx, i64 pos, i64 len)
313 fmtutil_handle_exif(c, pos, len);
316 static void do_XMP(deark *c, lctx *d, struct de_iffctx *ictx, i64 pos, i64 len)
318 dbuf_create_file_from_slice(ictx->f, pos, len, "xmp", NULL, DE_CREATEFLAG_IS_AUX);
321 static void do_RDIB_data(deark *c, lctx *d, struct de_iffctx *ictx, i64 pos, i64 len)
323 de_finfo *fi = NULL;
325 if(!ictx->chunkctx->parent) goto done;
326 if(ictx->chunkctx->parent->user_flags & 0x1) goto done; // Extraction suppressed, or already done
327 ictx->chunkctx->parent->user_flags |= 0x1;
329 fi = de_finfo_create(c);
330 if(ucstring_isnonempty(d->INAM_data)) {
331 // In CorelMOVE format, at least, the INAM chunk seems to have a usable
332 // name for the RDIB bitmap.
333 de_finfo_set_name_from_ucstring(c, fi, d->INAM_data, 0);
334 ucstring_empty(d->INAM_data);
336 do_extract_raw(c, d, ictx, pos, len, "bmp", fi, 0);
337 done:
338 de_finfo_destroy(c, fi);
341 static void do_RDIB_bmhd(deark *c, lctx *d, struct de_iffctx *ictx)
343 if(!ictx->chunkctx->parent) return;
344 // AFAICT, a 'bmhd' chunk means we're dealing with "extended RDIB", which we
345 // don't support. There may still be a 'data' chunk after this, but it will presumably
346 // be in a format we can't handle. Set a flag to remember that.
347 ictx->chunkctx->parent->user_flags |= 0x1;
350 static void do_DISP(deark *c, lctx *d, struct de_iffctx *ictx, i64 pos, i64 len)
352 unsigned int ty;
353 i64 dpos, dlen;
355 if(!ictx->is_le) return;
356 if(len<4) return;
357 ty = (unsigned int)de_getu32le(pos);
358 de_dbg(c, "data type: %u (%s)", ty,
359 fmtutil_get_windows_cb_data_type_name(ty));
361 dpos = pos+4;
362 dlen = len-4;
363 switch(ty) {
364 case 1:
365 case 7:
366 do_DISP_TEXT(c, d, ictx, dpos, dlen);
367 break;
368 case 8:
369 case 17:
370 do_DISP_DIB(c, d, ictx, dpos, dlen);
371 break;
375 static int is_fourcc_at(deark *c, struct de_iffctx *ictx, i64 pos)
377 u8 b[4];
378 size_t i;
380 dbuf_read(ictx->f, b, pos, 4);
381 for(i=0; i<4; i++) {
382 if(b[i]<32 || b[i]>126) return 0;
384 return 1;
387 static int do_cmx_parse_hack(deark *c, lctx *d, struct de_iffctx *ictx, i64 pos, i64 *plen)
389 i64 n, n_padded;
391 // Some CMX chunks seem to be followed by a non-RIFF segment starting with either
392 // 04 00 (4 bytes) or 10 00 (16 bytes). I'm just guessing how to parse them.
393 n = dbuf_getu16le(ictx->f, pos);
394 if(n>256 || n==0) return 0;
396 n_padded = de_pad_to_2(n);
397 if(is_fourcc_at(c, ictx, pos + n_padded)) {
398 de_dbg(c, "[%d non-RIFF bytes at %"I64_FMT"]", (int)n_padded, pos);
399 *plen = n_padded;
400 return 1;
402 return 0;
405 // CMV files seem to consist of two RIFF chunks, separated by four 0x00 bytes.
406 // (Maybe some sort of scan-for-the-next-RIFF-chunk logic should happen by
407 // default, but it's hard to be sure we won't break something.)
408 static int do_cmv_parse_hack(deark *c, lctx *d, struct de_iffctx *ictx, i64 pos, i64 *plen)
410 if(ictx->level!=0) return 0;
411 if(dbuf_getu32be(ictx->f, pos)!=0) return 0;
412 if(dbuf_getu32be(ictx->f, pos+4)!=CHUNK_RIFF) return 0;
413 de_dbg(c, "[%d non-RIFF bytes at %"I64_FMT"]", 4, pos);
414 *plen = 4;
415 return 1;
418 static int my_handle_nonchunk_riff_data_fn(struct de_iffctx *ictx,
419 i64 pos, i64 *plen)
421 deark *c = ictx->c;
422 lctx *d = (lctx*)ictx->userdata;
424 if(d->cmx_parse_hack) {
425 return do_cmx_parse_hack(c, d, ictx, pos, plen);
427 else if(d->cmv_parse_hack) {
428 return do_cmv_parse_hack(c, d, ictx, pos, plen);
430 return 0;
433 static int my_on_std_container_start_fn(struct de_iffctx *ictx)
435 deark *c = ictx->c;
436 lctx *d = (lctx*)ictx->userdata;
437 u32 chunktype = ictx->curr_container_fmt4cc.id;
438 u32 formtype = ictx->curr_container_contentstype4cc.id;
439 int suppress_decoding = 0;
441 if(ictx->level==0 && (chunktype==CHUNK_RIFF || chunktype==CHUNK_RIFX) &&
442 d->top_level_chunk_count==0)
444 const char *fmtname = NULL;
446 switch(formtype) {
447 case CODE_ACON: fmtname = "Windows animated cursor"; break;
448 case CODE_AVI: fmtname = "AVI"; break;
449 case CODE_CDRX: fmtname = "Corel CCX"; break;
450 case CODE_CMX1:
451 fmtname = "Corel CMX";
452 ictx->handle_nonchunk_data_fn = my_handle_nonchunk_riff_data_fn;
453 d->cmx_parse_hack = 1;
454 break;
455 case CODE_cmov:
456 fmtname = "CorelMOVE";
457 ictx->handle_nonchunk_data_fn = my_handle_nonchunk_riff_data_fn;
458 d->cmv_parse_hack = 1;
459 break;
460 case CODE_WAVE: fmtname = "WAVE"; break;
461 case CODE_WEBP: fmtname = "WebP"; break;
464 // Special check for CorelDraw formats.
465 if(!fmtname && (formtype>>8 == 0x434452U) /* "CDR" */) {
466 d->is_cdr = 1;
467 fmtname = "CorelDRAW (RIFF-based)";
470 if(fmtname) {
471 de_declare_fmt(c, fmtname);
475 if(d->is_cdr && chunktype==CHUNK_LIST) {
476 // 'cmpr' LISTs in CorelDraw files are not correctly formed.
477 // Tell the parser not to process them.
478 if(formtype==CODE_cmpr) {
479 de_dbg(c, "[not decoding CDR cmpr list]");
480 suppress_decoding = 1;
481 goto done;
485 if(chunktype==CHUNK_RIFF || chunktype==CHUNK_RIFX) {
486 // TODO: INAM data is probably not scoped entirely correctly.
487 if(d->INAM_data) {
488 ucstring_empty(d->INAM_data);
492 if(ictx->main_contentstype4cc.id==CODE_AVI && chunktype==CHUNK_LIST &&
493 formtype==CODE_movi)
495 // There are often a huge number of these chunks, and we can't do
496 // anything interesting with them, so skip them by default.
497 if(c->debug_level<2) {
498 de_dbg(c, "[not decoding movi chunks]");
499 suppress_decoding = 1;
500 goto done;
503 if(!d->in_movi) {
504 // Keep track of when we are inside a 'movi' container.
505 d->in_movi = 1;
506 d->in_movi_level = ictx->level;
510 done:
511 if(ictx->level==0) {
512 d->top_level_chunk_count++;
514 return !suppress_decoding;
517 static int my_on_container_end_fn(struct de_iffctx *ictx)
519 lctx *d = (lctx*)ictx->userdata;
521 if(ictx->curr_container_contentstype4cc.id==CODE_movi &&
522 d->in_movi && ictx->level==d->in_movi_level)
524 d->in_movi = 0;
527 return 1;
530 static int my_preprocess_riff_chunk_fn(struct de_iffctx *ictx)
532 const char *name = NULL;
534 // TODO: Need a better way to do this.
535 switch(ictx->chunkctx->chunk4cc.id) {
536 case CHUNK_DISP: name="display"; break;
537 case CHUNK_IART: name="artist"; break;
538 case CHUNK_ICOP: name="copyright"; break;
539 case CHUNK_ICMT: name="comments"; break;
540 case CHUNK_IKEY: name="keywords"; break;
541 case CHUNK_ISBJ: name="subject"; break;
542 case CHUNK_JUNK: name="filler"; break;
543 case CHUNK_LIST: name="subchunk container"; break;
546 if(name) {
547 ictx->chunkctx->chunk_name = name;
549 return 1;
552 static int my_riff_chunk_handler(struct de_iffctx *ictx)
554 i64 dpos, dlen;
555 u32 list_type;
556 deark *c = ictx->c;
557 lctx *d = (lctx*)ictx->userdata;
559 list_type = ictx->curr_container_contentstype4cc.id;
560 dpos = ictx->chunkctx->dpos;
561 dlen = ictx->chunkctx->dlen;
563 switch(ictx->chunkctx->chunk4cc.id) {
564 case CHUNK_RIFF:
565 case CHUNK_RIFX:
566 case CHUNK_LIST:
567 ictx->is_std_container = 1;
568 return 1;
571 if(list_type==CODE_INFO) {
572 do_INFO_item(c, d, ictx, dpos, dlen, ictx->chunkctx->chunk4cc.id);
573 ictx->handled = 1;
574 goto done;
577 switch(ictx->chunkctx->chunk4cc.id) {
579 case CHUNK_DISP:
580 do_DISP(c, d, ictx, dpos, dlen);
581 ictx->handled = 1;
582 break;
584 case CHUNK_ICCP: // Used by WebP
585 do_ICCP(c, d, ictx, dpos, dlen);
586 ictx->handled = 1;
587 break;
589 case CHUNK_EXIF: // Used by WebP
590 do_EXIF(c, d, ictx, dpos, dlen);
591 ictx->handled = 1;
592 break;
594 case CHUNK_XMP: // Used by WebP
595 case CHUNK__PMX: // Used by WAVE, AVI
596 do_XMP(c, d, ictx, dpos, dlen);
597 ictx->handled = 1;
598 break;
600 case CHUNK_bmhd:
601 if(list_type==CODE_RDIB) {
602 do_RDIB_bmhd(c, d, ictx);
603 ictx->handled = 1;
605 break;
607 case CHUNK_icon:
608 if(ictx->main_contentstype4cc.id==CODE_ACON) {
609 extract_ani_frame(c, d, ictx, dpos, dlen);
610 ictx->handled = 1;
612 break;
614 case CHUNK_data:
615 if(list_type==CODE_RMID) {
616 do_extract_raw(c, d, ictx, dpos, dlen, "mid", NULL, 0);
617 ictx->handled = 1;
619 else if(list_type==CODE_PAL) {
620 do_palette(c, d, ictx, dpos, dlen);
621 ictx->handled = 1;
623 else if(list_type==CODE_RDIB) {
624 do_RDIB_data(c, d, ictx, dpos, dlen);
625 ictx->handled = 1;
627 break;
629 case CHUNK_fmt:
630 if(ictx->main_contentstype4cc.id==CODE_WAVE) {
631 do_wav_fmt(c, d, ictx, dpos, dlen);
632 ictx->handled = 1;
634 break;
636 case CHUNK_fact:
637 if(ictx->main_contentstype4cc.id==CODE_WAVE) {
638 do_wav_fact(c, d, ictx, dpos, dlen);
639 ictx->handled = 1;
641 break;
643 case CHUNK_avih:
644 if(ictx->main_contentstype4cc.id==CODE_AVI) {
645 do_avi_avih(c, d, ictx, dpos, dlen);
646 ictx->handled = 1;
648 break;
650 case CHUNK_strh:
651 if(ictx->main_contentstype4cc.id==CODE_AVI) {
652 do_avi_strh(c, d, ictx, dpos, dlen);
653 ictx->handled = 1;
655 break;
657 case CHUNK_strf:
658 if(ictx->main_contentstype4cc.id==CODE_AVI) {
659 do_avi_strf(c, d, ictx, dpos, dlen);
660 ictx->handled = 1;
662 break;
664 case CHUNK_bmp:
665 if(d->is_cdr && ictx->curr_container_contentstype4cc.id==CODE_bmpt) {
666 do_cdr_bmp(c, d, ictx, dpos, dlen);
667 ictx->handled = 1;
669 break;
672 done:
673 return 1;
676 static void de_run_riff(deark *c, de_module_params *mparams)
678 lctx *d = NULL;
679 struct de_iffctx *ictx = NULL;
680 u8 buf[4];
682 d = de_malloc(c, sizeof(lctx));
684 ictx = fmtutil_create_iff_decoder(c);
685 ictx->userdata = (void*)d;
686 ictx->preprocess_chunk_fn = my_preprocess_riff_chunk_fn;
687 ictx->handle_chunk_fn = my_riff_chunk_handler;
688 ictx->on_std_container_start_fn = my_on_std_container_start_fn;
689 ictx->on_container_end_fn = my_on_container_end_fn;
690 ictx->input_encoding = de_get_input_encoding(c, NULL, DE_ENCODING_WINDOWS1252);
691 ictx->f = c->infile;
693 de_read(buf, 0, 4);
695 if(!de_memcmp(buf, "RIFF", 4)) {
696 ictx->is_le = 1;
697 ictx->reversed_4cc = 0;
699 else if(!de_memcmp(buf, "RIFX", 4)) {
700 ictx->is_le = 0;
701 ictx->reversed_4cc = 0;
703 else if(!de_memcmp(buf, "XFIR", 4)) {
704 ictx->is_le = 1;
705 ictx->reversed_4cc = 1;
707 else {
708 de_warn(c, "This is probably not a RIFF file.");
709 ictx->is_le = 1;
710 ictx->reversed_4cc = 0;
713 fmtutil_read_iff_format(ictx, 0, ictx->f->len);
715 fmtutil_destroy_iff_decoder(ictx);
716 if(d) {
717 ucstring_destroy(d->INAM_data);
718 de_free(c, d);
722 static int de_identify_riff(deark *c)
724 u8 buf[4];
725 int has_sig;
726 i64 dlen;
728 de_read(buf, 0, 4);
729 has_sig = (!de_memcmp(buf, "RIFF", 4)) ||
730 (!de_memcmp(buf, "XFIR", 4)) ||
731 (!de_memcmp(buf, "RIFX", 4));
732 if(!has_sig) return 0;
734 dlen = de_getu32le(4);
735 // This check screens out .AMV format, for example.
736 if(dlen==0 && c->infile->len!=8) return 0;
738 return 50;
741 void de_module_riff(deark *c, struct deark_module_info *mi)
743 mi->id = "riff";
744 mi->desc = "RIFF-based formats";
745 mi->run_fn = de_run_riff;
746 mi->identify_fn = de_identify_riff;