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
,
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
)
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
)
252 if(!ictx
->is_le
) return;
253 ver
= de_getu16le(pos
);
254 de_dbg(c
, "version: 0x%04x", (unsigned int)ver
);
256 n
= de_getu16le(pos
);
257 de_dbg(c
, "number of entries: %d", (int)n
);
259 if(n
>(len
-4)/4) n
=(len
-4)/4;
263 de_dbg(c
, "palette entries at %d", (int)pos
);
267 g
= de_getbyte(pos
+1);
268 b
= de_getbyte(pos
+2);
269 flags
= de_getbyte(pos
+3);
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
;
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
);
291 static void do_DISP_TEXT(deark
*c
, lctx
*d
, struct de_iffctx
*ictx
, i64 pos
, i64 len1
)
297 if(dbuf_search_byte(ictx
->f
, 0x00, pos
, len1
, &foundpos
)) {
298 len
= foundpos
- pos
;
300 de_dbg(c
, "text length: %d", (int)len
);
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
)
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);
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
)
355 if(!ictx
->is_le
) 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
));
366 do_DISP_TEXT(c
, d
, ictx
, dpos
, dlen
);
370 do_DISP_DIB(c
, d
, ictx
, dpos
, dlen
);
375 static int is_fourcc_at(deark
*c
, struct de_iffctx
*ictx
, i64 pos
)
380 dbuf_read(ictx
->f
, b
, pos
, 4);
382 if(b
[i
]<32 || b
[i
]>126) return 0;
387 static int do_cmx_parse_hack(deark
*c
, lctx
*d
, struct de_iffctx
*ictx
, i64 pos
, i64
*plen
)
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
);
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
);
418 static int my_handle_nonchunk_riff_data_fn(struct de_iffctx
*ictx
,
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
);
433 static int my_on_std_container_start_fn(struct de_iffctx
*ictx
)
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
;
447 case CODE_ACON
: fmtname
= "Windows animated cursor"; break;
448 case CODE_AVI
: fmtname
= "AVI"; break;
449 case CODE_CDRX
: fmtname
= "Corel CCX"; break;
451 fmtname
= "Corel CMX";
452 ictx
->handle_nonchunk_data_fn
= my_handle_nonchunk_riff_data_fn
;
453 d
->cmx_parse_hack
= 1;
456 fmtname
= "CorelMOVE";
457 ictx
->handle_nonchunk_data_fn
= my_handle_nonchunk_riff_data_fn
;
458 d
->cmv_parse_hack
= 1;
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" */) {
467 fmtname
= "CorelDRAW (RIFF-based)";
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;
485 if(chunktype
==CHUNK_RIFF
|| chunktype
==CHUNK_RIFX
) {
486 // TODO: INAM data is probably not scoped entirely correctly.
488 ucstring_empty(d
->INAM_data
);
492 if(ictx
->main_contentstype4cc
.id
==CODE_AVI
&& chunktype
==CHUNK_LIST
&&
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;
504 // Keep track of when we are inside a 'movi' container.
506 d
->in_movi_level
= ictx
->level
;
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
)
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;
547 ictx
->chunkctx
->chunk_name
= name
;
552 static int my_riff_chunk_handler(struct de_iffctx
*ictx
)
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
) {
567 ictx
->is_std_container
= 1;
571 if(list_type
==CODE_INFO
) {
572 do_INFO_item(c
, d
, ictx
, dpos
, dlen
, ictx
->chunkctx
->chunk4cc
.id
);
577 switch(ictx
->chunkctx
->chunk4cc
.id
) {
580 do_DISP(c
, d
, ictx
, dpos
, dlen
);
584 case CHUNK_ICCP
: // Used by WebP
585 do_ICCP(c
, d
, ictx
, dpos
, dlen
);
589 case CHUNK_EXIF
: // Used by WebP
590 do_EXIF(c
, d
, ictx
, dpos
, dlen
);
594 case CHUNK_XMP
: // Used by WebP
595 case CHUNK__PMX
: // Used by WAVE, AVI
596 do_XMP(c
, d
, ictx
, dpos
, dlen
);
601 if(list_type
==CODE_RDIB
) {
602 do_RDIB_bmhd(c
, d
, ictx
);
608 if(ictx
->main_contentstype4cc
.id
==CODE_ACON
) {
609 extract_ani_frame(c
, d
, ictx
, dpos
, dlen
);
615 if(list_type
==CODE_RMID
) {
616 do_extract_raw(c
, d
, ictx
, dpos
, dlen
, "mid", NULL
, 0);
619 else if(list_type
==CODE_PAL
) {
620 do_palette(c
, d
, ictx
, dpos
, dlen
);
623 else if(list_type
==CODE_RDIB
) {
624 do_RDIB_data(c
, d
, ictx
, dpos
, dlen
);
630 if(ictx
->main_contentstype4cc
.id
==CODE_WAVE
) {
631 do_wav_fmt(c
, d
, ictx
, dpos
, dlen
);
637 if(ictx
->main_contentstype4cc
.id
==CODE_WAVE
) {
638 do_wav_fact(c
, d
, ictx
, dpos
, dlen
);
644 if(ictx
->main_contentstype4cc
.id
==CODE_AVI
) {
645 do_avi_avih(c
, d
, ictx
, dpos
, dlen
);
651 if(ictx
->main_contentstype4cc
.id
==CODE_AVI
) {
652 do_avi_strh(c
, d
, ictx
, dpos
, dlen
);
658 if(ictx
->main_contentstype4cc
.id
==CODE_AVI
) {
659 do_avi_strf(c
, d
, ictx
, dpos
, dlen
);
665 if(d
->is_cdr
&& ictx
->curr_container_contentstype4cc
.id
==CODE_bmpt
) {
666 do_cdr_bmp(c
, d
, ictx
, dpos
, dlen
);
676 static void de_run_riff(deark
*c
, de_module_params
*mparams
)
679 struct de_iffctx
*ictx
= NULL
;
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
);
695 if(!de_memcmp(buf
, "RIFF", 4)) {
697 ictx
->reversed_4cc
= 0;
699 else if(!de_memcmp(buf
, "RIFX", 4)) {
701 ictx
->reversed_4cc
= 0;
703 else if(!de_memcmp(buf
, "XFIR", 4)) {
705 ictx
->reversed_4cc
= 1;
708 de_warn(c
, "This is probably not a RIFF file.");
710 ictx
->reversed_4cc
= 0;
713 fmtutil_read_iff_format(ictx
, 0, ictx
->f
->len
);
715 fmtutil_destroy_iff_decoder(ictx
);
717 ucstring_destroy(d
->INAM_data
);
722 static int de_identify_riff(deark
*c
)
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;
741 void de_module_riff(deark
*c
, struct deark_module_info
*mi
)
744 mi
->desc
= "RIFF-based formats";
745 mi
->run_fn
= de_run_riff
;
746 mi
->identify_fn
= de_identify_riff
;