Minor refactoring of the IFF and box-format parsers
[deark.git] / modules / pcpaint.c
blobcb484ffc5d1fa6a9cb560cdc66422efa1b297b03
1 // This file is part of Deark.
2 // Copyright (C) 2016 Jason Summers
3 // See the file COPYING for terms of use.
5 // PCPaint PIC and CLP format
7 #include <deark-config.h>
8 #include <deark-private.h>
9 DE_DECLARE_MODULE(de_module_pcpaint);
11 struct pal_info {
12 i64 edesc;
13 i64 esize;
14 u8 *data;
17 struct localctx_struct;
18 typedef struct localctx_struct lctx;
20 enum screen_mode_type_enum {
21 SCREENMODETYPE_UNKNOWN = 0,
22 SCREENMODETYPE_BITMAP,
23 SCREENMODETYPE_TEXT
26 typedef void (*decoder_fn_type)(deark *c, lctx *d);
28 struct localctx_struct {
29 #define FMT_PIC 1
30 #define FMT_CLP 2
31 int file_fmt;
32 int ver;
33 de_encoding input_encoding;
34 int opt_keep_invis_chars;
35 de_finfo *fi;
36 i64 header_size;
37 i64 npwidth, height;
38 i64 pdwidth;
39 u8 plane_info;
40 u8 palette_flag;
41 u8 video_mode; // 0 = unknown
42 struct pal_info pal_info_mainfile;
43 struct pal_info pal_info_palfile;
44 struct pal_info *pal_info_to_use; // Points to _mainfile or _palfile
45 i64 num_rle_blocks;
46 dbuf *unc_pixels;
47 decoder_fn_type decoder_fn;
48 enum screen_mode_type_enum screen_mode_type;
51 static void set_density(deark *c, lctx *d)
53 if(!d->fi) return;
55 switch(d->video_mode) {
56 case 'A': // 320x200
57 case 'B':
58 case 'I':
59 case 'J':
60 case 'L':
61 d->fi->density.code = DE_DENSITY_UNK_UNITS;
62 d->fi->density.xdens = 240.0;
63 d->fi->density.ydens = 200.0;
64 break;
65 case 'H': // 720x348 (Hercules)
66 case 'N':
67 d->fi->density.code = DE_DENSITY_UNK_UNITS;
68 // Various sources suggest aspect ratios of 1.46, 1.55, 1.59, ...
69 d->fi->density.xdens = 155.0;
70 d->fi->density.ydens = 100.0;
71 break;
72 case 'E': // 640x350
73 case 'F':
74 case 'G':
75 d->fi->density.code = DE_DENSITY_UNK_UNITS;
76 d->fi->density.xdens = 480.0;
77 d->fi->density.ydens = 350.0;
78 break;
79 case 'K':
80 case 'R':
81 d->fi->density.code = DE_DENSITY_UNK_UNITS;
82 d->fi->density.xdens = 480.0;
83 d->fi->density.ydens = 400.0;
84 break;
85 case 'C':
86 case 'D':
87 d->fi->density.code = DE_DENSITY_UNK_UNITS;
88 d->fi->density.xdens = 480.0;
89 d->fi->density.ydens = 200.0;
90 break;
94 static void decode_text(deark *c, lctx *d)
96 i64 width_in_chars;
97 struct de_char_context *charctx = NULL;
98 struct de_char_screen *screen;
99 i64 i, j, k;
100 u8 ch, attr;
101 struct de_encconv_state es;
103 // TODO: This might not work for monochrome text mode (d->video_mode==0x32).
105 width_in_chars = d->npwidth / 2;
107 charctx = de_malloc(c, sizeof(struct de_char_context));
108 charctx->no_density = 1;
109 charctx->nscreens = 1;
110 charctx->screens = de_mallocarray(c, charctx->nscreens, sizeof(struct de_char_screen*));
111 charctx->screens[0] = de_malloc(c, sizeof(struct de_char_screen));
112 screen = charctx->screens[0];
114 screen->width = width_in_chars;
115 screen->height = d->height;
117 de_dbg(c, "dimensions: %d"DE_CHAR_TIMES"%d characters", (int)screen->width, (int)screen->height);
119 if(screen->height<1) goto done;
121 screen->cell_rows = de_mallocarray(c, screen->height, sizeof(struct de_char_cell*));
122 de_encconv_init(&es, d->input_encoding);
124 for(j=0; j<screen->height; j++) {
125 i64 j2;
127 j2 = screen->height-1-j;
128 screen->cell_rows[j2] = de_mallocarray(c, screen->width, sizeof(struct de_char_cell));
130 for(i=0; i<screen->width; i++) {
131 ch = dbuf_getbyte(d->unc_pixels, j*d->npwidth + i*2);
132 attr = dbuf_getbyte(d->unc_pixels, j*d->npwidth + i*2 + 1);
134 screen->cell_rows[j2][i].fgcol = (u32)(attr & 0x0f);
135 screen->cell_rows[j2][i].bgcol = (u32)((attr & 0xf0) >> 4);
137 // In "blank" regions, some files have nonsense characters, with the fg
138 // and bg colors the same. We turn them into spaces, so that copy/paste
139 // works right with our HTML output.
140 if(ch==0 ||
141 (screen->cell_rows[j2][i].fgcol==screen->cell_rows[j2][i].bgcol &&
142 !d->opt_keep_invis_chars))
144 screen->cell_rows[j2][i].codepoint = 32;
145 screen->cell_rows[j2][i].codepoint_unicode = 32;
147 else {
148 screen->cell_rows[j2][i].codepoint = (i32)ch;
149 screen->cell_rows[j2][i].codepoint_unicode = de_char_to_unicode_ex((i32)ch, &es);
154 for(k=0; k<16; k++) {
155 // TODO: Is this always the right palette? Maybe we can't ignore ->edesc
156 charctx->pal[k] = de_palette_pc16((int)k);
159 de_char_output_to_file(c, charctx);
161 done:
162 de_free_charctx(c, charctx);
165 // Create a standard RGB palette from raw RGB palette data
166 static void make_rgb_palette(deark *c, lctx *d, u32 *pal, i64 num_entries)
168 i64 k;
169 u8 cr1, cg1, cb1;
170 u8 cr2, cg2, cb2;
171 int has_8bit_samples = 0;
172 char tmps[64];
174 // Pre-scan
175 for(k=0; k<num_entries; k++) {
176 if(3*k+2 >= d->pal_info_to_use->esize) break;
177 cr1 = d->pal_info_to_use->data[3*k+0];
178 cg1 = d->pal_info_to_use->data[3*k+1];
179 cb1 = d->pal_info_to_use->data[3*k+2];
180 if(cr1>63 || cg1>63 || cb1>63) {
181 de_dbg(c, "detected 8-bit palette samples");
182 has_8bit_samples = 1;
183 break;
187 // For real
188 de_dbg_indent(c, 1);
189 for(k=0; k<num_entries; k++) {
190 if(3*k+2 >= d->pal_info_to_use->esize) break;
191 cr1 = d->pal_info_to_use->data[3*k+0];
192 cg1 = d->pal_info_to_use->data[3*k+1];
193 cb1 = d->pal_info_to_use->data[3*k+2];
195 if(has_8bit_samples) {
196 cr2 = (cr1<<2) | (cr1>>6);
197 cg2 = (cg1<<2) | (cg1>>6);
198 cb2 = (cb1<<2) | (cb1>>6);
199 pal[k] = DE_MAKE_RGB(cr2, cg2, cb2);
200 de_dbg_pal_entry(c, k, pal[k]);
202 else {
203 cr2 = de_scale_63_to_255(cr1);
204 cg2 = de_scale_63_to_255(cg1);
205 cb2 = de_scale_63_to_255(cb1);
206 pal[k] = DE_MAKE_RGB(cr2, cg2, cb2);
207 de_snprintf(tmps, sizeof(tmps), "(%2d,%2d,%2d) "DE_CHAR_RIGHTARROW" ",
208 (int)cr1, (int)cg1, (int)cb1);
209 de_dbg_pal_entry2(c, k, pal[k], tmps, NULL, NULL);
212 de_dbg_indent(c, -1);
215 static void decode_egavga16(deark *c, lctx *d)
217 u32 pal[16];
218 i64 i, j;
219 i64 k;
220 i64 plane;
221 u8 z[4];
222 i64 src_rowspan;
223 i64 src_planespan;
224 int palent;
225 de_bitmap *img = NULL;
226 char tmps[32];
228 de_dbg(c, "image type: 16-color EGA/VGA");
229 de_zeromem(pal, sizeof(pal));
231 // Read the palette
232 if(d->pal_info_to_use->edesc==0) {
233 de_dbg(c, "palette type: standard 16-color palette (no palette in file)");
234 for(k=0; k<16; k++) {
235 pal[k] = de_palette_pc16((int)k);
238 else if(d->pal_info_to_use->edesc==3) {
239 // An EGA palette. Indexes into the standard EGA
240 // 64-color palette.
241 de_dbg(c, "palette type: 16 indices into standard EGA 64-color palette");
242 for(k=0; k<16; k++) {
243 if(k >= d->pal_info_to_use->esize) break;
244 pal[k] = de_palette_ega64(d->pal_info_to_use->data[k]);
245 de_snprintf(tmps, sizeof(tmps), "%2d ", (int)d->pal_info_to_use->data[k]);
246 de_dbg_pal_entry2(c, k, pal[k], tmps, NULL, NULL);
249 else { // assuming edesc==5
250 de_dbg(c, "palette type: 16-color palette (in file)");
251 make_rgb_palette(c, d, pal, 16);
254 if(d->plane_info==0x31) {
255 d->pdwidth = de_pad_to_n(d->npwidth, 8);
256 src_rowspan = d->pdwidth/8;
257 src_planespan = src_rowspan*d->height;
259 else {
260 d->pdwidth = de_pad_to_2(d->npwidth);
261 src_rowspan = d->pdwidth/2;
262 src_planespan = 0;
265 img = de_bitmap_create2(c, d->npwidth, d->pdwidth, d->height, 3);
267 for(j=0; j<d->height; j++) {
268 for(i=0; i<d->pdwidth; i++) {
269 if(d->plane_info==0x31) {
270 for(plane=0; plane<4; plane++) {
271 z[plane] = de_get_bits_symbol(d->unc_pixels, 1, plane*src_planespan + j*src_rowspan, i);
273 palent = z[0] + 2*z[1] + 4*z[2] + 8*z[3];
275 else {
276 palent = de_get_bits_symbol(d->unc_pixels, 4, j*src_rowspan, i);
278 de_bitmap_setpixel_rgb(img, i, j, pal[palent]);
282 de_bitmap_write_to_file_finfo(img, d->fi, DE_CREATEFLAG_FLIP_IMAGE);
284 de_bitmap_destroy(img);
287 static void decode_vga256(deark *c, lctx *d)
289 u32 pal[256];
290 i64 k;
291 de_bitmap *img = NULL;
293 de_dbg(c, "image type: 256-color");
294 de_zeromem(pal, sizeof(pal));
296 // Read the palette
297 if(d->pal_info_to_use->edesc==0) {
298 de_dbg(c, "palette type: standard 256-color palette (no palette in file)");
299 for(k=0; k<256; k++) {
300 pal[k] = de_palette_vga256((int)k);
303 else {
304 de_dbg(c, "palette type: 256-color palette (in file)");
305 de_dbg(c, "decoding palette");
306 make_rgb_palette(c, d, pal, 256);
309 img = de_bitmap_create2(c, d->npwidth, d->pdwidth, d->height, 3);
311 de_convert_image_paletted(d->unc_pixels, 0,
312 8, img->width, pal, img, 0);
314 de_bitmap_write_to_file_finfo(img, d->fi, DE_CREATEFLAG_FLIP_IMAGE);
316 de_bitmap_destroy(img);
319 static void decode_bilevel(deark *c, lctx *d)
321 i64 src_rowspan;
322 u32 pal[2];
323 int is_grayscale;
324 i64 edesc = d->pal_info_to_use->edesc;
325 de_bitmap *img = NULL;
327 de_dbg(c, "image type: bilevel");
329 if(!d->unc_pixels) goto done;
331 pal[0] = DE_STOCKCOLOR_BLACK;
332 pal[1] = DE_STOCKCOLOR_WHITE; // default
334 if(edesc!=0 && edesc!=4 && edesc!=5) {
335 de_warn(c, "The colors in this image might not be handled correctly (edesc=%d)",
336 (int)edesc);
339 if(edesc==4 || edesc==5) {
340 make_rgb_palette(c, d, pal, 2);
343 // PCPaint's CGA and EGA 2-color modes used gray shade 170 instead of
344 // white (255). Maybe they should be interpreted as white, but for
345 // historical accuracy I'll go with gray170.
346 if(edesc==0 && (d->video_mode==0x43 || d->video_mode==0x45)) {
347 pal[1] = DE_MAKE_GRAY(170);
350 d->pdwidth = de_pad_to_n(d->npwidth, 8);
351 src_rowspan = d->pdwidth/8;
352 is_grayscale = de_is_grayscale_palette(pal, 2);
353 img = de_bitmap_create2(c, d->npwidth, d->pdwidth, d->height, is_grayscale?1:3);
355 de_convert_image_paletted(d->unc_pixels, 0,
356 1, src_rowspan, pal, img, 0);
358 de_bitmap_write_to_file_finfo(img, d->fi, DE_CREATEFLAG_FLIP_IMAGE);
360 done:
361 de_bitmap_destroy(img);
364 static void decode_cga4(deark *c, lctx *d)
366 i64 k;
367 i64 src_rowspan;
368 u32 pal[4];
369 u8 pal_id = 0;
370 u8 border_col = 0;
371 de_bitmap *img = NULL;
373 de_dbg(c, "image type: CGA 4-color");
375 if(!d->unc_pixels) goto done;
377 if(d->pal_info_to_use->edesc==1) {
378 // Image includes information about which CGA 4-color palette it uses.
380 // This assumes PIC format. That should be the case, since edesc will
381 // be zero for CLP format (unless we are reading the palette from a separate
382 // PIC file).
383 if(d->pal_info_to_use->esize >= 1)
384 pal_id = d->pal_info_to_use->data[0];
385 if(d->pal_info_to_use->esize >= 2)
386 border_col = d->pal_info_to_use->data[1];
387 de_dbg(c, "pal_id=0x%02x border=0x%02x", pal_id, border_col);
389 for(k=0; k<4; k++) {
390 pal[k] = de_palette_pcpaint_cga4(pal_id, (int)k);
393 // Replace the first palette color with the border/background color.
394 pal[0] = de_palette_pc16(border_col);
396 else {
397 // No palette specified in the file. Use palette #2 by default.
398 for(k=0; k<4; k++) {
399 pal[k] = de_palette_pcpaint_cga4(2, (int)k);
403 d->pdwidth = de_pad_to_4(d->npwidth);
404 src_rowspan = d->pdwidth/4;
405 img = de_bitmap_create2(c, d->npwidth, d->pdwidth, d->height, 3);
407 de_convert_image_paletted(d->unc_pixels, 0,
408 2, src_rowspan, pal, img, 0);
410 de_bitmap_write_to_file_finfo(img, d->fi, DE_CREATEFLAG_FLIP_IMAGE);
412 done:
413 de_bitmap_destroy(img);
416 // decompress one block
417 // Writes decompressed bytes to d->unc_pixels.
418 // packed_data_size does not include header size.
419 // Returns 0 on error.
420 static int decompress_block(deark *c, lctx *d,
421 i64 pos1, i64 packed_data_size, u8 run_marker)
423 i64 pos = pos1;
424 i64 end_of_this_block;
425 u8 x;
426 i64 run_length;
428 end_of_this_block = pos1 + packed_data_size;
430 while(pos<end_of_this_block) {
431 x = de_getbyte_p(&pos);
432 if(x!=run_marker) {
433 // An non-compressed part of the image
434 dbuf_writebyte(d->unc_pixels, x);
435 continue;
438 // A compressed run.
439 x = de_getbyte_p(&pos);
440 if(x!=0) {
441 // If nonzero, this byte is the run length.
442 run_length = (i64)x;
444 else {
445 // If zero, it is followed by a 16-bit run length
446 run_length = de_getu16le_p(&pos);
449 // Read the byte value to repeat (run_length) times.
450 x = de_getbyte_p(&pos);
451 dbuf_write_run(d->unc_pixels, x, run_length);
454 return 1;
457 // Decompress multiple blocks of compressed pixels.
458 // This is for PIC format only.
459 static int decompress_pixels(deark *c, lctx *d)
461 i64 pos;
462 i64 i;
463 i64 packed_block_size;
464 i64 unpacked_block_size;
465 u8 run_marker;
466 int retval = 1;
467 i64 end_of_this_block;
468 int saved_indent_level;
470 de_dbg_indent_save(c, &saved_indent_level);
471 if(d->num_rle_blocks<1) {
472 // Not compressed
473 retval = 1;
474 goto done;
477 d->unc_pixels = dbuf_create_membuf(c, 16384, 0);
478 dbuf_set_length_limit(d->unc_pixels, (d->pdwidth+7) * d->height);
480 de_dbg(c, "decompressing image");
481 de_dbg_indent(c, 1);
482 pos = d->header_size;
484 for(i=0; i<d->num_rle_blocks; i++) {
485 de_dbg3(c, "block #%d at %"I64_FMT, (int)i, pos);
486 de_dbg_indent(c, 1);
487 // start_of_this_block = pos;
488 packed_block_size = de_getu16le(pos);
489 // block size includes the 5-byte header, so it can't be < 5.
490 if(packed_block_size<5) packed_block_size=5;
491 end_of_this_block = pos + packed_block_size; // Remember where this block ends
492 unpacked_block_size = de_getu16le(pos+2);
493 run_marker = de_getbyte(pos+4);
494 pos+=5;
496 de_dbg3(c, "packed size: %"I64_FMT, packed_block_size);
497 de_dbg3(c, "unpacked size: %"I64_FMT, unpacked_block_size);
498 de_dbg3(c, "run marker: 0x%02x", (UI)run_marker);
500 if(!decompress_block(c, d, pos, packed_block_size-5, run_marker)) {
501 goto done;
503 de_dbg_indent(c, -1);
505 pos = end_of_this_block;
508 de_dbg_indent(c, -1);
509 de_dbg(c, "decompressed to %"I64_FMT" bytes", d->unc_pixels->len);
510 retval = 1;
512 done:
513 de_dbg_indent_restore(c, saved_indent_level);
514 return retval;
517 static int do_read_palette_data(deark *c, lctx *d, dbuf *f, struct pal_info *palinfo)
519 palinfo->edesc = dbuf_getu16le(f, 13);
520 palinfo->esize = dbuf_getu16le(f, 15);
521 palinfo->data = de_malloc(c, palinfo->esize);
522 dbuf_read(f, palinfo->data, 17, palinfo->esize);
523 return 1;
526 // Figure out if we're supposed to read the palette from an alternate file.
527 // If so, open it and read a few fields from it. Modify settings so that
528 // we will read the palette from the alternate file.
529 // The palette file is assumed to be in PIC format.
530 static int do_read_alt_palette_file(deark *c, lctx *d)
532 const char *palfn;
533 dbuf *palfile = NULL;
534 int retval = 0;
535 i64 magic;
537 palfn = de_get_ext_option(c, "palfile");
538 if(!palfn) palfn = de_get_ext_option(c, "file2");
539 if(!palfn) {
540 retval = 1;
541 goto done;
544 de_dbg(c, "[reading palette from alternate file]");
546 palfile = dbuf_open_input_file(c, palfn);
547 if(!palfile) {
548 goto done;
551 magic = dbuf_getu16le(palfile, 0);
552 if(magic!=0x1234) {
553 de_err(c, "Palette file is not in PIC format.");
554 goto done;
557 do_read_palette_data(c, d, palfile, &d->pal_info_palfile);
559 if(d->pal_info_palfile.edesc==0) {
560 de_warn(c, "Palette file does not contain palette information.");
561 retval = 1;
562 goto done;
565 d->pal_info_to_use = &d->pal_info_palfile;
566 retval = 1;
568 done:
569 dbuf_close(palfile);
570 return retval;
573 // Determine if we can decode this type of image.
574 // Sets d->decoder_fn and d->screen_mode_type.
575 // If image can't be decoded, prints an error and returns 0.
576 static int do_set_up_decoder(deark *c, lctx *d)
578 i64 edesc;
580 edesc = d->pal_info_to_use->edesc; // For brevity
582 if(d->video_mode>='0' && d->video_mode<='3') {
583 d->screen_mode_type = SCREENMODETYPE_TEXT;
584 d->decoder_fn = decode_text;
586 else if(d->plane_info==0x01) {
587 // Expected video mode(s): 0x43, 0x45, 0x48, 0x4f, 0x50, 0x55
588 // CGA or EGA or VGA or Hercules 2-color
589 d->screen_mode_type = SCREENMODETYPE_BITMAP;
590 d->decoder_fn = decode_bilevel;
592 else if(d->plane_info==0x02 && (edesc==0 || edesc==1)) {
593 // Expected video mode(s): 0x41
594 d->screen_mode_type = SCREENMODETYPE_BITMAP;
595 d->decoder_fn = decode_cga4;
597 else if(d->plane_info==0x04 && edesc==3) {
598 d->screen_mode_type = SCREENMODETYPE_BITMAP;
599 d->decoder_fn = decode_egavga16;
601 else if((d->plane_info==0x04 || d->plane_info==0x31) &&
602 (edesc==0 || edesc==3 || edesc==5))
604 // Expected video mode(s): 0x4d, 0x47
605 d->screen_mode_type = SCREENMODETYPE_BITMAP;
606 d->decoder_fn = decode_egavga16;
608 else if(d->plane_info==0x08 && (edesc==0 || edesc==4)) {
609 // Expected video mode(s): 0x4c
610 d->screen_mode_type = SCREENMODETYPE_BITMAP;
611 d->decoder_fn = decode_vga256;
614 if(d->decoder_fn) {
615 de_dbg2(c, "image type: evideo=0x%02x, bitsinf=0x%02x, edesc=%d",
616 d->video_mode, d->plane_info, (int)edesc);
617 return 1;
620 de_err(c, "This type of PCPaint %s is not supported (evideo=0x%02x, bitsinf=0x%02x, edesc=%d)",
621 (d->file_fmt==FMT_CLP) ? "CLP" : "PIC",
622 d->video_mode, d->plane_info, (int)edesc);
624 return 0;
627 static void de_run_pcpaint_pic(deark *c, lctx *d, de_module_params *mparams)
629 int saved_indent_level;
631 de_dbg_indent_save(c, &saved_indent_level);
633 de_declare_fmt(c, "PCPaint PIC");
635 d->fi = de_finfo_create(c);
637 de_dbg(c, "header at %d", 0);
638 de_dbg_indent(c, 1);
640 d->npwidth = de_getu16le(2);
641 d->pdwidth = d->npwidth; // default
642 d->height = de_getu16le(4);
643 de_dbg_dimensions(c, d->npwidth, d->height);
645 d->plane_info = de_getbyte(10);
646 d->palette_flag = de_getbyte(11);
648 de_dbg(c, "plane info: 0x%02x", (int)d->plane_info);
649 de_dbg(c, "palette flag: 0x%02x", (int)d->palette_flag);
651 if(d->palette_flag==0xff) {
652 d->ver = 2;
655 if(d->ver!=2) {
656 de_err(c, "This version of PCPaint PIC is not supported");
657 goto done;
660 d->video_mode = de_getbyte(12);
661 de_dbg(c, "video mode: 0x%02x", (int)d->video_mode);
663 do_read_palette_data(c, d, c->infile, &d->pal_info_mainfile);
664 de_dbg(c, "edesc: %d", (int)d->pal_info_mainfile.edesc);
665 de_dbg(c, "esize: %d", (int)d->pal_info_mainfile.esize);
667 if(d->pal_info_mainfile.esize>0) {
668 de_dbg(c, "palette or other info at %d", 17);
671 set_density(c, d);
673 d->pal_info_to_use = &d->pal_info_mainfile; // tentative
674 if(!do_read_alt_palette_file(c, d)) goto done;
676 d->num_rle_blocks = de_getu16le(17+d->pal_info_mainfile.esize);
678 d->header_size = 17 + d->pal_info_mainfile.esize + 2;
680 de_dbg(c, "num rle blocks: %d", (int)d->num_rle_blocks);
681 de_dbg_indent(c, -1);
683 de_dbg(c, "image data at %d", (int)d->header_size);
684 de_dbg_indent(c, 1);
685 if(!do_set_up_decoder(c, d)) goto done;
686 if(d->screen_mode_type==SCREENMODETYPE_BITMAP) {
687 if(!de_good_image_dimensions(c, d->npwidth, d->height)) goto done;
690 if(d->num_rle_blocks>0) {
691 // Image is compressed.
692 decompress_pixels(c, d);
694 else {
695 // Image is not compressed.
696 d->unc_pixels = dbuf_open_input_subfile(c->infile, d->header_size,
697 c->infile->len-d->header_size);
700 d->decoder_fn(c, d);
702 done:
703 de_dbg_indent_restore(c, saved_indent_level);
706 static void de_run_pcpaint_clp(deark *c, lctx *d, de_module_params *mparams)
708 i64 file_size;
709 u8 run_marker;
710 int is_compressed;
711 int saved_indent_level;
713 de_dbg_indent_save(c, &saved_indent_level);
715 de_declare_fmt(c, "PCPaint CLP");
717 de_dbg(c, "header at %d", 0);
718 de_dbg_indent(c, 1);
720 file_size = de_getu16le(0);
721 de_dbg(c, "reported file size: %"I64_FMT, file_size);
722 if(file_size != c->infile->len) {
723 if(file_size==0x1234) {
724 de_warn(c, "This is probably a .PIC file, not a CLIP file.");
726 else {
727 de_warn(c, "Reported file size (%"I64_FMT") does not equal actual file size (%"I64_FMT"). "
728 "Format may not be correct.", file_size, c->infile->len);
732 d->npwidth = de_getu16le(2);
733 d->pdwidth = d->npwidth; // default
734 d->height = de_getu16le(4);
735 de_dbg_dimensions(c, d->npwidth, d->height);
737 d->plane_info = de_getbyte(10);
739 is_compressed = (d->plane_info==0xff);
741 if(is_compressed) {
742 d->header_size = 13;
743 d->plane_info = de_getbyte(11);
745 else {
746 d->header_size = 11;
748 de_dbg(c, "compressed: %d", (int)is_compressed);
749 de_dbg(c, "plane info: 0x%02x", (int)d->plane_info);
751 de_dbg_indent(c, -1);
753 // The colors probably won't be right, but we have no way to tell what palette
754 // is used by a CLP image.
755 d->video_mode = 0;
756 d->pal_info_mainfile.edesc = 0;
757 d->pal_info_mainfile.esize = 0;
759 d->pal_info_to_use = &d->pal_info_mainfile; // tentative
760 if(!do_read_alt_palette_file(c, d)) goto done;
762 de_dbg(c, "image data at %"I64_FMT, d->header_size);
763 de_dbg_indent(c, 1);
764 if(!do_set_up_decoder(c, d)) goto done;
766 if(is_compressed) {
767 run_marker = de_getbyte(12);
768 de_dbg3(c, "run marker: 0x%02x", (UI)run_marker);
770 de_dbg(c, "decompressing image");
771 de_dbg_indent(c, 1);
772 d->unc_pixels = dbuf_create_membuf(c, 16384, 0);
773 dbuf_set_length_limit(d->unc_pixels, (d->pdwidth+7) * d->height);
775 if(!decompress_block(c, d, d->header_size,
776 c->infile->len - d->header_size, run_marker))
778 goto done;
780 de_dbg_indent(c, -1);
781 de_dbg(c, "decompressed to %"I64_FMT" bytes", d->unc_pixels->len);
783 else {
784 d->unc_pixels = dbuf_open_input_subfile(c->infile,
785 d->header_size, c->infile->len-d->header_size);
788 d->decoder_fn(c, d);
790 done:
791 de_dbg_indent_restore(c, saved_indent_level);
794 // Dispatch to either pcpaint_pic or pcpaint_clp.
795 static void de_run_pcpaint(deark *c, de_module_params *mparams)
797 // 0=unknown, 1=pic, 2=clp
798 const char *pcpaintfmt;
799 u8 buf[16];
800 lctx *d;
802 d = de_malloc(c, sizeof(lctx));
804 d->input_encoding = de_get_input_encoding(c, NULL, DE_ENCODING_CP437_G);
806 pcpaintfmt = de_get_ext_option(c, "pcpaint:fmt");
807 if(pcpaintfmt) {
808 if(!de_strcmp(pcpaintfmt, "pic")) {
809 d->file_fmt = FMT_PIC;
811 else if(!de_strcmp(pcpaintfmt, "clp")) {
812 d->file_fmt = FMT_CLP;
814 else if(!de_strcmp(pcpaintfmt, "clip")) {
815 d->file_fmt = FMT_CLP;
819 if(!d->file_fmt) {
820 // File subtype not given by user. Try to detect it.
821 de_read(buf, 0, 16);
822 if(buf[0]==0x34 && buf[1]==0x12) {
823 if(c->infile->len==0x1234) {
824 // Pathological case where both formats could start with 0x1234.
825 if(buf[10]==0xff) { // definitely a compressed CLP
826 d->file_fmt = FMT_CLP;
828 else {
829 de_warn(c, "Format can't be reliably identified. Try \"-opt pcpaint:fmt=clp\" if necessary.");
830 d->file_fmt = FMT_PIC;
833 else {
834 d->file_fmt = FMT_PIC;
837 else {
838 d->file_fmt = FMT_CLP;
842 d->opt_keep_invis_chars = de_get_ext_option_bool(c, "pcpaint:invistext", 0);
844 if(d->file_fmt==FMT_CLP) {
845 de_run_pcpaint_clp(c, d, mparams);
847 else {
848 de_run_pcpaint_pic(c, d, mparams);
851 if(d->unc_pixels) dbuf_close(d->unc_pixels);
852 de_finfo_destroy(c, d->fi);
853 de_free(c, d->pal_info_mainfile.data);
854 de_free(c, d->pal_info_palfile.data);
855 de_free(c, d);
858 static int de_identify_pcpaint(deark *c)
860 u8 buf[12];
861 int pic_ext, clp_ext;
862 i64 x;
864 pic_ext = de_input_file_has_ext(c, "pic");
866 de_read(buf, 0, 12);
867 if(buf[0]==0x34 && buf[1]==0x12 && buf[11]==0xff) {
868 return pic_ext ? 100 : 50;
871 clp_ext = de_input_file_has_ext(c, "clp");
872 if(clp_ext) {
873 x = de_getu16le_direct(&buf[0]);
874 if(x==c->infile->len) {
875 return 50;
879 return 0;
882 static void de_help_pcpaint(deark *c)
884 de_msg(c, "-file2 <file.pic> : PIC file to read the palette from");
885 de_msg(c, "-opt pcpaint:fmt=pic : Assume PIC format");
886 de_msg(c, "-opt pcpaint:fmt=clp : Assume CLP format");
889 void de_module_pcpaint(deark *c, struct deark_module_info *mi)
891 mi->id = "pcpaint";
892 mi->desc = "PCPaint PIC or CLP image";
893 mi->run_fn = de_run_pcpaint;
894 mi->identify_fn = de_identify_pcpaint;
895 mi->help_fn = de_help_pcpaint;