Made Unix builds more likely to be Y2038-compliant
[deark.git] / modules / mmm.c
blobcdb7be5c95e1fb0b4843bfd1f36f5807e96bfd82
1 // This file is part of Deark.
2 // Copyright (C) 2024 Jason Summers
3 // See the file COPYING for terms of use.
5 // RIFF Multimedia Movie / RMMP / .MMM
7 #include <deark-private.h>
8 #include <deark-fmtutil.h>
9 DE_DECLARE_MODULE(de_module_mmm);
11 #define CODE_CFTC 0x43465443U
12 #define CODE_CLUT 0x434c5554U
13 #define CODE_CURS 0x43555253U
14 #define CODE_DIB 0x44494220U
15 #define CODE_McNm 0x4d634e6dU
16 #define CODE_RIFF 0x52494646U
17 #define CODE_RMMP 0x524d4d50U
18 #define CODE_SCVW 0x53435657U
19 #define CODE_STR 0x53545220U
20 #define CODE_STXT 0x53545854U
21 #define CODE_VWAC 0x56574143U
22 #define CODE_VWCF 0x56574346U
23 #define CODE_VWCR 0x56574352U
24 #define CODE_VWFM 0x5657464dU
25 #define CODE_VWLB 0x56574c42U
26 #define CODE_VWSC 0x56575343U
27 #define CODE_VWTL 0x5657544cU
28 #define CODE_VWtc 0x56577463U
29 #define CODE_Ver_ 0x5665722eU // "Ver."
30 #define CODE_cftc 0x63667463U
31 #define CODE_clut 0x636c7574U
32 #define CODE_dib 0x64696220U
33 #define CODE_mcnm 0x6d636e6dU
34 #define CODE_snd 0x736e6420U // Not sure if 'SND' exists
35 #define CODE_str 0x73747220U
36 #define CODE_scvw 0x73637677U
37 #define CODE_stxt 0x73747874U
38 #define CODE_ver 0x76657220U // "ver "
39 #define CODE_vwac 0x76776163U
40 #define CODE_vwcf 0x76776366U
41 #define CODE_vwcr 0x76776372U
42 #define CODE_vwfm 0x7677666dU
43 #define CODE_vwlb 0x76776c62U
44 #define CODE_vwsc 0x76777363U
45 #define CODE_vwtc 0x76777463U
46 #define CODE_vwtl 0x7677746cU
48 struct mmm_ctx {
49 int pass; // 100=reading alt. palette file
50 u8 opt_allowbad;
51 u8 use_alt_palfile;
52 u8 force_pal; // Set if mmm:palid option was used
53 int pal_id_to_use; // Valid if mmm:palid option was used
54 de_encoding input_encoding;
56 u8 errflag;
57 u8 suppress_dib_pass2;
58 int clut_count;
59 int dib_count;
61 int tmp_rsrcid;
62 u8 have_pal;
63 de_ucstring *tmp_namestr;
64 de_color pal1[2];
65 de_color pal[256];
68 static void mmm_default_pal256(deark *c, struct mmm_ctx *d)
70 UI i, j, k;
71 static const u8 v[6] = {0, 48, 100, 152, 204, 252};
73 de_warn(c, "Using a default palette. Colors might be wrong.");
75 // Note: This sets pal[40] incorrectly, but it will be fixed later.
76 for(i=0; i<6; i++) {
77 for(j=0; j<6; j++) {
78 for(k=0; k<6; k++) {
79 d->pal[40 + i*36 + j*6 + k] = DE_MAKE_RGB(v[i], v[j], v[k]);
84 for(i=0; i<=10; i++) {
85 d->pal[i] = DE_MAKE_GRAY(i*24);
88 for(i=0; i<10; i++) {
89 UI t;
91 t = 16+24*i;
92 if(t>=128) t += 4;
93 d->pal[11+i] = DE_MAKE_RGB(0, 0, t);
94 d->pal[21+i] = DE_MAKE_RGB(0, t, 0);
95 d->pal[31+i] = DE_MAKE_RGB(t, 0, 0);
98 d->have_pal = 1;
101 static void mmm_allowbad_note(deark *c)
103 de_info(c, "Note: Use \"-opt mmm:allowbad\" to extract anyway.");
106 static void mmm_default_pal16(deark *c, struct mmm_ctx *d)
108 de_make_grayscale_palette(d->pal, 16, 0);
109 d->have_pal = 1;
111 // TODO: Figure out if there is a default 16-color palette.
112 if(d->opt_allowbad) {
113 de_warn(c, "No palette found. Colors will be wrong.");
115 else {
116 de_err(c, "No palette found");
117 mmm_allowbad_note(c);
118 d->errflag = 1;
122 // Read rsrc id to d->tmp_rsrcid
123 static void mmm_read_rsrcid_p(deark *c, struct mmm_ctx *d, struct de_iffctx *ictx,
124 i64 *ppos, const char *name)
126 // Note: I can't rule out the possibility that this is a 16-bit field (like
127 // in macrsrc format), followed by a 16-bit field that is always either 0
128 // or 0xffff. I haven't found any resources for which it would make a
129 // difference if we read it as 16-bit.
130 d->tmp_rsrcid = (int)dbuf_geti32le_p(ictx->f, ppos);
131 de_dbg(c, "%s: %d", (name ? name : "rsrc id"), d->tmp_rsrcid);
134 // Read chunk name to d->tmp_namestr
135 static void mmm_read_name_p(deark *c, struct mmm_ctx *d, struct de_iffctx *ictx,
136 i64 *ppos, const char *name)
138 i64 pos = *ppos;
139 i64 namelen;
141 ucstring_empty(d->tmp_namestr);
142 namelen = (i64)dbuf_getbyte(ictx->f, pos);
143 if(namelen) {
144 dbuf_read_to_ucstring(ictx->f, pos+1, namelen, d->tmp_namestr, 0, d->input_encoding);
145 de_dbg(c, "%s: \"%s\"", (name ? name : "name"), ucstring_getpsz_d(d->tmp_namestr));
147 *ppos += de_pad_to_2(1+namelen);
150 static void do_mmm_mcnm(deark *c, struct mmm_ctx *d, struct de_iffctx *ictx,
151 i64 dpos1, i64 dlen)
153 i64 pos = dpos1+4;
155 mmm_read_name_p(c, d, ictx, &pos, "movie name");
158 static void do_mmm_ver(deark *c, struct mmm_ctx *d, struct de_iffctx *ictx,
159 i64 dpos1, i64 dlen)
161 UI ver;
163 ver = (UI)dbuf_getu32le(ictx->f, dpos1);
164 de_dbg(c, "version: %u", ver);
167 static void do_mmm_cftc(deark *c, struct mmm_ctx *d, struct de_iffctx *ictx,
168 i64 dpos1, i64 dlen)
170 i64 nentries;
171 i64 i;
172 i64 pos;
173 struct de_fourcc tmp4cc;
175 if(dlen<4) goto done;
176 nentries = (dlen-4)/16;
178 pos = dpos1+4;
179 for(i=0; i<nentries; i++) {
180 int chk_id;
181 i64 chk_pos;
182 i64 chk_dlen;
183 de_zeromem(&tmp4cc, sizeof(struct de_fourcc));
184 dbuf_read_fourcc(ictx->f, pos, &tmp4cc, 4, 0);
185 pos += 4;
186 if(tmp4cc.id==0) break;
187 chk_dlen = dbuf_getu32le_p(ictx->f, &pos);
188 chk_id = (int)dbuf_geti32le_p(ictx->f, &pos);
189 chk_pos = dbuf_getu32le_p(ictx->f, &pos);
190 de_dbg(c, "toc entry '%s' id=%d pos=%"I64_FMT" dlen=%"I64_FMT,
191 tmp4cc.id_sanitized_sz, chk_id, chk_pos, chk_dlen);
194 done:
198 // TODO?: This duplicates some code in the macrsrc module.
199 // What we probably ought to do is generate a macrsrc file containing all the
200 // cursor and icon resources, and any other suitable resources. But that's
201 // easier said than done. Macrsrc is a complex format, and some things may
202 // have been lost in the translation to MMM format.
203 static void do_mmm_CURS(deark *c, struct mmm_ctx *d, struct de_iffctx *ictx, i64 pos1, i64 len)
205 i64 pos = pos1;
206 de_bitmap *img_fg = NULL;
207 de_bitmap *img_mask = NULL;
208 de_finfo *fi = NULL;
210 mmm_read_rsrcid_p(c, d, ictx, &pos, NULL);
211 mmm_read_name_p(c, d, ictx, &pos, NULL);
212 if(pos1+len-pos < 68) goto done;
214 fi = de_finfo_create(c);
216 img_fg = de_bitmap_create(c, 16, 16, 2);
217 img_mask = de_bitmap_create(c, 16, 16, 1);
219 de_dbg(c, "foreground at %"I64_FMT, pos);
220 de_convert_image_bilevel(c->infile, pos, 2, img_fg, DE_CVTF_WHITEISZERO);
221 pos += 32;
222 de_dbg(c, "mask at %"I64_FMT, pos);
223 de_convert_image_bilevel(c->infile, pos, 2, img_mask, 0);
224 pos += 32;
225 de_bitmap_apply_mask(img_fg, img_mask, 0);
227 fi->hotspot_y = (int)de_geti16be_p(&pos);
228 fi->hotspot_x = (int)de_geti16be_p(&pos);
229 fi->has_hotspot = 1;
230 de_dbg(c, "hotspot: (%d,%d)", fi->hotspot_x, fi->hotspot_y);
232 if(!c->filenames_from_file) ucstring_empty(d->tmp_namestr);
233 if(ucstring_isnonempty(d->tmp_namestr)) {
234 ucstring_append_char(d->tmp_namestr, '.');
236 ucstring_append_sz(d->tmp_namestr, "CURS", DE_ENCODING_LATIN1);
237 de_finfo_set_name_from_ucstring(c, fi, d->tmp_namestr, 0);
238 de_bitmap_write_to_file_finfo(img_fg, fi, DE_CREATEFLAG_OPT_IMAGE);
240 done:
241 de_bitmap_destroy(img_fg);
242 de_bitmap_destroy(img_mask);
243 de_finfo_destroy(c, fi);
246 static void do_mmm_dib(deark *c, struct mmm_ctx *d, struct de_iffctx *ictx, i64 pos1, i64 len)
248 i64 pos;
249 i64 i_infohdr_pos;
250 i64 i_bits_pos;
251 i64 infosize;
252 i64 i_bits_size;
253 i64 o_bits_size;
254 i64 i_rowspan;
255 i64 k;
256 i64 j;
257 u8 special_1bpp = 0;
258 int ret;
259 dbuf *outf = NULL;
260 struct de_bmpinfo bi;
262 pos = pos1;
263 mmm_read_rsrcid_p(c, d, ictx, &pos, "dib id");
264 mmm_read_name_p(c, d, ictx, &pos, NULL);
265 i_infohdr_pos = pos;
267 infosize = dbuf_getu32le(ictx->f, i_infohdr_pos);
268 if(infosize<40 || infosize>124) goto done;
269 i_bits_pos = i_infohdr_pos + infosize;
270 i_bits_size = pos+len-i_bits_pos;
272 ret = fmtutil_get_bmpinfo(c, ictx->f, &bi, i_infohdr_pos, infosize, 0);
273 if(!ret) goto done;
274 if(bi.num_colors > 256) goto done;
276 if(bi.bitcount==1) {
277 special_1bpp = 1;
280 if(special_1bpp) {
281 i_rowspan = de_pad_to_n(bi.bitcount*bi.width, 16) / 8;
283 else {
284 i_rowspan = bi.rowspan;
287 if(bi.compression_field==0) {
288 o_bits_size = bi.foreground_size;
290 else {
291 o_bits_size = i_bits_size;
294 // color table
295 if(bi.bitcount==4 && !d->have_pal) {
296 mmm_default_pal16(c, d);
298 if(bi.bitcount==8 && !d->have_pal) {
299 mmm_default_pal256(c, d);
301 if(d->errflag) goto done;
303 outf = dbuf_create_output_file(c, "bmp", NULL, 0);
305 // file header
306 fmtutil_generate_bmpfileheader(c, outf, &bi,
307 14 + infosize + bi.num_colors*4 + o_bits_size);
309 // info header
310 dbuf_copy(ictx->f, i_infohdr_pos, 20, outf);
311 if(special_1bpp) {
312 // She biSizeImage field may be wrong, so zero it out.
313 dbuf_write_zeroes(outf, 4);
315 else {
316 dbuf_copy(ictx->f, i_infohdr_pos+20, 4, outf);
318 dbuf_copy(ictx->f, i_infohdr_pos+24, infosize-24, outf);
320 // color table
322 if(bi.bitcount==1) {
323 for(k=0; k<2; k++) {
324 dbuf_writebyte(outf, DE_COLOR_B(d->pal1[k]));
325 dbuf_writebyte(outf, DE_COLOR_G(d->pal1[k]));
326 dbuf_writebyte(outf, DE_COLOR_R(d->pal1[k]));
327 dbuf_writebyte(outf, 0);
330 else if(bi.bitcount<=8) {
331 if(bi.num_colors>256) goto done;
332 for(k=0; k<bi.num_colors; k++) {
333 dbuf_writebyte(outf, DE_COLOR_B(d->pal[k]));
334 dbuf_writebyte(outf, DE_COLOR_G(d->pal[k]));
335 dbuf_writebyte(outf, DE_COLOR_R(d->pal[k]));
336 dbuf_writebyte(outf, 0);
340 // bits
341 if(special_1bpp) {
342 for(j=0; j<bi.height; j++) {
343 dbuf_copy(ictx->f, i_bits_pos+(bi.height-1-j)*i_rowspan, bi.rowspan, outf);
346 else {
347 dbuf_copy(ictx->f, i_bits_pos, i_bits_size, outf);
350 done:
351 dbuf_close(outf);
354 static void do_mmm_clut(deark *c, struct mmm_ctx *d, struct de_iffctx *ictx,
355 i64 pos1, i64 len)
357 i64 pos;
358 i64 num_entries;
359 i64 i;
360 int rsrc_id;
361 u8 is_correct_pass;
362 u8 keep_this_pal = 0;
364 pos = pos1;
365 mmm_read_rsrcid_p(c, d, ictx, &pos, "pal id");
366 rsrc_id = d->tmp_rsrcid;
367 mmm_read_name_p(c, d, ictx, &pos, NULL);
369 num_entries = (pos1+len-pos)/6;
371 de_dbg(c, "palette at %"I64_FMT", %u entries", pos, (UI)num_entries);
373 if(num_entries!=16 && num_entries!=256) {
374 de_warn(c, "Unsupported type of palette");
375 goto done;
378 is_correct_pass = (d->pass==1 && !d->use_alt_palfile) ||
379 (d->pass==100 && d->use_alt_palfile);
381 if(d->force_pal) {
382 if(rsrc_id==d->pal_id_to_use && is_correct_pass) {
383 keep_this_pal = 1;
386 else if(!d->have_pal && is_correct_pass) {
387 keep_this_pal = 1;
390 de_dbg_indent(c, 1);
391 for(i=0; i<num_entries; i++) {
392 UI samp[3];
393 de_color clr;
394 i64 idx;
395 UI k;
397 idx = num_entries-1-i;
399 for(k=0; k<3; k++) {
400 samp[k] = (UI)dbuf_getu16be_p(ictx->f, &pos);
401 samp[k] = (UI)de_sample_nbit_to_8bit(16, samp[k]);
403 clr = DE_MAKE_RGB(samp[0], samp[1], samp[2]);
404 de_dbg_pal_entry(c, idx, clr);
406 if(keep_this_pal) {
407 d->pal[idx] = clr;
410 de_dbg_indent(c, -1);
411 if(keep_this_pal) {
412 d->have_pal = 1;
415 done:
416 d->clut_count++;
419 static void do_mmm_snd(deark *c, struct mmm_ctx *d, struct de_iffctx *ictx, i64 pos1, i64 len)
421 i64 pos = pos1;
422 i64 apos, alen;
423 dbuf *outf = NULL;
424 de_finfo *fi = NULL;
425 i64 pad = 0;
426 i64 sample_rate;
427 UI sr_code;
428 UI n;
429 u8 x;
430 u8 ok = 0;
432 fi = de_finfo_create(c);
433 mmm_read_rsrcid_p(c, d, ictx, &pos, NULL);
435 mmm_read_name_p(c, d, ictx, &pos, NULL);
436 if(ucstring_isnonempty(d->tmp_namestr) && c->filenames_from_file) {
437 de_finfo_set_name_from_ucstring(c, fi, d->tmp_namestr, 0);
440 // Note: The code in this function is probably wrong. Don't use it for
441 // anything important.
443 if(c->debug_level>=3) {
444 de_dbg_hexdump(c, ictx->f, pos, 96, 96, "sndhdr", 0);
447 n = (UI)dbuf_getu16le_p(ictx->f, &pos);
448 if(n != 0x0200) goto done;
449 pos += 20;
450 sr_code = (UI)dbuf_getbyte_p(ictx->f, &pos);
451 if(sr_code==0x2b) sample_rate = 11025;
452 else if(sr_code==0x56) sample_rate = 22050;
453 else sample_rate = sr_code * 256;
454 de_dbg(c, "sample rate info: 0x%02x (using %"I64_FMT")", sr_code, sample_rate);
455 if(sample_rate<40) goto done;
456 pos += 11;
457 x = dbuf_getbyte_p(ictx->f, &pos);
458 pos += 1;
459 // Usually the header is 36 bytes, but sometimes 78.
460 if(x==0xff) pos += 42;
462 apos = pos;
463 alen = pos1+len-apos;
464 if(alen<=0) goto done;
466 outf = dbuf_create_output_file(c, "wav", fi, 0);
467 dbuf_writeu32be(outf, CODE_RIFF);
468 pad = alen%2;
469 dbuf_writeu32le(outf, alen+36+pad);
470 dbuf_write(outf, (const u8*)"WAVEfmt \x10\0\0\0\x01\0\x01\0", 16);
471 dbuf_writeu32le(outf, sample_rate);
472 dbuf_writeu32le(outf, sample_rate);
473 dbuf_write(outf, (const u8*)"\x01\0\x08\0" "data", 8);
474 dbuf_writeu32le(outf, alen);
475 dbuf_copy(ictx->f, apos, alen, outf);
476 dbuf_write_zeroes(outf, pad);
477 ok = 1;
479 done:
480 if(!ok) {
481 de_warn(c, "Unsupported type of audio resource");
483 dbuf_close(outf);
484 de_finfo_destroy(c, fi);
487 static int my_mmm_chunk_handler(struct de_iffctx *ictx)
489 deark *c = ictx->c;
490 struct mmm_ctx *d = (struct mmm_ctx*)ictx->userdata;
491 i64 dpos, dlen;
493 dpos = ictx->chunkctx->dpos;
494 dlen = ictx->chunkctx->dlen;
496 switch(ictx->chunkctx->chunk4cc.id) {
497 case CODE_RIFF:
498 ictx->is_std_container = 1;
499 goto done;
502 if(ictx->level != 1) goto done;
504 ictx->handled = 1;
506 switch(ictx->chunkctx->chunk4cc.id) {
507 case CODE_Ver_:
508 case CODE_ver:
509 if(d->pass==1) {
510 do_mmm_ver(c, d, ictx, dpos, dlen);
512 break;
513 case CODE_CFTC:
514 case CODE_cftc:
515 if(d->pass==1) {
516 do_mmm_cftc(c, d, ictx, dpos, dlen);
518 break;
519 case CODE_CLUT:
520 case CODE_clut:
521 if(d->pass==1 || d->pass==100) {
522 do_mmm_clut(c, d, ictx, dpos, dlen);
524 break;
525 case CODE_DIB:
526 case CODE_dib:
527 if(d->pass==1) {
528 d->dib_count++;
530 else if(d->pass==2 && !d->suppress_dib_pass2) {
531 do_mmm_dib(c, d, ictx, dpos, dlen);
533 break;
534 case CODE_snd:
535 if(d->pass==2) {
536 do_mmm_snd(c, d, ictx, dpos, dlen);
538 break;
539 case CODE_CURS:
540 if(d->pass==2) {
541 do_mmm_CURS(c, d, ictx, dpos, dlen);
543 break;
544 case CODE_McNm:
545 case CODE_mcnm:
546 if(d->pass==1) {
547 do_mmm_mcnm(c, d, ictx, dpos, dlen);
549 break;
550 default:
551 if(d->pass==1) {
552 // Make sure the default behavior only happens in one of the passes
553 // (e.g. hexdump if the debug level is set high enough).
554 ictx->handled = 0;
558 done:
559 if(d->errflag) return 0;
560 return 1;
563 static int looks_like_a_4cc_b(const u8 *buf)
565 i64 i;
567 for(i=0; i<4; i++) {
568 if(buf[i]<32 || buf[i]>126) return 0;
570 return 1;
573 // A few files are seen to be malformed in the neighborhood of the VWCF chunk,
574 // having two extra bytes after it that shouldn't be there.
575 // This is a quick hack to try to handle such files.
576 // (But this is evidence that we really ought to be relying on the table
577 // of contents, instead of reading the file sequentially.)
578 static int my_mmm_handle_nonchunk_data_fn(struct de_iffctx *ictx,
579 i64 pos, i64 *plen)
581 u8 buf[6];
583 dbuf_read(ictx->f, buf, pos, sizeof(buf));
585 if(buf[0]<65 || buf[1]<65) {
586 if(looks_like_a_4cc_b(&buf[2])) {
587 *plen = 2;
588 de_dbg(ictx->c, "[%"I64_FMT" non-RIFF bytes at %"I64_FMT"]", *plen, pos);
589 return 1;
593 return 0;
596 static int my_preprocess_mmm_chunk_fn(struct de_iffctx *ictx)
598 size_t k;
599 struct mmmnames_struct {
600 u32 id1, id2;
601 const char *name;
603 static const struct mmmnames_struct mmmnames[] = {
604 { CODE_CFTC, CODE_cftc, "table of contents" },
605 { CODE_CLUT, CODE_clut, "palette" },
606 { CODE_DIB, CODE_dib, "bitmap" },
607 { CODE_McNm, CODE_mcnm, "movie name" },
608 { CODE_SCVW, CODE_scvw, "director score" },
609 { CODE_snd, CODE_snd, "sound resource" },
610 { CODE_STR, CODE_str, "string table" },
611 { CODE_STXT, CODE_stxt, "styled text" },
612 { CODE_Ver_, CODE_ver, "converter version" },
613 { CODE_VWAC, CODE_vwac, "script commands" },
614 { CODE_VWCF, CODE_vwcf, "movie config" },
615 { CODE_VWCR, CODE_vwcr, "cast record array" },
616 { CODE_VWFM, CODE_vwfm, "font mapping" },
617 { CODE_VWLB, CODE_vwlb, "label list" },
618 { CODE_VWSC, CODE_vwsc, "movie score" },
619 { CODE_VWtc, CODE_vwtc, "timecode" },
620 { CODE_VWTL, CODE_vwtl, "pixel pattern tile" }
621 // Other known chunks: VWFI vwfi VWCI vwci
622 // crsr CURS FOND fwst
623 // icl4 icl8 ICN# ics# ics4 ics8
624 // NFNT pict vers XCMD XCOD XFCN
627 for(k=0; k<DE_ARRAYCOUNT(mmmnames); k++) {
628 if((ictx->chunkctx->chunk4cc.id == mmmnames[k].id1) ||
629 (ictx->chunkctx->chunk4cc.id == mmmnames[k].id2))
631 ictx->chunkctx->chunk_name = mmmnames[k].name;
632 break;
635 return 1;
638 static void setup_ictx_for_mmm(deark *c, struct mmm_ctx *d, struct de_iffctx *ictx)
640 ictx->userdata = (void*)d;
641 ictx->is_le = 1;
642 ictx->handle_chunk_fn = my_mmm_chunk_handler;
643 ictx->handle_nonchunk_data_fn = my_mmm_handle_nonchunk_data_fn;
644 ictx->preprocess_chunk_fn = my_preprocess_mmm_chunk_fn;
647 static int read_alt_palette_file(deark *c, struct mmm_ctx *d)
649 dbuf *palfile = NULL;
650 struct de_iffctx *ictx = NULL;
651 const char *palfn;
652 int retval = 1;
654 palfn = de_get_ext_option(c, "file2");
655 if(!palfn) {
656 goto done;
659 d->use_alt_palfile = 1;
660 palfile = dbuf_open_input_file(c, palfn);
661 if(!palfile) {
662 retval = 0;
663 goto done;
666 ictx = fmtutil_create_iff_decoder(c);
667 setup_ictx_for_mmm(c, d, ictx);
668 ictx->f = palfile;
670 d->pass = 100;
671 de_dbg(c, "reading alt pal file");
672 de_dbg_indent(c, 1);
673 fmtutil_read_iff_format(ictx, 0, palfile->len);
674 de_dbg_indent(c, -1);
676 if(!d->have_pal) {
677 if(d->force_pal) {
678 de_err(c, "Palette %d not found", d->pal_id_to_use);
680 else {
681 de_err(c, "No palette found");
683 retval = 0;
684 goto done;
687 done:
688 fmtutil_destroy_iff_decoder(ictx);
689 dbuf_close(palfile);
691 // (hack) Reset some things that this function isn't supposed to change.
692 d->errflag = 0;
693 d->clut_count = 0;
694 d->dib_count = 0;
696 return retval;
699 static void de_run_mmm(deark *c, de_module_params *mparams)
701 struct mmm_ctx *d = NULL;
702 struct de_iffctx *ictx = NULL;
703 const char *s;
704 int saved_indent_level;
706 de_dbg_indent_save(c, &saved_indent_level);
707 d = de_malloc(c, sizeof(struct mmm_ctx));
708 d->input_encoding = de_get_input_encoding(c, mparams, DE_ENCODING_ASCII);
709 d->opt_allowbad = (u8)de_get_ext_option_bool(c, "mmm:allowbad", 0);
710 s = de_get_ext_option(c, "mmm:palid");
711 if(s) {
712 d->pal_id_to_use = de_atoi(s);
713 d->force_pal = 1;
716 d->tmp_namestr = ucstring_create(c);
718 d->pal1[0] = DE_STOCKCOLOR_BLACK;
719 d->pal1[1] = DE_STOCKCOLOR_WHITE;
721 if(!read_alt_palette_file(c, d)) goto done;
723 ictx = fmtutil_create_iff_decoder(c);
724 setup_ictx_for_mmm(c, d, ictx);
725 ictx->f = c->infile;
727 d->pass = 1;
728 de_dbg(c, "pass %d", d->pass);
729 de_dbg_indent(c, 1);
730 fmtutil_read_iff_format(ictx, 0, c->infile->len);
731 de_dbg_indent(c, -1);
732 if(d->errflag) goto done;
734 if(d->force_pal && !d->have_pal && !d->opt_allowbad) {
735 de_err(c, "Palette %d not found", d->pal_id_to_use);
736 goto done;
739 if(d->clut_count>1 && d->dib_count>0 && !d->opt_allowbad && !d->force_pal) {
740 de_err(c, "Multiple palettes found (not supported, "
741 "or try \"-opt mmm:palid=...\").");
742 mmm_allowbad_note(c);
743 d->suppress_dib_pass2 = 1;
746 d->pass = 2;
747 de_dbg(c, "pass %d", d->pass);
748 de_dbg_indent(c, 1);
749 fmtutil_read_iff_format(ictx, 0, c->infile->len);
750 de_dbg_indent(c, -1);
752 done:
753 fmtutil_destroy_iff_decoder(ictx);
754 if(d) {
755 de_dbg(c, "dib count: %d", d->dib_count);
756 de_dbg(c, "clut count: %d", d->clut_count);
757 ucstring_destroy(d->tmp_namestr);
758 de_free(c, d);
760 de_dbg_indent_restore(c, saved_indent_level);
763 static int de_identify_mmm(deark *c)
765 if((u32)de_getu32be(0)!=CODE_RIFF) return 0;
766 if((u32)de_getu32be(8)!=CODE_RMMP) return 0;
767 return 100;
770 static void de_help_mmm(deark *c)
772 de_msg(c, "-file2 <file.mmm> : File to read the palette from");
773 de_msg(c, "-opt mmm:palid=<id> : Use this palette");
774 de_msg(c, "-opt mmm:allowbad : Keep going after certain errors");
776 // file2 with palid: Use that id in file2 if it exists, otherwise fatal error.
777 // file2 w/o palid: Use the first palette in file2 if it exists, otherwise fatal error.
778 // palid w/o file2: Use that id if it exists, otherwise fatal error.
779 // neither: (not explained here)
782 void de_module_mmm(deark *c, struct deark_module_info *mi)
784 mi->id = "mmm";
785 mi->desc = "RIFF Multimedia Movie";
786 mi->run_fn = de_run_mmm;
787 mi->identify_fn = de_identify_mmm;
788 mi->help_fn = de_help_mmm;