Refactoring the iff decoder
[deark.git] / modules / corel.c
blob2df26e843923539f07d60ca2e283681c0fd79d1f
1 // This file is part of Deark.
2 // Copyright (C) 2021 Jason Summers
3 // See the file COPYING for terms of use.
5 // Misc. Corel / CorelDRAW formats
7 #include <deark-private.h>
8 #include <deark-fmtutil.h>
9 DE_DECLARE_MODULE(de_module_cdr_wl);
10 DE_DECLARE_MODULE(de_module_corel_clb);
11 DE_DECLARE_MODULE(de_module_corel_bmf);
12 DE_DECLARE_MODULE(de_module_corel_ccx);
14 // **************************************************************************
15 // CorelDRAW CDR - old "WL" format
16 // **************************************************************************
18 static void de_run_cdr_wl(deark *c, de_module_params *mparams)
20 u8 version;
21 i64 pos = 0;
22 de_bitmap *img = NULL;
23 de_finfo *fi = NULL;
24 de_module_params *mparams2 = NULL;
26 de_declare_fmt(c, "CorelDRAW (WL format)");
27 version = de_getbyte(2);
28 de_dbg(c, "version code: 0x%02x", (unsigned int)version);
29 if(version <= (u8)'e') goto done;
31 pos = de_getu32le(28);
32 de_dbg(c, "preview image at %"I64_FMT, pos);
33 de_dbg_indent(c, 1);
34 fi = de_finfo_create(c);
35 de_finfo_set_name_from_sz(c, fi, "preview", 0, DE_ENCODING_LATIN1);
37 pos += 2; // ?
38 // Seems to be Windows DDB format, or something like it.
39 mparams2 = de_malloc(c, sizeof(de_module_params));
40 mparams2->in_params.codes = "NX";
41 mparams2->in_params.fi = fi;
42 de_run_module_by_id_on_slice(c, "ddb", mparams2, c->infile, pos, c->infile->len-pos);
43 de_dbg_indent(c, -1);
45 done:
46 de_bitmap_destroy(img);
47 de_finfo_destroy(c, fi);
48 de_free(c, mparams2);
51 static int de_identify_cdr_wl(deark *c)
53 if(!dbuf_memcmp(c->infile, 0, "WL", 2)) {
54 if(de_input_file_has_ext(c, "cdr")) return 100;
55 return 6;
57 return 0;
60 void de_module_cdr_wl(deark *c, struct deark_module_info *mi)
62 mi->id = "cdr_wl";
63 mi->desc = "CorelDRAW (old WL format)";
64 mi->desc2 = "extract preview image";
65 mi->run_fn = de_run_cdr_wl;
66 mi->identify_fn = de_identify_cdr_wl;
69 // **************************************************************************
70 // CorelMOSAIC .CLB
71 // **************************************************************************
73 struct clb_ctx {
74 de_encoding input_encoding;
75 i64 bytes_consumed;
78 static int do_clb_item(deark *c, struct clb_ctx *d, i64 pos1)
80 i64 pos = pos1;
81 i64 nlen;
82 i64 imglen;
83 i64 n;
84 de_ucstring *name = NULL;
85 de_finfo *fi = NULL;
86 int retval = 0;
87 int saved_indent_level;
88 int bitmap_ok = 1;
89 de_module_params *mparams2 = NULL;
91 de_dbg_indent_save(c, &saved_indent_level);
92 de_dbg(c, "item at %"I64_FMT, pos1);
93 de_dbg_indent(c, 1);
95 nlen = de_getu16le_p(&pos);
96 if(nlen<1 || nlen>63) goto done;
97 name = ucstring_create(c);
98 dbuf_read_to_ucstring(c->infile, pos, nlen, name, DE_CONVFLAG_STOP_AT_NUL,
99 d->input_encoding);
100 de_dbg(c, "name: \"%s\"", ucstring_getpsz_d(name));
101 pos += nlen;
103 imglen = de_getu32le_p(&pos);
104 de_dbg(c, "bitmap size: %"I64_FMT, imglen);
106 // Look ahead at the DDB bmBits field.
107 // It seems to be used, and it seems to be an absolute file position.
108 // Bail out if it's not what we expect.
109 n = de_getu32le(pos+12);
110 if(n!=0 && n!=pos+16) {
111 de_err(c, "Unexpected value for bmBits field");
112 bitmap_ok = 0;
115 if(bitmap_ok) {
116 de_dbg(c, "bitmap at %"I64_FMT, pos);
117 de_dbg_indent(c, 1);
118 fi = de_finfo_create(c);
119 ucstring_append_sz(name, ".preview", DE_ENCODING_LATIN1);
120 de_finfo_set_name_from_ucstring(c, fi, name, 0);
122 mparams2 = de_malloc(c, sizeof(de_module_params));
123 mparams2->in_params.codes = "N";
124 mparams2->in_params.fi = fi;
125 de_run_module_by_id_on_slice(c, "ddb", mparams2, c->infile, pos+2, imglen-2);
126 de_dbg_indent(c, -1);
129 pos += imglen;
131 // There are 9 bytes after the bitmap.
132 pos += 5; // ?
133 n = de_getu32le_p(&pos);
134 // This is a reference to the original file from which this preview was derived
135 // (should be in the companion .CLH file).
136 de_dbg(c, "original file size: %"I64_FMT, n);
138 d->bytes_consumed = pos-pos1;
139 retval = 1;
140 done:
141 de_free(c, mparams2);
142 de_finfo_destroy(c, fi);
143 ucstring_destroy(name);
144 de_dbg_indent_restore(c, saved_indent_level);
145 return retval;
148 static void de_run_corel_clb(deark *c, de_module_params *mparams)
150 struct clb_ctx *d = NULL;
151 i64 pos = 0;
153 d = de_malloc(c, sizeof(struct clb_ctx));
154 d->input_encoding = de_get_input_encoding(c, NULL, DE_ENCODING_ASCII);
155 while(1) {
156 if(pos+18 > c->infile->len) goto done;
157 d->bytes_consumed = 0;
158 if(!do_clb_item(c, d, pos)) goto done;
159 if(d->bytes_consumed<=0) goto done;
160 pos += d->bytes_consumed;
162 done:
163 de_free(c, d);
166 static int de_identify_corel_clb(deark *c)
168 i64 nlen, n;
170 // This might be too strict. Need more samples.
171 if(!de_input_file_has_ext(c, "clb")) return 0;
172 nlen = de_getu16le(0);
173 if(nlen<1 || nlen>63) return 0;
174 if(de_getbyte(2+nlen-1)!=0x00) return 0;
175 n = de_getu32le(2+nlen+4);
176 if(n!=0x00000001) return 0;
177 n = de_getu32le(2+nlen+16);
178 if(n!=2+nlen+20 && n!=0) return 0;
179 return 100;
182 void de_module_corel_clb(deark *c, struct deark_module_info *mi)
184 mi->id = "corel_clb";
185 mi->desc = "CorelMOSAIC .CLB library";
186 mi->run_fn = de_run_corel_clb;
187 mi->identify_fn = de_identify_corel_clb;
190 // **************************************************************************
191 // Corel Gallery .BMF
192 // **************************************************************************
194 // Warning: The BMF preview image decoder is based on reverse engineering, may not
195 // be correct.
197 static void de_run_corel_bmf(deark *c, de_module_params *mparams1)
199 de_module_params *mparams2 = NULL;
200 int saved_indent_level;
201 i64 pos;
202 i64 n;
203 i64 seg_size;
205 de_dbg_indent_save(c, &saved_indent_level);
206 pos = 65;
207 seg_size = de_getu32le_p(&pos);
208 de_dbg(c, "preview image segment at %"I64_FMT", len=%"I64_FMT, pos, seg_size);
209 de_dbg_indent(c, 1);
211 if(pos + seg_size > c->infile->len) {
212 seg_size = c->infile->len - pos;
215 n = de_getu32le(pos);
216 if(n!=40) {
217 de_err(c, "Unsupported Corel BMF version");
218 goto done;
221 mparams2 = de_malloc(c, sizeof(de_module_params));
222 mparams2->in_params.codes = "X";
223 mparams2->in_params.flags = 0x81;
224 de_run_module_by_id_on_slice(c, "dib", mparams2, c->infile, pos, seg_size);
226 done:
227 de_free(c, mparams2);
228 de_dbg_indent_restore(c, saved_indent_level);
231 static int de_identify_corel_bmf(deark *c)
233 if(!dbuf_memcmp(c->infile, 0, "@CorelBMF\x0a\x0d", 11)) return 100;
234 return 0;
237 void de_module_corel_bmf(deark *c, struct deark_module_info *mi)
239 mi->id = "corel_bmf";
240 mi->desc = "Corel Gallery BMF";
241 mi->run_fn = de_run_corel_bmf;
242 mi->identify_fn = de_identify_corel_bmf;
245 // **************************************************************************
246 // Corel CCX
247 // Decompress/convert Corel CCX clip art to Corel CMX
248 // **************************************************************************
250 #define CODE_CDRX 0x43445258U
251 #define CODE_CMX1 0x434d5831U
252 #define CODE_CPng 0x43506e67U
253 #define CODE_RIFF 0x52494646U
254 #define CODE_pack 0x7061636bU
256 typedef struct localctx_struct {
257 i64 pack_pos; // 0 = not found
258 i64 pack_dpos;
259 i64 pack_dlen;
260 int wrote_cmx_file;
261 } lctx;
263 static void do_decompress(deark *c, lctx *d)
265 u32 cmprmeth;
266 i64 pos = d->pack_dpos;
267 i64 cmpr_start;
268 i64 cmpr_len;
269 i64 unc_len;
270 i64 unc_len_padded;
271 i64 unc_riff_size;
272 i64 after_pack_pos;
273 i64 after_pack_len;
274 dbuf *outf = NULL;
275 dbuf *unc_data = NULL;
276 struct de_dfilter_in_params dcmpri;
277 struct de_dfilter_out_params dcmpro;
278 struct de_dfilter_results dres;
279 struct de_deflate_params inflparams;
281 de_zeromem(&inflparams, sizeof(struct de_deflate_params));
282 if(d->pack_dlen < 12) goto done;
283 unc_len = de_getu32le_p(&pos);
284 de_dbg(c, "uncompressed len (reported): %"I64_FMT, unc_len);
285 if(unc_len > DE_MAX_SANE_OBJECT_SIZE) goto done;
287 cmprmeth = (u32)de_getu32be_p(&pos);
288 if(cmprmeth != CODE_CPng) {
289 de_err(c, "Unsupported compression method");
290 goto done;
292 pos += 4; // Unknown field
294 unc_len_padded = de_pad_to_2(unc_len);
295 cmpr_start = pos;
296 cmpr_len = d->pack_dpos + d->pack_dlen - cmpr_start;
297 if(cmpr_len<1) goto done;
299 // Decompress the "pack" chunk to a membuf.
300 // We *could* decompress directly to the output file instead, but this way
301 // is more flexible.
302 unc_data = dbuf_create_membuf(c, unc_len_padded, 0x1);
303 de_dfilter_init_objects(c, &dcmpri, &dcmpro, &dres);
304 dcmpri.f = c->infile;
305 dcmpri.pos = cmpr_start;
306 dcmpri.len = cmpr_len;
307 dcmpro.f = unc_data;
308 dcmpro.len_known = 1;
309 dcmpro.expected_len = unc_len;
310 inflparams.flags = DE_DEFLATEFLAG_ISZLIB;
311 fmtutil_decompress_deflate_ex(c, &dcmpri, &dcmpro, &dres, &inflparams);
313 if(dres.errcode) {
314 de_err(c, "%s", dres.errmsg);
315 goto done;
317 if(unc_data->len < unc_len) {
318 de_warn(c, "Decompression may have failed (expected %"I64_FMT" bytes, got %"I64_FMT")",
319 unc_len, unc_data->len);
321 dbuf_truncate(unc_data, unc_len_padded);
323 after_pack_pos = d->pack_dpos + de_pad_to_2(d->pack_dlen);
324 after_pack_len = de_pad_to_2(c->infile->len - after_pack_pos);
325 unc_riff_size = d->pack_pos + unc_data->len + after_pack_len - 8;
327 outf = dbuf_create_output_file(c, "cmx", NULL, 0);
328 d->wrote_cmx_file = 1;
330 dbuf_writeu32be(outf, CODE_RIFF);
331 // Use the new the RIFF size
332 dbuf_writeu32le(outf, unc_riff_size);
333 // Change the RIFF type: CDRX -> CMX1
334 dbuf_writeu32be(outf, CODE_CMX1);
336 // Copy everything else, up to the "pack" chunk
337 dbuf_copy(c->infile, 12, d->pack_pos-12, outf);
339 // Copy the decompressed contents of the "pack" chunk
340 dbuf_copy(unc_data, 0, unc_data->len, outf);
342 // Copy everything after the "pack" chunk
343 dbuf_copy(c->infile, after_pack_pos, after_pack_len, outf);
345 done:
346 dbuf_close(unc_data);
347 dbuf_close(outf);
350 static int my_ccx_chunk_handler(struct de_iffctx *ictx)
352 lctx *d = (lctx*)ictx->userdata;
354 switch(ictx->chunkctx->chunk4cc.id) {
355 case CODE_RIFF:
356 ictx->is_std_container = 1;
357 return 1;
358 case CODE_pack:
359 d->pack_pos = ictx->chunkctx->pos;
360 d->pack_dpos = ictx->chunkctx->dpos;
361 d->pack_dlen = ictx->chunkctx->dlen;
362 // We have what we need; tell the RIFF parser to stop.
363 return 0;
366 ictx->handled = 1;
367 return 1;
370 static void de_run_corel_ccx(deark *c, de_module_params *mparams)
372 lctx *d = NULL;
373 struct de_iffctx *ictx = NULL;
375 d = de_malloc(c, sizeof(lctx));
377 ictx = fmtutil_create_iff_decoder(c);
378 ictx->is_le = 1;
379 ictx->reversed_4cc = 0;
380 ictx->userdata = (void*)d;
381 ictx->handle_chunk_fn = my_ccx_chunk_handler;
382 ictx->f = c->infile;
384 fmtutil_read_iff_format(ictx, 0, c->infile->len);
385 if(d->pack_pos) {
386 de_dbg(c, "pack chunk found at %"I64_FMT, d->pack_pos);
387 de_dbg_indent(c, 1);
388 do_decompress(c, d);
389 de_dbg_indent(c, -1);
392 fmtutil_destroy_iff_decoder(ictx);
393 if(d) {
394 if(!d->wrote_cmx_file) {
395 de_err(c, "Cannot convert this CCX file. Try \"-m riff\" to decode.");
397 de_free(c, d);
401 static int de_identify_corel_ccx(deark *c)
403 if((de_getu32be(0)==CODE_RIFF) &&
404 (de_getu32be(8)==CODE_CDRX))
406 return 100;
408 return 0;
411 void de_module_corel_ccx(deark *c, struct deark_module_info *mi)
413 mi->id = "corel_ccx";
414 mi->desc = "Corel CCX";
415 mi->desc2 = "Decompress to CMX";
416 mi->run_fn = de_run_corel_ccx;
417 mi->identify_fn = de_identify_corel_ccx;