zoo: Various improvements
[deark.git] / modules / shg.c
blobacca2f4437ac4fe807477a975b993e470e63e94e
1 // This file is part of Deark.
2 // Copyright (C) 2016 Jason Summers
3 // See the file COPYING for terms of use.
5 // Segmented Hypergraphics (SHG) and Multiple Resolution Bitmap (MRB)
7 #include <deark-config.h>
8 #include <deark-private.h>
9 #include <deark-fmtutil.h>
10 DE_DECLARE_MODULE(de_module_shg);
12 struct picture_ctx {
13 u8 picture_type;
14 u8 packing_method;
16 i64 xdpi, ydpi;
17 i64 planes;
18 i64 bitcount; // per plane
19 i64 rowspan; // per plane
20 i64 width, height;
21 i64 pal_size_in_colors;
22 i64 pal_size_in_bytes;
23 i64 final_image_size;
24 i64 colors_used;
25 i64 colors_important;
26 i64 pal_offset;
29 typedef struct localctx_struct {
30 i64 signature;
32 i64 shg_startpos;
33 i64 num_pictures;
34 } lctx;
36 struct rlectx {
37 dbuf *outf;
38 int compressed_run_pending;
39 i64 compressed_run_count;
40 i64 uncompressed_run_bytes_left;
41 i64 nbytes_consumed;
44 static void my_shgrle_codec_addbuf(struct de_dfilter_ctx *dfctx,
45 const u8 *buf, i64 buf_len)
47 i64 k;
48 struct rlectx *rctx = (struct rlectx*)dfctx->codec_private;
50 for(k=0; k<buf_len; k++) {
51 if(rctx->uncompressed_run_bytes_left>0) {
52 dbuf_writebyte(rctx->outf, buf[k]);
53 rctx->uncompressed_run_bytes_left--;
54 rctx->nbytes_consumed++;
55 continue;
58 if(rctx->compressed_run_pending) {
59 dbuf_write_run(rctx->outf, buf[k], rctx->compressed_run_count);
60 rctx->compressed_run_pending = 0;
61 rctx->nbytes_consumed += 2;
62 continue;
65 if(buf[k] & 0x80) { // beginning of uncompressed run
66 rctx->uncompressed_run_bytes_left = (i64)(buf[k] & 0x7f);
67 rctx->nbytes_consumed++;
68 continue;
71 rctx->compressed_run_count = (i64)buf[k];
72 rctx->compressed_run_pending = 1;
76 static void my_shgrle_codec_finish(struct de_dfilter_ctx *dfctx)
78 struct rlectx *rctx = (struct rlectx*)dfctx->codec_private;
80 dfctx->dres->bytes_consumed_valid = 1;
81 dfctx->dres->bytes_consumed = rctx->nbytes_consumed;
84 static void my_shgrle_codec_destroy(struct de_dfilter_ctx *dfctx)
86 struct rlectx *rctx = (struct rlectx*)dfctx->codec_private;
88 de_free(dfctx->c, rctx);
91 static void dfilter_shgrle_codec(struct de_dfilter_ctx *dfctx, void *codec_private_params)
93 struct rlectx *rctx = NULL;
95 rctx = de_malloc(dfctx->c, sizeof(struct rlectx));
96 rctx->outf = dfctx->dcmpro->f;
98 dfctx->codec_private = (void*)rctx;
99 dfctx->codec_finish_fn = my_shgrle_codec_finish;
100 dfctx->codec_destroy_fn = my_shgrle_codec_destroy;
101 dfctx->codec_addbuf_fn = my_shgrle_codec_addbuf;
104 // RunLength
105 static void do_decompress_type_1(deark *c, lctx *d,
106 struct de_dfilter_in_params *dcmpri, struct de_dfilter_out_params *dcmpro,
107 struct de_dfilter_results *dres)
109 de_dbg(c, "doing RLE decompression");
110 de_dfilter_decompress_oneshot(c, dfilter_shgrle_codec, NULL,
111 dcmpri, dcmpro, dres);
114 // LZ77
115 static void do_decompress_type_2(deark *c, lctx *d,
116 struct de_dfilter_in_params *dcmpri, struct de_dfilter_out_params *dcmpro,
117 struct de_dfilter_results *dres)
119 de_dbg(c, "doing LZ77 decompression");
120 fmtutil_hlp_lz77_codectype1(c, dcmpri, dcmpro, dres, NULL);
123 // LZ77 + RLE
124 static void do_decompress_type_3(deark *c, lctx *d,
125 struct de_dfilter_in_params *dcmpri, struct de_dfilter_out_params *dcmpro,
126 struct de_dfilter_results *dres)
128 struct de_dcmpr_two_layer_params tlp;
130 de_dbg(c, "doing LZ77+RLE decompression");
132 de_zeromem(&tlp, sizeof(struct de_dcmpr_two_layer_params));
133 tlp.codec1_type1 = fmtutil_hlp_lz77_codectype1;
134 tlp.codec1_private_params = NULL;
135 tlp.codec2 = dfilter_shgrle_codec;
136 tlp.dcmpri = dcmpri;
137 tlp.dcmpro = dcmpro;
138 tlp.dres = dres;
139 de_dfilter_decompress_two_layer(c, &tlp);
142 static int do_uncompress_picture_data(deark *c, lctx *d,
143 struct picture_ctx *pctx,
144 i64 compressed_offset, i64 compressed_size,
145 dbuf *pixels_final, i64 final_image_size)
147 int retval = 0;
148 struct de_dfilter_in_params dcmpri;
149 struct de_dfilter_out_params dcmpro;
150 struct de_dfilter_results dres;
152 if(pctx->packing_method>3) {
153 de_err(c, "Unsupported compression type: %d", (int)pctx->packing_method);
154 goto done;
157 de_dfilter_init_objects(c, &dcmpri, &dcmpro, &dres);
158 dcmpri.f = c->infile;
159 dcmpri.pos = compressed_offset;
160 dcmpri.len = compressed_size;
161 dcmpro.f = pixels_final;
162 dcmpro.len_known = 1;
163 dcmpro.expected_len = final_image_size;
165 switch(pctx->packing_method) {
166 case 1:
167 do_decompress_type_1(c, d, &dcmpri, &dcmpro, &dres);
168 break;
169 case 2:
170 do_decompress_type_2(c, d, &dcmpri, &dcmpro, &dres);
171 break;
172 case 3:
173 do_decompress_type_3(c, d, &dcmpri, &dcmpro, &dres);
174 break;
175 default: // 0, uncompressed
176 fmtutil_decompress_uncompressed(c, &dcmpri, &dcmpro, &dres, 0);
179 if(dres.errcode) {
180 de_err(c, "%s", de_dfilter_get_errmsg(c, &dres));
181 goto done;
184 if(pixels_final->len < final_image_size) {
185 de_warn(c, "Expected %"I64_FMT" bytes after decompression, only got %"I64_FMT,
186 final_image_size, pixels_final->len);
189 retval = 1;
191 done:
192 return retval;
195 static i64 per_inch_to_per_meter(i64 dpi)
197 return (i64)(0.5 + (100.0/2.54)*(double)dpi);
200 // Translate the picture into a BMP for output.
201 static void reconstruct_bmp(deark *c, lctx *d, struct picture_ctx *pctx,
202 dbuf *pixels_final)
204 dbuf *outf = NULL;
205 struct de_bmpinfo bi;
207 outf = dbuf_create_output_file(c, "bmp", NULL, 0);
209 // Write fileheader
210 de_zeromem(&bi, sizeof(struct de_bmpinfo));
211 bi.size_of_headers_and_pal = 40 + pctx->pal_size_in_bytes;
212 bi.total_size = bi.size_of_headers_and_pal + pctx->final_image_size;
213 fmtutil_generate_bmpfileheader(c, outf, &bi, 0);
215 // Write infoheader
216 dbuf_writeu32le(outf, 40);
217 dbuf_writeu32le(outf, pctx->width);
218 dbuf_writeu32le(outf, pctx->height);
219 dbuf_writeu16le(outf, pctx->planes);
220 dbuf_writeu16le(outf, pctx->bitcount);
221 dbuf_writeu32le(outf, 0); // compression
222 dbuf_writeu32le(outf, 0); // SizeImage
223 dbuf_writeu32le(outf, per_inch_to_per_meter(pctx->xdpi));
224 dbuf_writeu32le(outf, per_inch_to_per_meter(pctx->ydpi));
225 dbuf_writeu32le(outf, pctx->colors_used);
226 dbuf_writeu32le(outf, pctx->colors_important);
228 // Write color table
229 dbuf_copy(c->infile, pctx->pal_offset, pctx->pal_size_in_bytes, outf);
231 // Write pixels
232 dbuf_copy(pixels_final, 0, pctx->final_image_size, outf);
234 dbuf_close(outf);
237 // Translate the picture into a DDB, then call the ddb module.
238 static void reconstruct_ddb(deark *c, lctx *d, struct picture_ctx *pctx,
239 dbuf *pixels_final)
241 dbuf *tmpf = NULL;
242 de_finfo *fi = NULL;
243 de_module_params *mparams = NULL;
245 tmpf = dbuf_create_membuf(c, 14+pctx->final_image_size, 0);
247 // DDB header
248 dbuf_writeu16le(tmpf, 0); // bmType
249 dbuf_writeu16le(tmpf, pctx->width); // bmWidth
250 dbuf_writeu16le(tmpf, pctx->height); // bmHeight
251 dbuf_writeu16le(tmpf, pctx->rowspan); // bmWidthBytes
252 dbuf_writebyte(tmpf, (u8)pctx->planes); // bmPlanes
253 dbuf_writebyte(tmpf, (u8)pctx->bitcount); // bmBitsPixel
254 dbuf_writeu32le(tmpf, 0); // bmBits
256 dbuf_copy(pixels_final, 0, pctx->final_image_size, tmpf);
258 de_dbg(c, "processing decompressed DDB");
259 de_dbg_indent(c, 1);
260 fi = de_finfo_create(c);
261 fi->density.code = DE_DENSITY_DPI;
262 fi->density.xdens = (double)pctx->xdpi;
263 fi->density.ydens = (double)pctx->ydpi;
264 mparams = de_malloc(c, sizeof(de_module_params));
265 mparams->in_params.codes = "N";
266 mparams->in_params.fi = fi;
267 de_run_module_by_id_on_slice(c, "ddb", mparams, tmpf, 0, tmpf->len);
268 de_free(c, mparams);
269 de_dbg_indent(c, -1);
271 dbuf_close(tmpf);
272 de_finfo_destroy(c, fi);
275 // Handle a picture of type DIB or DDB.
276 static int do_dib_ddb(deark *c, lctx *d, struct picture_ctx *pctx, i64 pos1)
278 i64 compressed_size;
279 i64 hotspot_size;
280 i64 compressed_offset_rel, compressed_offset_abs;
281 i64 hotspot_offset_rel, hotspot_offset_abs;
282 i64 pos;
283 dbuf *pixels_final = NULL;
284 int retval = 0;
286 pos = pos1 + 2;
288 pctx->xdpi = fmtutil_hlp_get_cul_p(c->infile, &pos);
289 pctx->ydpi = fmtutil_hlp_get_cul_p(c->infile, &pos);
290 de_dbg(c, "dpi: %d"DE_CHAR_TIMES"%d", (int)pctx->xdpi, (int)pctx->ydpi);
291 if(pctx->xdpi<10 || pctx->ydpi<10 || pctx->xdpi>30000 || pctx->ydpi>30000) {
292 pctx->xdpi = 0;
293 pctx->ydpi = 0;
296 pctx->planes = fmtutil_hlp_get_cus_p(c->infile, &pos);
297 de_dbg(c, "planes: %d", (int)pctx->planes);
298 pctx->bitcount = fmtutil_hlp_get_cus_p(c->infile, &pos);
299 de_dbg(c, "bitcount: %d", (int)pctx->bitcount);
300 pctx->width = fmtutil_hlp_get_cul_p(c->infile, &pos);
301 pctx->height = fmtutil_hlp_get_cul_p(c->infile, &pos);
302 de_dbg_dimensions(c, pctx->width, pctx->height);
304 pctx->colors_used = fmtutil_hlp_get_cul_p(c->infile, &pos);
305 pctx->colors_important = fmtutil_hlp_get_cul_p(c->infile, &pos);
306 de_dbg(c, "colors used=%d, important=%d", (int)pctx->colors_used,
307 (int)pctx->colors_important);
308 if(pctx->colors_important==1) {
309 de_warn(c, "This image might have transparency, which is not supported");
310 pctx->colors_important = 0;
313 compressed_size = fmtutil_hlp_get_cul_p(c->infile, &pos);
314 hotspot_size = fmtutil_hlp_get_cul_p(c->infile, &pos);
315 compressed_offset_rel = de_getu32le_p(&pos);
316 compressed_offset_abs = pos1 + compressed_offset_rel;
317 hotspot_offset_rel = de_getu32le_p(&pos);
318 hotspot_offset_abs = pos1 + hotspot_offset_rel;
319 de_dbg(c, "bits offset=%"I64_FMT" (+%"I64_FMT"=%"I64_FMT"), size=%"I64_FMT,
320 compressed_offset_rel, pos1, compressed_offset_abs, compressed_size);
321 de_dbg(c, "hotspot offset=%"I64_FMT" (+%"I64_FMT"=%"I64_FMT"), size=%"I64_FMT,
322 hotspot_offset_rel, pos1, hotspot_offset_abs, hotspot_size);
324 if(pctx->picture_type==5) {
325 if(pctx->bitcount!=1 && pctx->bitcount!=4 &&pctx-> bitcount!=8)
327 de_err(c, "Unsupported bit count: %d", (int)pctx->bitcount);
328 goto done;
331 if(pctx->planes<1 || pctx->planes>8) {
332 de_err(c, "Unsupported planes: %d", (int)pctx->planes);
333 goto done;
336 else if(pctx->picture_type==6) {
337 if(pctx->bitcount!=1 && pctx->bitcount!=4 &&pctx-> bitcount!=8 &&
338 pctx->bitcount!=16 && pctx->bitcount!=24)
340 de_err(c, "Unsupported bit count: %d", (int)pctx->bitcount);
341 goto done;
344 if(pctx->planes!=1) {
345 de_err(c, "Unsupported planes: %d", (int)pctx->planes);
346 goto done;
350 if(!de_good_image_dimensions(c, pctx->width, pctx->height)) goto done;
352 if(compressed_offset_abs + compressed_size > c->infile->len) {
353 de_err(c, "Image goes beyond end of file");
354 goto done;
357 pctx->pal_offset = pos;
359 if(pctx->picture_type==5) {
360 pctx->pal_size_in_colors = 0;
362 else if(pctx->bitcount>8) {
363 pctx->pal_size_in_colors = 0;
365 else if(pctx->colors_used==0) {
366 pctx->pal_size_in_colors = ((i64)1)<<pctx->bitcount;
368 else {
369 pctx->pal_size_in_colors = pctx->colors_used;
370 if(pctx->pal_size_in_colors<1 ||
371 pctx->pal_size_in_colors>(((i64)1)<<pctx->bitcount))
373 goto done;
377 de_dbg(c, "image data at %"I64_FMT", len=%"I64_FMT, compressed_offset_abs,
378 compressed_size);
380 pctx->pal_size_in_bytes = 4*pctx->pal_size_in_colors;
382 if(pctx->picture_type==5) {
383 pctx->rowspan = (((pctx->width*pctx->bitcount +15)/16)*2);
385 else {
386 pctx->rowspan = (((pctx->width*pctx->bitcount +31)/32)*4);
388 pctx->final_image_size = pctx->height * pctx->planes * pctx->rowspan;
390 pixels_final = dbuf_create_membuf(c, 0, 0);
391 if(!do_uncompress_picture_data(c, d, pctx,
392 compressed_offset_abs, compressed_size,
393 pixels_final, pctx->final_image_size))
395 goto done;
398 if(pctx->picture_type==5) {
399 reconstruct_ddb(c, d, pctx, pixels_final);
401 else if(pctx->picture_type==6) {
402 reconstruct_bmp(c, d, pctx, pixels_final);
405 retval = 1;
406 done:
407 dbuf_close(pixels_final);
408 return retval;
411 static int do_wmf(deark *c, lctx *d, struct picture_ctx *pctx, i64 pos1)
413 i64 pos;
414 i64 mapping_mode;
415 i64 width, height;
416 i64 decompressed_size;
417 i64 compressed_size;
418 i64 hotspot_size;
419 i64 compressed_offset;
420 i64 hotspot_offset;
421 dbuf *pixels_final = NULL;
422 dbuf *outf = NULL;
423 int retval = 0;
425 pos = pos1 + 2;
427 mapping_mode = fmtutil_hlp_get_cus_p(c->infile, &pos);
428 width = de_getu16le(pos);
429 pos+=2;
430 height = de_getu16le(pos);
431 pos+=2;
432 de_dbg(c, "mapping mode: %d, nominal dimensions: %d"DE_CHAR_TIMES"%d",
433 (int)mapping_mode, (int)width, (int)height);
434 decompressed_size = fmtutil_hlp_get_cul_p(c->infile, &pos);
435 compressed_size = fmtutil_hlp_get_cul_p(c->infile, &pos);
436 hotspot_size = fmtutil_hlp_get_cul_p(c->infile, &pos);
437 compressed_offset = de_getu32le(pos);
438 pos+=4;
439 compressed_offset += pos1;
440 hotspot_offset = de_getu32le(pos);
441 pos+=4;
442 hotspot_offset += pos1;
444 de_dbg(c, "wmf offset=%d, size=%d", (int)compressed_offset,
445 (int)compressed_size);
446 de_dbg(c, "hotspot offset=%d, size=%d", (int)hotspot_offset,
447 (int)hotspot_size);
448 if(compressed_offset+compressed_size>c->infile->len) {
449 de_err(c, "WMF data goes beyond end of file");
450 goto done;
453 pixels_final = dbuf_create_membuf(c, decompressed_size, 0x1);
454 if(!do_uncompress_picture_data(c, d, pctx, compressed_offset, compressed_size,
455 pixels_final, decompressed_size))
457 goto done;
460 if(pixels_final->len != decompressed_size) {
461 de_warn(c, "Expected %d bytes after decompression, got %d",
462 (int)decompressed_size, (int)pixels_final->len);
465 outf = dbuf_create_output_file(c, "wmf", NULL, 0);
466 dbuf_copy(pixels_final, 0, pixels_final->len, outf);
468 retval = 1;
469 done:
470 dbuf_close(outf);
471 dbuf_close(pixels_final);
472 return retval;
475 static int do_picture(deark *c, lctx *d, i64 pic_index)
477 i64 pic_offset;
478 const char *ptname;
479 struct picture_ctx *pctx = NULL;
480 int retval = 0;
482 pctx = de_malloc(c, sizeof(struct picture_ctx));
483 de_dbg(c, "picture #%d", (int)pic_index);
484 de_dbg_indent(c, 1);
486 pic_offset = de_getu32le(d->shg_startpos + 4 + 4*pic_index);
487 pic_offset += d->shg_startpos;
488 de_dbg(c, "picture data at %d", (int)pic_offset);
489 if(pic_offset >= c->infile->len) {
490 goto done;
493 pctx->picture_type = de_getbyte(pic_offset);
494 pctx->packing_method = de_getbyte(pic_offset+1);
496 switch(pctx->picture_type) {
497 case 5: ptname="DDB"; break;
498 case 6: ptname="DIB"; break;
499 case 8: ptname="metafile"; break;
500 default: ptname="?";
502 de_dbg(c, "picture type: %d (%s)", (int)pctx->picture_type, ptname);
503 de_dbg(c, "packing method: %d", (int)pctx->packing_method);
505 if(pctx->picture_type==5 || pctx->picture_type==6) { // DDB or DIB
506 do_dib_ddb(c, d, pctx, pic_offset);
508 else if(pctx->picture_type==8) { // WMF
509 do_wmf(c, d, pctx, pic_offset);
511 else {
512 de_warn(c, "Unsupported picture type: %d", (int)pctx->picture_type);
515 retval = 1;
516 done:
517 de_free(c, pctx);
518 de_dbg_indent(c, -1);
519 return retval;
522 static void do_shg(deark *c, lctx *d)
524 i64 k;
526 d->num_pictures = de_getu16le(d->shg_startpos+2);
527 de_dbg(c, "number of pictures in file: %d", (int)d->num_pictures);
528 if(!de_good_image_count(c, d->num_pictures)) {
529 goto done;
532 for(k=0; k<d->num_pictures; k++) {
533 if(!do_picture(c, d, k)) {
534 goto done;
538 done:
542 static void de_run_shg(deark *c, de_module_params *mparams)
544 lctx *d = NULL;
546 d = de_malloc(c, sizeof(lctx));
548 d->shg_startpos = 0;
549 d->signature = de_getu16le(d->shg_startpos);
550 if(d->signature==0x506c) {
551 de_declare_fmt(c, "SHG");
553 else if(d->signature==0x706c) {
554 de_declare_fmt(c, "MRB");
556 else {
557 de_warn(c, "This is probably not an SHG/MRB file.");
560 do_shg(c, d);
562 de_free(c, d);
565 static int de_identify_shg(deark *c)
567 u8 buf[2];
568 de_read(buf, 0, 2);
569 if(buf[0]==0x6c && (buf[1]==0x50 || buf[1]==0x70)) {
570 return 50;
572 return 0;
575 void de_module_shg(deark *c, struct deark_module_info *mi)
577 mi->id = "shg";
578 mi->desc = "SHG (Segmented Hypergraphics), MRB (Multiple Resolution Bitmap)";
579 mi->run_fn = de_run_shg;
580 mi->identify_fn = de_identify_shg;