Added support for cp932 (Shift-JIS) conversion
[deark.git] / modules / bintext.c
blobdb5163849774a513e63f57e548f47e247b19b209
1 // This file is part of Deark.
2 // Copyright (C) 2016 Jason Summers
3 // See the file COPYING for terms of use.
5 // XBIN character graphics
6 // "Binary Text" character graphics
7 // ArtWorx ADF character graphics
8 // Etc.
10 #include <deark-config.h>
11 #include <deark-private.h>
12 #include <deark-fmtutil.h>
14 DE_DECLARE_MODULE(de_module_xbin);
15 DE_DECLARE_MODULE(de_module_bintext);
16 DE_DECLARE_MODULE(de_module_artworx_adf);
17 DE_DECLARE_MODULE(de_module_icedraw);
18 DE_DECLARE_MODULE(de_module_thedraw_com);
19 DE_DECLARE_MODULE(de_module_aciddraw_com);
20 DE_DECLARE_MODULE(de_module_grabber);
22 typedef struct localctx_struct_bintext {
23 u8 errflag;
24 u8 need_errmsg;
25 u8 has_palette, has_font, compression, has_512chars;
27 i64 font_height;
28 i64 font_data_len;
29 u8 *font_data;
30 int is_standard_font;
31 struct de_bitmap_font *font;
32 struct fmtutil_char_simplectx csctx;
33 } lctx;
35 static void free_lctx(deark *c, lctx *d)
37 if(d->font) {
38 de_free(c, d->font->char_array);
39 de_destroy_bitmap_font(c, d->font);
41 de_free(c, d->font_data);
42 de_free(c, d);
45 static void read_and_discard_SAUCE(deark *c)
47 struct de_SAUCE_detection_data sdd;
49 fmtutil_detect_SAUCE(c, c->infile, &sdd, 0x1);
50 if(sdd.has_SAUCE) {
51 // Read the SAUCE record if present, just for the debugging info.
52 struct de_SAUCE_info *si = NULL;
54 si = fmtutil_create_SAUCE(c);
56 de_dbg_indent(c, 1);
57 fmtutil_handle_SAUCE(c, c->infile, si);
58 de_dbg_indent(c, -1);
60 fmtutil_free_SAUCE(c, si);
64 static void xbin_decompress_data(deark *c, lctx *d, i64 pos1, dbuf *unc_data)
66 i64 pos;
67 u8 cmprtype;
68 i64 count;
69 i64 xpos, ypos;
70 u8 b;
71 u8 b1, b2;
72 i64 k;
74 pos = pos1;
76 xpos = 0; ypos = 0;
78 while(pos < c->infile->len) {
79 if(xpos >= d->csctx.width_in_chars) {
80 ypos++;
81 xpos = 0;
83 if(ypos >= d->csctx.height_in_chars) {
84 break;
87 b = de_getbyte_p(&pos);
88 cmprtype = b>>6;
89 count = (i64)(b&0x3f) +1;
91 switch(cmprtype) {
92 case 0: // Uncompressed
93 dbuf_copy(c->infile, pos, count*2, unc_data);
94 pos += count*2;
95 break;
96 case 1: // Character compression
97 b1 = de_getbyte_p(&pos); // character code
98 for(k=0; k<count; k++) {
99 b2 = de_getbyte_p(&pos); // attribute code
100 dbuf_writebyte(unc_data, b1);
101 dbuf_writebyte(unc_data, b2);
103 break;
104 case 2: // Attribute compression
105 b2 = de_getbyte_p(&pos); // attribute code
106 for(k=0; k<count; k++) {
107 b1 = de_getbyte_p(&pos); // character code
108 dbuf_writebyte(unc_data, b1);
109 dbuf_writebyte(unc_data, b2);
111 break;
112 case 3: // Character/Attribute compression
113 b1 = de_getbyte_p(&pos); // character code
114 b2 = de_getbyte_p(&pos); // attribute code
115 for(k=0; k<count; k++) {
116 dbuf_writebyte(unc_data, b1);
117 dbuf_writebyte(unc_data, b2);
119 break;
122 xpos += count;
126 static void do_read_palette(deark *c, lctx *d,struct de_char_context *charctx,
127 i64 pos, int adf_style)
129 i64 k;
130 u8 cr1, cg1, cb1;
131 u8 cr2, cg2, cb2;
132 i64 cpos;
133 char tmps[64];
135 de_dbg(c, "palette at %"I64_FMT, pos);
137 for(k=0; k<16; k++) {
138 i64 idx = k;
140 if(adf_style) {
141 if(k>=8) idx = 48+k;
142 else if(k==6) idx = 20;
144 cpos = pos + idx*3;
145 cr1 = de_getbyte(cpos);
146 cg1 = de_getbyte(cpos+1);
147 cb1 = de_getbyte(cpos+2);
148 cr2 = de_scale_63_to_255(cr1);
149 cg2 = de_scale_63_to_255(cg1);
150 cb2 = de_scale_63_to_255(cb1);
151 charctx->pal[k] = DE_MAKE_RGB(cr2, cg2, cb2);
152 de_snprintf(tmps, sizeof(tmps), "(%2d,%2d,%2d) "DE_CHAR_RIGHTARROW" ",
153 (int)cr1, (int)cg1, (int)cb1);
154 de_dbg_pal_entry2(c, k, charctx->pal[k], tmps, NULL, NULL);
158 static void do_extract_font(deark *c, lctx *d)
160 de_finfo *fi = NULL;
162 if(!d->has_font || !d->font) return;
163 fi = de_finfo_create(c);
164 de_finfo_set_name_from_sz(c, fi, "font", 0, DE_ENCODING_ASCII);
166 de_font_bitmap_font_to_image(c, d->font, fi, DE_CREATEFLAG_IS_AUX);
168 de_finfo_destroy(c, fi);
171 static void do_read_font_data(deark *c, lctx *d, i64 pos)
173 u32 crc;
174 struct de_crcobj *crco;
176 de_dbg(c, "font at %"I64_FMT", %"I64_FMT" bytes", pos, d->font_data_len);
177 de_dbg_indent(c, 1);
178 d->font_data = de_malloc(c, d->font_data_len);
179 de_read(d->font_data, pos, d->font_data_len);
181 crco = de_crcobj_create(c, DE_CRCOBJ_CRC32_IEEE);
182 de_crcobj_addbuf(crco, d->font_data, d->font_data_len);
183 crc = de_crcobj_getval(crco);
184 de_crcobj_destroy(crco);
186 d->is_standard_font = de_font_is_standard_vga_font(c, crc);
187 de_dbg(c, "font crc: 0x%08x (%s)", (UI)crc,
188 d->is_standard_font?"known CP437 font":"unrecognized");
190 if(de_get_ext_option(c, "font:dumpvgafont")) {
191 dbuf *df;
192 df = dbuf_create_output_file(c, "font.dat", NULL, DE_CREATEFLAG_IS_AUX);
193 dbuf_write(df, d->font_data, d->font_data_len);
194 dbuf_close(df);
196 de_dbg_indent(c, -1);
199 // Finish populating the d->font struct.
200 static int do_generate_font(deark *c, lctx *d)
202 i64 i;
203 struct de_encconv_state es;
205 if(!d->font) return 0;
206 if(d->font->num_chars!=256) {
207 de_err(c, "Only 256-character fonts are supported");
208 return 0;
210 if(d->font_data_len!=d->font->num_chars*d->font_height) {
211 de_err(c, "Incorrect font data size");
212 return 0;
214 d->font->nominal_width = 8;
215 d->font->nominal_height = (int)d->font_height;
216 d->font->char_array = de_mallocarray(c, d->font->num_chars, sizeof(struct de_bitmap_font_char));
217 de_encconv_init(&es, DE_ENCODING_CP437_G);
219 for(i=0; i<d->font->num_chars; i++) {
220 d->font->char_array[i].codepoint_nonunicode = (i32)i;
221 d->font->char_array[i].codepoint_unicode = de_char_to_unicode_ex((i32)i, &es);
222 d->font->char_array[i].width = d->font->nominal_width;
223 d->font->char_array[i].height = d->font->nominal_height;
224 d->font->char_array[i].rowspan = 1;
225 d->font->char_array[i].bitmap = &d->font_data[i*d->font_height];
228 return 1;
231 static void de_run_xbin(deark *c, de_module_params *mparams)
233 lctx *d = NULL;
234 struct de_char_context *charctx = NULL;
235 struct de_SAUCE_detection_data sdd;
236 struct de_SAUCE_info *si = NULL;
237 i64 pos = 0;
238 u8 flags;
239 dbuf *unc_data = NULL;
241 d = de_malloc(c, sizeof(lctx));
243 charctx = de_create_charctx(c, 0);
244 charctx->prefer_image_output = 1;
245 de_char_decide_output_format(c, charctx);
247 fmtutil_detect_SAUCE(c, c->infile, &sdd, 0x1);
248 if(sdd.has_SAUCE) {
249 si = fmtutil_create_SAUCE(c);
251 de_dbg_indent(c, 1);
252 fmtutil_handle_SAUCE(c, c->infile, si);
253 de_dbg_indent(c, -1);
255 charctx->title = si->title;
256 charctx->artist = si->artist;
257 charctx->organization = si->organization;
258 charctx->creation_date = si->creation_date;
259 charctx->comment = si->comment;
262 d->csctx.width_in_chars = de_getu16le(5);
263 d->csctx.height_in_chars = de_getu16le(7);
264 d->font_height = (i64)de_getbyte(9);
266 flags = de_getbyte(10);
267 de_dbg(c, "dimensions: %d"DE_CHAR_TIMES"%d characters", (int)d->csctx.width_in_chars,
268 (int)d->csctx.height_in_chars);
269 de_dbg(c, "font height: %d", (int)d->font_height);
270 de_dbg(c, "flags: 0x%02x", (UI)flags);
271 d->has_palette = (flags&0x01)?1:0;
272 d->has_font = (flags&0x02)?1:0;
273 d->compression = (flags&0x04)?1:0;
274 d->csctx.nonblink = (flags&0x08)?1:0;
275 d->has_512chars = (flags&0x10)?1:0;
276 de_dbg_indent(c, 1);
277 de_dbg(c, "has palette: %u", (UI)d->has_palette);
278 de_dbg(c, "has font: %u", (UI)d->has_font);
279 de_dbg(c, "compression: %u", (UI)d->compression);
280 de_dbg(c, "non-blink mode: %u", (UI)d->csctx.nonblink);
281 de_dbg(c, "512 character mode: %u", (UI)d->has_512chars);
282 de_dbg_indent(c, -1);
284 if(d->has_font && (d->font_height<1 || d->font_height>32)) {
285 de_err(c, "Invalid font height: %d", (int)d->font_height);
286 goto done;
288 pos = 11;
290 if(d->has_palette) {
291 do_read_palette(c, d, charctx, pos, 0);
292 pos += 48;
294 else {
295 de_dbg(c, "using default palette");
296 d->csctx.use_default_pal = 1;
299 if(d->has_font) {
300 d->font = de_create_bitmap_font(c);
301 d->font->has_nonunicode_codepoints = 1;
302 d->font->has_unicode_codepoints = 1;
303 d->font->prefer_unicode = 0;
304 d->font->num_chars = d->has_512chars ? 512 : 256;
305 d->font_data_len = d->font->num_chars * d->font_height;
306 if(d->font->num_chars!=256) {
307 de_err(c, "%d-character mode is not supported", (int)d->font->num_chars);
308 goto done;
311 do_read_font_data(c, d, pos);
312 pos += d->font_data_len;
314 if(d->is_standard_font) {
315 charctx->suppress_custom_font_warning = 1;
318 if(!do_generate_font(c, d)) goto done;
320 if(c->extract_level>=2) {
321 do_extract_font(c, d);
324 charctx->font = d->font;
326 else {
327 // Use default font
329 if(d->has_512chars) {
330 de_err(c, "This type of XBIN file is not supported.");
331 goto done;
334 if(d->font_height==0) {
335 // Not really legal, but we'll let it mean "default".
337 else if(d->font_height!=16) {
338 if(charctx->outfmt==1) { // image output
339 de_warn(c, "Incompatible font height (%d), using 16 instead.", (int)d->font_height);
342 d->font_height = 16;
345 de_dbg(c, "image data at %"I64_FMT, pos);
347 if(d->compression) {
348 unc_data = dbuf_create_membuf(c, d->csctx.width_in_chars * d->csctx.height_in_chars * 2, 1);
349 xbin_decompress_data(c, d, pos, unc_data);
351 else {
352 unc_data = dbuf_open_input_subfile(c->infile, pos, c->infile->len-pos);
354 d->csctx.inf = unc_data;
355 d->csctx.inf_pos = 0;
356 d->csctx.inf_len = unc_data->len;
357 fmtutil_char_simple_run(c, &d->csctx, charctx);
359 done:
360 dbuf_close(unc_data);
361 de_free_charctx_screens(c, charctx);
362 de_destroy_charctx(c, charctx);
363 fmtutil_free_SAUCE(c, si);
364 free_lctx(c, d);
367 static int de_identify_xbin(deark *c)
369 if(!dbuf_memcmp(c->infile, 0, "XBIN\x1a", 5))
370 return 100;
371 return 0;
374 static void de_help_xbin(deark *c)
376 de_msg(c, "-opt char:output=html : Write HTML instead of an image file");
377 de_msg(c, "-opt char:charwidth=<8|9> : Width of a character cell");
380 void de_module_xbin(deark *c, struct deark_module_info *mi)
382 mi->id = "xbin";
383 mi->desc = "XBIN character graphics";
384 mi->run_fn = de_run_xbin;
385 mi->identify_fn = de_identify_xbin;
386 mi->help_fn = de_help_xbin;
389 ////////////////////// Binary Text //////////////////////
391 static void de_run_bintext(deark *c, de_module_params *mparams)
393 lctx *d = NULL;
394 struct de_char_context *charctx = NULL;
395 struct de_SAUCE_detection_data sdd;
396 struct de_SAUCE_info *si = NULL;
397 i64 effective_file_size = 0;
398 int valid_sauce = 0;
399 const char *s;
400 i64 width_req = 0;
402 d = de_malloc(c, sizeof(lctx));
403 d->csctx.input_encoding = de_get_input_encoding(c, NULL, DE_ENCODING_CP437);
405 charctx = de_create_charctx(c, 0);
406 charctx->prefer_image_output = 0;
408 s=de_get_ext_option(c, "char:width");
409 if(s) {
410 width_req = de_atoi(s);
413 fmtutil_detect_SAUCE(c, c->infile, &sdd, 0x1);
414 if(sdd.has_SAUCE) {
415 si = fmtutil_create_SAUCE(c);
417 de_dbg_indent(c, 1);
418 fmtutil_handle_SAUCE(c, c->infile, si);
419 de_dbg_indent(c, -1);
421 charctx->title = si->title;
422 charctx->artist = si->artist;
423 charctx->organization = si->organization;
424 charctx->creation_date = si->creation_date;
425 charctx->comment = si->comment;
427 effective_file_size = si->original_file_size;
429 if(si->data_type==5) {
430 valid_sauce = 1;
432 if(si->file_type==1 && si->tinfo1>0) {
433 // Some files created by ACiDDraw do this.
434 d->csctx.width_in_chars = 2*(i64)si->tinfo1;
436 else {
437 // For BinText, the FileType field is inexplicably used for the width (usually).
438 d->csctx.width_in_chars = 2*(i64)si->file_type;
441 if(si->tflags & 0x01) {
442 d->csctx.nonblink = 1;
444 if((si->tflags & 0x18)>>3 == 0x02) {
445 // Square pixels requested
446 charctx->no_density = 1;
448 if((si->tflags & 0x06)>>1 == 0x02) {
449 charctx->prefer_9col_mode = 1;
454 if(!valid_sauce) {
455 d->csctx.width_in_chars = 160;
456 effective_file_size = c->infile->len;
459 if(width_req>0) d->csctx.width_in_chars = width_req;
461 if(d->csctx.width_in_chars<1) d->csctx.width_in_chars=160;
462 if(effective_file_size%(d->csctx.width_in_chars*2)) {
463 de_warn(c, "File does not contain a whole number of rows. The width may "
464 "be wrong. Try \"-opt char:width=...\".");
466 d->csctx.height_in_chars = effective_file_size / (d->csctx.width_in_chars*2);
468 de_dbg(c, "width: %d chars", (int)d->csctx.width_in_chars);
469 de_dbg(c, "calculated height: %d chars", (int)d->csctx.height_in_chars);
470 d->has_palette = 1;
471 d->has_font = 1;
472 d->compression = 0;
473 d->has_512chars = 0;
475 d->csctx.use_default_pal = 1;
476 d->csctx.inf = c->infile;
477 d->csctx.inf_pos = 0;
478 d->csctx.inf_len = effective_file_size;
479 fmtutil_char_simple_run(c, &d->csctx, charctx);
481 de_free_charctx(c, charctx);
482 fmtutil_free_SAUCE(c, si);
483 free_lctx(c, d);
486 static int de_identify_bintext(deark *c)
488 if(!c->detection_data->SAUCE_detection_attempted) {
489 // FIXME?: This is known to happen if "-disablemods sauce" was used.
490 de_err(c, "bintext detection requires sauce module");
491 return 0;
493 if(c->detection_data->sauce.has_SAUCE) {
494 if(c->detection_data->sauce.data_type==5)
496 return 100;
499 return 0;
502 static void de_help_bintext(deark *c)
504 de_msg(c, "-opt char:output=image : Write an image file instead of HTML");
505 de_msg(c, " -opt char:charwidth=<8|9> : Width of a character cell");
506 de_msg(c, "-opt char:width=<n> : Number of characters per row");
509 void de_module_bintext(deark *c, struct deark_module_info *mi)
511 mi->id = "bintext";
512 mi->desc = "Binary Text character graphics";
513 mi->run_fn = de_run_bintext;
514 mi->identify_fn = de_identify_bintext;
515 mi->help_fn = de_help_bintext;
518 ////////////////////// ArtWorx Data Format (ADF) //////////////////////
520 static void de_run_artworx_adf(deark *c, de_module_params *mparams)
522 lctx *d = NULL;
523 struct de_char_context *charctx = NULL;
524 i64 data_start;
525 i64 data_len;
527 d = de_malloc(c, sizeof(lctx));
529 // TODO: ADF files can probably have SAUCE records, so we should read
530 // the SAUCE data if present. But there does not seem to be a defined
531 // SAUCE file type for ADF.
533 charctx = de_create_charctx(c, 0);
534 charctx->prefer_image_output = 1;
536 data_start = 1+192+4096;
537 data_len = c->infile->len - data_start;
538 if(data_len<0) goto done;
540 d->csctx.width_in_chars = 80;
541 d->csctx.height_in_chars = data_len / (d->csctx.width_in_chars*2);
543 de_dbg(c, "guessed width: %d chars", (int)d->csctx.width_in_chars);
544 de_dbg(c, "calculated height: %d chars", (int)d->csctx.height_in_chars);
545 if(d->csctx.height_in_chars<1) goto done;
546 d->has_palette = 0;
547 d->has_font = 1;
548 d->compression = 0;
549 d->has_512chars = 0;
550 d->csctx.nonblink = 1;
552 do_read_palette(c, d, charctx, 1, 1);
555 // TODO: This duplicates a lot of the xbin code.
557 d->font = de_create_bitmap_font(c);
558 d->font->has_nonunicode_codepoints = 1;
559 d->font->has_unicode_codepoints = 1;
560 d->font->prefer_unicode = 0;
561 d->font->num_chars = 256;
562 d->font_height = 16;
563 d->font_data_len = d->font->num_chars * d->font_height;
565 do_read_font_data(c, d, 1+192);
567 if(d->is_standard_font) {
568 charctx->suppress_custom_font_warning = 1;
571 if(!do_generate_font(c, d)) goto done;
573 if(c->extract_level>=2) {
574 do_extract_font(c, d);
577 charctx->font = d->font;
580 d->csctx.inf = c->infile;
581 d->csctx.inf_pos = data_start;
582 d->csctx.inf_len = data_len;
583 fmtutil_char_simple_run(c, &d->csctx, charctx);
585 done:
586 de_free_charctx(c, charctx);
587 free_lctx(c, d);
590 static int de_identify_artworx_adf(deark *c)
592 u8 ver;
594 // TODO: This detection algorithm will fail if there is a SAUCE record.
596 if(c->infile->len < 1+192+4096+160) {
597 return 0;
599 if((c->infile->len - (1+192+4096))%160 != 0) {
600 return 0;
602 if(!de_input_file_has_ext(c, "adf")) return 0;
603 ver = de_getbyte(0);
604 // I don't know what version numbers are allowed, but I'll assume the
605 // version number should be small.
606 if(ver>4) return 0;
607 return 75;
610 static void de_help_artworx_adf(deark *c)
612 de_msg(c, "-opt char:output=html : Write HTML instead of an image file");
613 de_msg(c, "-opt char:charwidth=<8|9> : Width of a character cell");
614 de_msg(c, "-opt char:width=<n> : Number of characters per row");
617 void de_module_artworx_adf(deark *c, struct deark_module_info *mi)
619 mi->id = "artworx_adf";
620 mi->desc = "ArtWorx Data Format (ADF)";
621 mi->run_fn = de_run_artworx_adf;
622 mi->identify_fn = de_identify_artworx_adf;
623 mi->help_fn = de_help_artworx_adf;
626 ////////////////////// iCEDraw format (.idf) //////////////////////
628 // This module is not yet implemented. This stub exists because it seemed
629 // like the simplest way to accomplish multiple goals:
630 // * Avoid having iCEDraw mis-identified as ANSI Art.
631 // * Avoid an error message from the SAUCE module implying that ANSI
632 // Art is not a supported format.
633 // * Print debugging info about the SAUCE record, if present.
634 // * Print the same error message whether or not a SAUCE record is present.
636 static void de_run_icedraw(deark *c, de_module_params *mparams)
638 read_and_discard_SAUCE(c);
639 de_err(c, "iCEDraw format is not supported");
642 static int de_identify_icedraw(deark *c)
644 if(!dbuf_memcmp(c->infile, 0, "\x04\x31\x2e\x34", 4)) {
645 return 100;
647 return 0;
650 void de_module_icedraw(deark *c, struct deark_module_info *mi)
652 mi->id = "icedraw";
653 mi->desc = "iCEDraw character graphics format";
654 mi->run_fn = de_run_icedraw;
655 mi->identify_fn = de_identify_icedraw;
656 mi->flags |= DE_MODFLAG_NONWORKING;
659 ////////////////////// TheDraw COM //////////////////////
661 struct thedrawcom_dcmpr_state {
662 u8 prescan_mode;
663 u8 curr_fg_code;
664 u8 curr_bg_code;
665 u8 curr_blink_code;
666 i64 next_xpos, next_ypos;
667 i64 max_xpos, max_ypos;
668 i64 ypos_of_last_nonblank;
671 struct thedrawcom_ctx {
672 lctx *d;
673 struct de_char_context *charctx;
674 dbuf *unc_data;
675 struct de_crcobj *crco;
676 i64 screen_pos_raw;
677 i64 data_pos;
678 u8 fmt_subtype;
679 i64 cmpr_len;
680 i64 viewer_start;
681 i64 viewer_len;
682 u32 viewer_expected_crc;
683 struct thedrawcom_dcmpr_state dc;
686 #define THEDRAWCOM_MAX_WIDTH 160
687 #define THEDRAWCOM_MAX_HEIGHT 200
689 static void tdc_decrunch_emit_char(deark *c, struct thedrawcom_ctx *tdc, u8 ch)
691 if(!tdc->dc.prescan_mode) {
692 dbuf_writebyte(tdc->unc_data, ch);
693 dbuf_writebyte(tdc->unc_data, (u8)(tdc->dc.curr_blink_code |
694 tdc->dc.curr_bg_code | tdc->dc.curr_fg_code));
696 if(tdc->dc.next_xpos > tdc->dc.max_xpos) {
697 tdc->dc.max_xpos = tdc->dc.next_xpos;
699 if(tdc->dc.next_ypos > tdc->dc.max_ypos) {
700 tdc->dc.max_ypos = tdc->dc.next_ypos;
702 if(ch!=0 && ch!=32 && tdc->dc.next_ypos>tdc->dc.ypos_of_last_nonblank) {
703 tdc->dc.ypos_of_last_nonblank = tdc->dc.next_ypos;
705 tdc->dc.next_xpos++;
708 // Based on the description in UNCRUNCH.ASM from TheDraw 4.xx.
709 static void thedrawcom_decrunch(deark *c, struct thedrawcom_ctx *tdc, u8 prescan_mode)
711 i64 pos = tdc->data_pos;
712 i64 endpos = tdc->data_pos + tdc->cmpr_len;
713 struct thedrawcom_dcmpr_state *dc = &tdc->dc;
715 de_zeromem(dc, sizeof(struct thedrawcom_dcmpr_state));
716 dc->prescan_mode = prescan_mode;
717 dc->curr_fg_code = 0xf;
719 if(endpos > c->infile->len) {
720 tdc->d->errflag = 1;
721 tdc->d->need_errmsg = 1;
722 goto done;
725 if(!tdc->unc_data) {
726 tdc->unc_data = dbuf_create_membuf(c, 80*2*25, 0);
727 dbuf_set_length_limit(tdc->unc_data,
728 THEDRAWCOM_MAX_WIDTH*2*THEDRAWCOM_MAX_HEIGHT);
729 dbuf_enable_wbuffer(tdc->unc_data);
732 while(1) {
733 u8 b0, b1, b2;
734 i64 k;
736 if(tdc->d->errflag) goto done;
737 if(pos >= endpos) break;
739 b0 = de_getbyte_p(&pos);
741 if(b0>=32) {
742 tdc_decrunch_emit_char(c, tdc, b0);
743 continue;
745 if(b0<=15) {
746 dc->curr_fg_code = b0;
748 else if(b0<=23) {
749 dc->curr_bg_code = (b0-16)<<4;
751 else if(b0==24) { // Newline
753 dc->next_ypos++;
754 dc->next_xpos = 0;
756 if(!dc->prescan_mode) {
757 i64 expected_len;
758 i64 actual_len;
760 // Make sure we're in the right place in the decompressed data.
761 expected_len = tdc->d->csctx.width_in_chars * 2 * dc->next_ypos;
762 actual_len = dbuf_get_length(tdc->unc_data);
763 if(actual_len != expected_len) {
764 // It would be better to pad with spaces, but in practice
765 // this doesn't happen.
766 dbuf_truncate(tdc->unc_data, expected_len);
770 else if(b0==25) { // Run of spaces
771 b1 = de_getbyte_p(&pos);
772 for(k=0; k<(i64)b1+1; k++) {
773 tdc_decrunch_emit_char(c, tdc, 32);
776 else if(b0==26) { // Run of an arbitrary character
777 b1 = de_getbyte_p(&pos);
778 b2 = de_getbyte_p(&pos);
779 for(k=0; k<(i64)b1+1; k++) {
780 tdc_decrunch_emit_char(c, tdc, b2);
783 else if(b0==27) { // Toggle blink mode
784 dc->curr_blink_code = (dc->curr_blink_code==0) ? 0x80 : 0x00;
786 else { // Unknown opcode
787 tdc->d->errflag = 1;
788 tdc->d->need_errmsg = 1;
789 goto done;
793 // Decompression finished normally
795 if((dc->max_xpos+1 > THEDRAWCOM_MAX_WIDTH) ||
796 (dc->max_ypos+1 > THEDRAWCOM_MAX_HEIGHT))
798 tdc->d->errflag = 1;
799 tdc->d->need_errmsg = 1;
800 goto done;
803 if(dc->prescan_mode && !tdc->d->errflag) {
804 de_dbg(c, "number of rows: %d", (int)(dc->max_ypos+1));
805 de_dbg(c, "last nonblank row: %d", (int)dc->ypos_of_last_nonblank);
806 tdc->d->csctx.width_in_chars = dc->max_xpos + 1;
808 if(dc->max_ypos>24) {
809 tdc->d->csctx.height_in_chars = dc->ypos_of_last_nonblank + 1;
811 else {
812 tdc->d->csctx.height_in_chars = dc->max_ypos + 1;
815 de_dbg_dimensions(c, tdc->d->csctx.width_in_chars, tdc->d->csctx.height_in_chars);
818 done:
819 dbuf_flush(tdc->unc_data);
821 if(tdc->d->need_errmsg) {
822 de_err(c, "Decompression failed");
823 tdc->d->need_errmsg = 0;
824 tdc->d->errflag = 1;
828 static void de_run_thedraw_com(deark *c, de_module_params *mparams)
830 struct thedrawcom_ctx *tdc = NULL;
831 u32 cv;
832 int saved_indent_level;
834 de_dbg_indent_save(c, &saved_indent_level);
835 tdc = de_malloc(c, sizeof(struct thedrawcom_ctx));
836 tdc->d = de_malloc(c, sizeof(lctx));
837 tdc->d->csctx.input_encoding = de_get_input_encoding(c, NULL, DE_ENCODING_CP437);
838 tdc->charctx = de_create_charctx(c, 0);
839 tdc->d->csctx.use_default_pal = 1;
841 tdc->fmt_subtype = de_getbyte(6);
842 de_dbg(c, "format subtype: %u", (UI)tdc->fmt_subtype);
844 tdc->viewer_start = 2 + (i64)de_getbyte(1);
845 de_dbg2(c, "viewer pos: %"I64_FMT, tdc->viewer_start);
847 if(tdc->fmt_subtype==0) {
848 tdc->viewer_len = 113;
849 tdc->viewer_expected_crc = 0x440affaeU;
850 tdc->data_pos = 176;
852 else if(tdc->fmt_subtype==1) {
853 tdc->viewer_len = 68;
854 tdc->viewer_expected_crc = 0xe427d2e3U;
855 tdc->data_pos = 94;
857 else if(tdc->fmt_subtype==2) {
858 tdc->viewer_len = 177;
859 tdc->viewer_expected_crc = 0x492a698d;
860 tdc->data_pos = 240;
862 else {
863 de_err(c, "Unsupported format subtype: %u", (UI)tdc->fmt_subtype);
864 goto done;
867 tdc->crco = de_crcobj_create(c, DE_CRCOBJ_CRC32_IEEE);
868 de_crcobj_addslice(tdc->crco, c->infile, tdc->viewer_start, tdc->viewer_len);
870 cv = de_crcobj_getval(tdc->crco);
871 de_dbg2(c, "viewer crc: 0x%08x", (UI)cv);
872 if(cv != tdc->viewer_expected_crc) {
873 de_warn(c, "Unrecognized format variant. This file might not be "
874 "decoded correctly.");
877 if(tdc->fmt_subtype==0) {
878 tdc->d->csctx.height_in_chars = (i64)de_getbyte(4);
879 tdc->d->csctx.width_in_chars = (i64)de_getbyte(5);
880 de_dbg_dimensions(c, tdc->d->csctx.width_in_chars, tdc->d->csctx.height_in_chars);
883 if(tdc->fmt_subtype==1) {
884 i64 nchars;
886 tdc->d->csctx.width_in_chars = 80;
887 nchars = de_getu16le(4);
888 de_dbg(c, "num. chars: %"I64_FMT, nchars);
889 tdc->d->csctx.height_in_chars = de_pad_to_n(nchars, 80)/80;
890 de_dbg_dimensions(c, tdc->d->csctx.width_in_chars, tdc->d->csctx.height_in_chars);
893 if(tdc->fmt_subtype==2) {
894 tdc->cmpr_len = de_getu16le(4);
895 de_dbg(c, "cmpr len: %"I64_FMT, tdc->cmpr_len);
898 tdc->screen_pos_raw = de_geti16le(7);
899 de_dbg(c, "screen pos: %"I64_FMT, tdc->screen_pos_raw);
900 // TODO? The screen pos is relevant if just a block, instead of a whole
901 // screen, was saved.
902 // We could artificially pad the image with spaces above/left/right in
903 // that case.
905 de_dbg(c, "data pos: %"I64_FMT, tdc->data_pos);
907 if(tdc->fmt_subtype==2) {
908 de_dbg(c, "decompressing");
909 de_dbg_indent(c, 1);
910 // The first decompression is to figure out the dimensions.
911 thedrawcom_decrunch(c, tdc, 1);
912 if(tdc->d->errflag) goto done;
913 thedrawcom_decrunch(c, tdc, 0);
914 if(tdc->d->errflag) goto done;
915 de_dbg_indent(c, -1);
917 else {
918 tdc->unc_data = dbuf_open_input_subfile(c->infile, tdc->data_pos,
919 c->infile->len-tdc->data_pos);
922 if(!tdc->unc_data || tdc->d->csctx.width_in_chars<1 || tdc->d->csctx.height_in_chars<1) {
923 tdc->d->need_errmsg = 1;
924 goto done;
927 if(tdc->d->errflag) goto done;
928 tdc->d->csctx.inf = tdc->unc_data;
929 tdc->d->csctx.inf_pos = 0;
930 tdc->d->csctx.inf_len = tdc->unc_data->len;
931 fmtutil_char_simple_run(c, &tdc->d->csctx, tdc->charctx);
933 done:
934 if(tdc) {
935 if(tdc->d && tdc->d->need_errmsg) {
936 de_err(c, "Failed to decode this file");
939 dbuf_close(tdc->unc_data);
940 if(tdc->charctx) {
941 de_free_charctx_screens(c, tdc->charctx);
942 de_destroy_charctx(c, tdc->charctx);
944 de_crcobj_destroy(tdc->crco);
945 free_lctx(c, tdc->d);
947 de_dbg_indent_restore(c, saved_indent_level);
950 static int de_identify_thedraw_com(deark *c)
952 u8 n1, n2;
954 if(c->infile->len>65280) return 0;
955 n1 = de_getbyte(0);
956 if(n1!=0xeb) return 0;
958 n1 = de_getbyte(1);
959 n2 = de_getbyte(6);
960 // Check format subtype & viewer start position.
961 if((n2==0 && n1==0x3d) ||
962 (n2==1 && n1==0x18) ||
963 (n2==2 && n1==0x3d))
967 else {
968 return 0;
971 if(!dbuf_memcmp(c->infile, 9, (const void*)"TheDraw COM file", 16)) {
972 return 100;
975 if(n2==0) {
976 if(!dbuf_memcmp(c->infile, 0x3f,
977 (const void*)"\xb4\x0f\xcd\x10\x8c\xcb\x8e\xdb\xbb\x00", 10))
979 // An extra check to defend against lookalike formats, particularly
980 // P-Screen (search for pscrn_55.zip).
981 if(de_getbyte(0x3f+76)==0xad) {
982 return 40;
986 else if(n2==1) {
987 if(!dbuf_memcmp(c->infile, 0x1a,
988 (const void*)"\xb4\x0f\xcd\x10\x8b\x3e\x07\x01\xbe\x5e", 10))
990 return 40;
993 else if(n2==2) {
994 if(!dbuf_memcmp(c->infile, 0x3f,
995 (const void*)"\xb4\x0f\xcd\x10\xbb\x00\xb8\x3c\x02\x74", 10))
997 return 40;
1001 return 0;
1004 void de_module_thedraw_com(deark *c, struct deark_module_info *mi)
1006 mi->id = "thedraw_com";
1007 mi->desc = "TheDraw COM file";
1008 mi->run_fn = de_run_thedraw_com;
1009 mi->identify_fn = de_identify_thedraw_com;
1012 ////////////////////// ACiDDraw COM //////////////////////
1014 struct aciddraw_id_data {
1015 u8 is_aciddraw;
1016 i64 jmppos;
1019 struct aciddraw_ctx {
1020 int opt_disable_blink;
1021 struct de_SAUCE_info *si;
1022 struct de_char_context *charctx;
1025 static void aciddraw_id(deark *c, u8 b0, struct aciddraw_id_data *adi)
1027 de_zeromem(adi, sizeof(struct aciddraw_id_data));
1028 if(b0==0xe9) {
1029 adi->jmppos = de_geti16le(1) + 3;
1031 else if(b0==0xeb) {
1032 adi->jmppos = de_getbyte(1) + 2;
1034 else {
1035 return;
1038 if(dbuf_memcmp(c->infile, adi->jmppos,
1039 (const void*)"\xb8\x03\x00\xcd\x10\xb4\x01\xb9\x00\x20\xcd\x10\xe8", 13))
1041 return;
1043 adi->is_aciddraw = 1;
1046 static void aciddraw_handle_SAUCE(deark *c, struct aciddraw_ctx *adctx)
1048 struct de_SAUCE_detection_data sdd;
1050 fmtutil_detect_SAUCE(c, c->infile, &sdd, 0x1);
1051 if(!sdd.has_SAUCE) goto done;
1053 adctx->si = fmtutil_create_SAUCE(c);
1054 de_dbg_indent(c, 1);
1055 fmtutil_handle_SAUCE(c, c->infile, adctx->si);
1056 de_dbg_indent(c, -1);
1057 if(!adctx->si->is_valid) goto done;
1059 adctx->charctx->title = adctx->si->title;
1060 adctx->charctx->artist = adctx->si->artist;
1061 adctx->charctx->organization = adctx->si->organization;
1062 adctx->charctx->creation_date = adctx->si->creation_date;
1063 adctx->charctx->comment = adctx->si->comment;
1065 // Note about blinking text:
1066 // ACiDDraw has a nonblink mode (Ctrl+Z), but the COM files it writes do
1067 // not record or respect this mode in any way, even if a SAUCE record is
1068 // written. They just use whatever mode the video hardware is in, which
1069 // is usually the mode with blinking text.
1070 // So we don't bother to respect the SAUCE flag.
1071 // (ACiDDraw does have a separate BLINK.EXE utility that changes the mode,
1072 // which could maybe be useful in batch files.)
1074 done:
1078 static void de_run_aciddraw_com(deark *c, de_module_params *mparams)
1080 struct aciddraw_ctx *adctx = NULL;
1081 lctx *d = NULL;
1082 u8 b;
1083 UI fmtver = 0;
1084 struct aciddraw_id_data adi;
1085 i64 data_pos, data_len;
1086 i64 num_rows;
1088 adctx = de_malloc(c, sizeof(struct aciddraw_ctx));
1089 d = de_malloc(c, sizeof(lctx));
1090 d->csctx.input_encoding = de_get_input_encoding(c, NULL, DE_ENCODING_CP437);
1091 adctx->charctx = de_create_charctx(c, 0);
1093 if(de_get_ext_option(c, "ansiart:noblink")) {
1094 adctx->opt_disable_blink = 1;
1097 aciddraw_id(c, de_getbyte(0), &adi);
1098 if(!adi.is_aciddraw) {
1099 d->need_errmsg = 1;
1100 goto done;
1103 aciddraw_handle_SAUCE(c, adctx);
1105 d->csctx.nonblink = adctx->opt_disable_blink;
1107 de_dbg(c, "jmp pos: %"I64_FMT, adi.jmppos);
1109 b = de_getbyte(adi.jmppos+224);
1110 if(b==0xe8) {
1111 fmtver = 20;
1113 else if(b==0xb4) {
1114 fmtver = 25;
1116 else {
1117 d->need_errmsg = 1;
1118 goto done;
1120 de_dbg(c, "version: v1.%u-like", fmtver);
1122 data_pos = de_getu16le(adi.jmppos-24);
1123 data_pos -= 0x100;
1124 de_dbg(c, "data pos: %"I64_FMT, data_pos);
1126 num_rows = de_getu16le(adi.jmppos+16);
1127 if(fmtver==25) num_rows++;
1128 de_dbg(c, "num rows: %"I64_FMT, num_rows);
1130 d->csctx.width_in_chars = 80;
1131 d->csctx.height_in_chars = num_rows;
1133 data_len = num_rows * d->csctx.width_in_chars * 2;
1134 de_dbg(c, "data endpos: %"I64_FMT, data_pos + data_len);
1136 if(data_pos+data_len > c->infile->len) {
1137 d->need_errmsg = 1;
1138 goto done;
1141 d->csctx.use_default_pal = 1;
1142 d->csctx.inf = c->infile;
1143 d->csctx.inf_pos = data_pos;
1144 d->csctx.inf_len = data_len;
1145 fmtutil_char_simple_run(c, &d->csctx, adctx->charctx);
1147 done:
1148 if(d) {
1149 if(d->need_errmsg) {
1150 de_err(c, "Unsupported ACiDDraw format");
1152 free_lctx(c, d);
1154 if(adctx) {
1155 de_free_charctx(c, adctx->charctx);
1156 fmtutil_free_SAUCE(c, adctx->si);
1160 static int de_identify_aciddraw_com(deark *c)
1162 u8 n1;
1163 struct aciddraw_id_data adi;
1165 if(c->infile->len>65280) return 0;
1166 n1 = de_getbyte(0);
1167 if(n1!=0xe9 && n1!=0xeb) return 0;
1168 aciddraw_id(c, n1, &adi);
1169 if(adi.is_aciddraw) {
1170 return 92;
1172 return 0;
1175 void de_module_aciddraw_com(deark *c, struct deark_module_info *mi)
1177 mi->id = "aciddraw_com";
1178 mi->desc = "ACiDDraw COM file";
1179 mi->run_fn = de_run_aciddraw_com;
1180 mi->identify_fn = de_identify_aciddraw_com;
1183 ////////////////////// GRABBER //////////////////////
1185 struct grabber_id_data {
1186 u8 is_grabber;
1187 UI fmt_class;
1188 i64 jmppos;
1191 struct grabber_ctx {
1192 u8 screen_mode;
1193 i64 data_pos, data_len;
1194 struct de_char_context *charctx;
1195 struct grabber_id_data gi;
1198 static void grabber_id_com(deark *c, u8 b0, struct grabber_id_data *gi)
1200 de_zeromem(gi, sizeof(struct grabber_id_data));
1202 if(b0==0xfb) {
1203 if(!dbuf_memcmp(c->infile, 1,
1204 (const void*)"\xbe\x81\x00\x8a\x4c\xff\x30\xed\x09\xc9\x74", 11)) {
1205 gi->is_grabber = 1;
1206 gi->fmt_class = 200;
1208 return;
1211 if(b0!=0xe9) return;
1212 gi->jmppos = de_geti16le(1) + 3;
1214 if(!dbuf_memcmp(c->infile, gi->jmppos,
1215 (const void*)"\xbe\x81\x00\xad\x80\xfc\x0d\x74\x17\x3c\x0d\x74", 12))
1217 gi->is_grabber = 1;
1218 gi->fmt_class = 300;
1219 return;
1221 if(!dbuf_memcmp(c->infile, gi->jmppos,
1222 (const void*)"\xbe\x81\x00\xfc\xad\x80\xfc\x0d\x74\x1c\x3c\x0d\x74", 13))
1224 gi->is_grabber = 1;
1225 gi->fmt_class = 334;
1226 return;
1230 static void decode_grabber_com(deark *c, lctx *d, struct grabber_ctx *gctx)
1232 i64 foundpos = 0;
1233 i64 pos_of_data_ptr;
1234 i64 pos_of_mode;
1235 int ret;
1236 u8 *mem = NULL;
1238 #define GRABBER_SEARCH1_START 112
1239 #define GRABBER_BUF_LEN1 1024
1240 mem = de_malloc(c, GRABBER_BUF_LEN1);
1241 de_read(mem, GRABBER_SEARCH1_START, GRABBER_BUF_LEN1);
1242 // Search for the byte pattern preceding the data pointer.
1243 // Known positions range from 121 (v2.10) to 869 (v3.34).
1244 ret = de_memsearch_match(mem, GRABBER_BUF_LEN1,
1245 (const u8*)"\xb8\x00?\x8e\xc0\xbe", 6,
1246 '?', &foundpos);
1247 if(!ret) {
1248 d->errflag = 1;
1249 d->need_errmsg = 1;
1250 goto done;
1253 pos_of_data_ptr = foundpos+GRABBER_SEARCH1_START+6;
1254 de_dbg(c, "pos of data ptr: %"I64_FMT, pos_of_data_ptr);
1256 gctx->data_pos = de_getu16le(pos_of_data_ptr);
1257 gctx->data_pos -= 256;
1258 de_dbg(c, "data pos: %"I64_FMT, gctx->data_pos);
1260 if(gctx->gi.fmt_class<300) {
1261 pos_of_mode = gctx->data_pos - 7;
1263 else {
1264 pos_of_mode = gctx->data_pos - 17;
1267 gctx->screen_mode = de_getbyte(pos_of_mode);
1268 de_dbg(c, "mode: 0x%02x", (UI)gctx->screen_mode);
1269 if(gctx->screen_mode != 3) {
1270 d->errflag = 1;
1271 d->need_errmsg = 1;
1272 goto done;
1275 gctx->data_len = de_getu16le(pos_of_mode+2);
1276 de_dbg(c, "data len: %"I64_FMT, gctx->data_len);
1277 d->csctx.width_in_chars = 80;
1278 done:
1279 de_free(c, mem);
1282 static void de_run_grabber(deark *c, de_module_params *mparams)
1284 struct grabber_ctx *gctx = NULL;
1285 lctx *d = NULL;
1286 UI sig;
1288 gctx = de_malloc(c, sizeof(struct grabber_ctx));
1289 gctx->charctx = de_create_charctx(c, 0);
1290 d = de_malloc(c, sizeof(lctx));
1291 d->csctx.input_encoding = de_get_input_encoding(c, NULL, DE_ENCODING_CP437);
1293 sig = (UI)de_getu16le(0);
1294 if(sig==0x5a4d || sig==0x4d5a) {
1295 d->need_errmsg = 1;
1296 goto done;
1299 grabber_id_com(c, (sig&0xff), &gctx->gi);
1300 if(!gctx->gi.is_grabber) {
1301 d->need_errmsg = 1;
1302 goto done;
1305 de_dbg(c, "format class: %u", gctx->gi.fmt_class);
1306 decode_grabber_com(c, d, gctx);
1307 if(d->errflag) goto done;
1309 d->csctx.height_in_chars = de_pad_to_n(gctx->data_len, d->csctx.width_in_chars*2) /
1310 (d->csctx.width_in_chars*2);
1311 de_dbg(c, "screen size: %"I64_FMT DE_CHAR_TIMES "%"I64_FMT, d->csctx.width_in_chars,
1312 d->csctx.height_in_chars);
1313 if(gctx->data_pos+gctx->data_len > c->infile->len) {
1314 d->need_errmsg = 1;
1315 goto done;
1318 d->csctx.use_default_pal = 1;
1319 d->csctx.inf = c->infile;
1320 d->csctx.inf_pos = gctx->data_pos;
1321 d->csctx.inf_len = gctx->data_len;
1322 fmtutil_char_simple_run(c, &d->csctx, gctx->charctx);
1324 done:
1325 if(d) {
1326 if(d->need_errmsg) {
1327 de_err(c, "Unsupported GRABBER format");
1329 free_lctx(c, d);
1331 if(gctx) {
1332 de_free_charctx(c, gctx->charctx);
1336 static int de_identify_grabber(deark *c)
1338 struct grabber_id_data gi;
1339 u8 b0;
1341 if(c->infile->len>65280) return 0;
1342 b0 = de_getbyte(0);
1343 if(b0!=0xe9 && b0!=0xfb) return 0;
1345 grabber_id_com(c, b0, &gi);
1346 if(gi.is_grabber) return 100;
1347 return 0;
1350 void de_module_grabber(deark *c, struct deark_module_info *mi)
1352 mi->id = "grabber";
1353 mi->desc = "GRABBER";
1354 mi->run_fn = de_run_grabber;
1355 mi->identify_fn = de_identify_grabber;
1356 mi->flags |= DE_MODFLAG_HIDDEN;