1 // This file is part of Deark.
2 // Copyright (C) 2016 Jason Summers
3 // See the file COPYING for terms of use.
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
;
58 u32 curr_avi_stream_type
;
63 de_ucstring
*INAM_data
;
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
87 ucstring_empty(d
->INAM_data
);
90 d
->INAM_data
= ucstring_create(c
);
93 ucstring_truncate(s
, 64);
95 ucstring_append_ucstring(d
->INAM_data
, s
);
101 static void extract_ani_frame(deark
*c
, lctx
*d
, struct de_iffctx
*ictx
, i64 pos
, i64 len
)
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)) {
114 else if(!de_memcmp(buf
, "\x00\x00\x02\x00", 4)) {
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
;
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
;
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
);
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
)
178 if(!ictx
->is_le
) 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
)
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
;
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
)
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
)
251 if(!ictx
->is_le
) return;
252 ver
= de_getu16le(pos
);
253 de_dbg(c
, "version: 0x%04x", (unsigned int)ver
);
255 n
= de_getu16le(pos
);
256 de_dbg(c
, "number of entries: %d", (int)n
);
258 if(n
>(len
-4)/4) n
=(len
-4)/4;
262 de_dbg(c
, "palette entries at %d", (int)pos
);
266 g
= de_getbyte(pos
+1);
267 b
= de_getbyte(pos
+2);
268 flags
= de_getbyte(pos
+3);
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
;
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
);
290 static void do_DISP_TEXT(deark
*c
, lctx
*d
, struct de_iffctx
*ictx
, i64 pos
, i64 len1
)
296 if(dbuf_search_byte(ictx
->f
, 0x00, pos
, len1
, &foundpos
)) {
297 len
= foundpos
- pos
;
299 de_dbg(c
, "text length: %d", (int)len
);
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
)
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);
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
)
354 if(!ictx
->is_le
) 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
));
365 do_DISP_TEXT(c
, d
, ictx
, dpos
, dlen
);
369 do_DISP_DIB(c
, d
, ictx
, dpos
, dlen
);
374 static int is_fourcc_at(deark
*c
, struct de_iffctx
*ictx
, i64 pos
)
379 dbuf_read(ictx
->f
, b
, pos
, 4);
381 if(b
[i
]<32 || b
[i
]>126) return 0;
386 static int do_cmx_parse_hack(deark
*c
, lctx
*d
, struct de_iffctx
*ictx
, i64 pos
, i64
*plen
)
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
);
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
);
417 static int my_handle_nonchunk_riff_data_fn(struct de_iffctx
*ictx
,
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
);
432 static int my_on_std_container_start_fn(struct de_iffctx
*ictx
)
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
;
446 case CODE_ACON
: fmtname
= "Windows animated cursor"; break;
447 case CODE_AVI
: fmtname
= "AVI"; break;
448 case CODE_CDRX
: fmtname
= "Corel CCX"; break;
450 fmtname
= "Corel CMX";
451 ictx
->handle_nonchunk_data_fn
= my_handle_nonchunk_riff_data_fn
;
452 d
->cmx_parse_hack
= 1;
455 fmtname
= "CorelMOVE";
456 ictx
->handle_nonchunk_data_fn
= my_handle_nonchunk_riff_data_fn
;
457 d
->cmv_parse_hack
= 1;
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" */) {
466 fmtname
= "CorelDRAW (RIFF-based)";
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;
484 if(chunktype
==CHUNK_RIFF
|| chunktype
==CHUNK_RIFX
) {
485 // TODO: INAM data is probably not scoped entirely correctly.
487 ucstring_empty(d
->INAM_data
);
491 if(ictx
->main_contentstype4cc
.id
==CODE_AVI
&& chunktype
==CHUNK_LIST
&&
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;
503 // Keep track of when we are inside a 'movi' container.
505 d
->in_movi_level
= ictx
->level
;
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
)
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;
546 ictx
->chunkctx
->chunk_name
= name
;
551 static int my_riff_chunk_handler(struct de_iffctx
*ictx
)
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
) {
566 ictx
->is_std_container
= 1;
570 if(list_type
==CODE_INFO
) {
571 do_INFO_item(c
, d
, ictx
, dpos
, dlen
, ictx
->chunkctx
->chunk4cc
.id
);
576 switch(ictx
->chunkctx
->chunk4cc
.id
) {
579 do_DISP(c
, d
, ictx
, dpos
, dlen
);
583 case CHUNK_ICCP
: // Used by WebP
584 do_ICCP(c
, d
, ictx
, dpos
, dlen
);
588 case CHUNK_EXIF
: // Used by WebP
589 do_EXIF(c
, d
, ictx
, dpos
, dlen
);
593 case CHUNK_XMP
: // Used by WebP
594 case CHUNK__PMX
: // Used by WAVE, AVI
595 do_XMP(c
, d
, ictx
, dpos
, dlen
);
600 if(list_type
==CODE_RDIB
) {
601 do_RDIB_bmhd(c
, d
, ictx
);
607 if(ictx
->main_contentstype4cc
.id
==CODE_ACON
) {
608 extract_ani_frame(c
, d
, ictx
, dpos
, dlen
);
614 if(list_type
==CODE_RMID
) {
615 do_extract_raw(c
, d
, ictx
, dpos
, dlen
, "mid", NULL
, 0);
618 else if(list_type
==CODE_PAL
) {
619 do_palette(c
, d
, ictx
, dpos
, dlen
);
622 else if(list_type
==CODE_RDIB
) {
623 do_RDIB_data(c
, d
, ictx
, dpos
, dlen
);
629 if(ictx
->main_contentstype4cc
.id
==CODE_WAVE
) {
630 do_wav_fmt(c
, d
, ictx
, dpos
, dlen
);
636 if(ictx
->main_contentstype4cc
.id
==CODE_WAVE
) {
637 do_wav_fact(c
, d
, ictx
, dpos
, dlen
);
643 if(ictx
->main_contentstype4cc
.id
==CODE_AVI
) {
644 do_avi_avih(c
, d
, ictx
, dpos
, dlen
);
650 if(ictx
->main_contentstype4cc
.id
==CODE_AVI
) {
651 do_avi_strh(c
, d
, ictx
, dpos
, dlen
);
657 if(ictx
->main_contentstype4cc
.id
==CODE_AVI
) {
658 do_avi_strf(c
, d
, ictx
, dpos
, dlen
);
664 if(d
->is_cdr
&& ictx
->curr_container_contentstype4cc
.id
==CODE_bmpt
) {
665 do_cdr_bmp(c
, d
, ictx
, dpos
, dlen
);
675 static void de_run_riff(deark
*c
, de_module_params
*mparams
)
678 struct de_iffctx
*ictx
= NULL
;
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
);
694 if(!de_memcmp(buf
, "RIFF", 4)) {
696 ictx
->reversed_4cc
= 0;
698 else if(!de_memcmp(buf
, "RIFX", 4)) {
700 ictx
->reversed_4cc
= 0;
702 else if(!de_memcmp(buf
, "XFIR", 4)) {
704 ictx
->reversed_4cc
= 1;
707 de_warn(c
, "This is probably not a RIFF file.");
709 ictx
->reversed_4cc
= 0;
712 fmtutil_read_iff_format(ictx
, 0, ictx
->f
->len
);
714 fmtutil_destroy_iff_decoder(ictx
);
716 ucstring_destroy(d
->INAM_data
);
721 static int de_identify_riff(deark
*c
)
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;
740 void de_module_riff(deark
*c
, struct deark_module_info
*mi
)
743 mi
->desc
= "RIFF-based formats";
744 mi
->run_fn
= de_run_riff
;
745 mi
->identify_fn
= de_identify_riff
;