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
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
{
25 u8 has_palette
, has_font
, compression
, has_512chars
;
31 struct de_bitmap_font
*font
;
32 struct fmtutil_char_simplectx csctx
;
35 static void free_lctx(deark
*c
, lctx
*d
)
38 de_free(c
, d
->font
->char_array
);
39 de_destroy_bitmap_font(c
, d
->font
);
41 de_free(c
, d
->font_data
);
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);
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
);
57 fmtutil_handle_SAUCE(c
, c
->infile
, si
);
60 fmtutil_free_SAUCE(c
, si
);
64 static void xbin_decompress_data(deark
*c
, lctx
*d
, i64 pos1
, dbuf
*unc_data
)
78 while(pos
< c
->infile
->len
) {
79 if(xpos
>= d
->csctx
.width_in_chars
) {
83 if(ypos
>= d
->csctx
.height_in_chars
) {
87 b
= de_getbyte_p(&pos
);
89 count
= (i64
)(b
&0x3f) +1;
92 case 0: // Uncompressed
93 dbuf_copy(c
->infile
, pos
, count
*2, unc_data
);
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
);
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
);
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
);
126 static void do_read_palette(deark
*c
, lctx
*d
,struct de_char_context
*charctx
,
127 i64 pos
, int adf_style
)
135 de_dbg(c
, "palette at %"I64_FMT
, pos
);
137 for(k
=0; k
<16; k
++) {
142 else if(k
==6) idx
= 20;
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
)
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
)
174 struct de_crcobj
*crco
;
176 de_dbg(c
, "font at %"I64_FMT
", %"I64_FMT
" bytes", pos
, d
->font_data_len
);
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")) {
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
);
196 de_dbg_indent(c
, -1);
199 // Finish populating the d->font struct.
200 static int do_generate_font(deark
*c
, lctx
*d
)
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");
210 if(d
->font_data_len
!=d
->font
->num_chars
*d
->font_height
) {
211 de_err(c
, "Incorrect font data size");
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
];
231 static void de_run_xbin(deark
*c
, de_module_params
*mparams
)
234 struct de_char_context
*charctx
= NULL
;
235 struct de_SAUCE_detection_data sdd
;
236 struct de_SAUCE_info
*si
= NULL
;
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);
249 si
= fmtutil_create_SAUCE(c
);
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;
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
);
291 do_read_palette(c
, d
, charctx
, pos
, 0);
295 de_dbg(c
, "using default palette");
296 d
->csctx
.use_default_pal
= 1;
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
);
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
;
329 if(d
->has_512chars
) {
330 de_err(c
, "This type of XBIN file is not supported.");
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
);
345 de_dbg(c
, "image data at %"I64_FMT
, pos
);
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
);
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
);
360 dbuf_close(unc_data
);
361 de_free_charctx_screens(c
, charctx
);
362 de_destroy_charctx(c
, charctx
);
363 fmtutil_free_SAUCE(c
, si
);
367 static int de_identify_xbin(deark
*c
)
369 if(!dbuf_memcmp(c
->infile
, 0, "XBIN\x1a", 5))
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
)
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
)
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;
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");
410 width_req
= de_atoi(s
);
413 fmtutil_detect_SAUCE(c
, c
->infile
, &sdd
, 0x1);
415 si
= fmtutil_create_SAUCE(c
);
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) {
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
;
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;
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
);
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
);
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");
493 if(c
->detection_data
->sauce
.has_SAUCE
) {
494 if(c
->detection_data
->sauce
.data_type
==5)
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
)
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
)
523 struct de_char_context
*charctx
= NULL
;
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
;
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;
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
);
586 de_free_charctx(c
, charctx
);
590 static int de_identify_artworx_adf(deark
*c
)
594 // TODO: This detection algorithm will fail if there is a SAUCE record.
596 if(c
->infile
->len
< 1+192+4096+160) {
599 if((c
->infile
->len
- (1+192+4096))%160 != 0) {
602 if(!de_input_file_has_ext(c
, "adf")) return 0;
604 // I don't know what version numbers are allowed, but I'll assume the
605 // version number should be small.
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)) {
650 void de_module_icedraw(deark
*c
, struct deark_module_info
*mi
)
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
{
666 i64 next_xpos
, next_ypos
;
667 i64 max_xpos
, max_ypos
;
668 i64 ypos_of_last_nonblank
;
671 struct thedrawcom_ctx
{
673 struct de_char_context
*charctx
;
675 struct de_crcobj
*crco
;
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
;
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
) {
721 tdc
->d
->need_errmsg
= 1;
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
);
736 if(tdc
->d
->errflag
) goto done
;
737 if(pos
>= endpos
) break;
739 b0
= de_getbyte_p(&pos
);
742 tdc_decrunch_emit_char(c
, tdc
, b0
);
746 dc
->curr_fg_code
= b0
;
749 dc
->curr_bg_code
= (b0
-16)<<4;
751 else if(b0
==24) { // Newline
756 if(!dc
->prescan_mode
) {
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
788 tdc
->d
->need_errmsg
= 1;
793 // Decompression finished normally
795 if((dc
->max_xpos
+1 > THEDRAWCOM_MAX_WIDTH
) ||
796 (dc
->max_ypos
+1 > THEDRAWCOM_MAX_HEIGHT
))
799 tdc
->d
->need_errmsg
= 1;
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;
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
);
819 dbuf_flush(tdc
->unc_data
);
821 if(tdc
->d
->need_errmsg
) {
822 de_err(c
, "Decompression failed");
823 tdc
->d
->need_errmsg
= 0;
828 static void de_run_thedraw_com(deark
*c
, de_module_params
*mparams
)
830 struct thedrawcom_ctx
*tdc
= NULL
;
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
;
852 else if(tdc
->fmt_subtype
==1) {
853 tdc
->viewer_len
= 68;
854 tdc
->viewer_expected_crc
= 0xe427d2e3U
;
857 else if(tdc
->fmt_subtype
==2) {
858 tdc
->viewer_len
= 177;
859 tdc
->viewer_expected_crc
= 0x492a698d;
863 de_err(c
, "Unsupported format subtype: %u", (UI
)tdc
->fmt_subtype
);
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) {
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
905 de_dbg(c
, "data pos: %"I64_FMT
, tdc
->data_pos
);
907 if(tdc
->fmt_subtype
==2) {
908 de_dbg(c
, "decompressing");
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);
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;
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
);
935 if(tdc
->d
&& tdc
->d
->need_errmsg
) {
936 de_err(c
, "Failed to decode this file");
939 dbuf_close(tdc
->unc_data
);
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
)
954 if(c
->infile
->len
>65280) return 0;
956 if(n1
!=0xeb) return 0;
960 // Check format subtype & viewer start position.
961 if((n2
==0 && n1
==0x3d) ||
962 (n2
==1 && n1
==0x18) ||
971 if(!dbuf_memcmp(c
->infile
, 9, (const void*)"TheDraw COM file", 16)) {
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) {
987 if(!dbuf_memcmp(c
->infile
, 0x1a,
988 (const void*)"\xb4\x0f\xcd\x10\x8b\x3e\x07\x01\xbe\x5e", 10))
994 if(!dbuf_memcmp(c
->infile
, 0x3f,
995 (const void*)"\xb4\x0f\xcd\x10\xbb\x00\xb8\x3c\x02\x74", 10))
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
{
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
));
1029 adi
->jmppos
= de_geti16le(1) + 3;
1032 adi
->jmppos
= de_getbyte(1) + 2;
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))
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.)
1078 static void de_run_aciddraw_com(deark
*c
, de_module_params
*mparams
)
1080 struct aciddraw_ctx
*adctx
= NULL
;
1084 struct aciddraw_id_data adi
;
1085 i64 data_pos
, data_len
;
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
) {
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);
1120 de_dbg(c
, "version: v1.%u-like", fmtver
);
1122 data_pos
= de_getu16le(adi
.jmppos
-24);
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
) {
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
);
1149 if(d
->need_errmsg
) {
1150 de_err(c
, "Unsupported ACiDDraw format");
1155 de_free_charctx(c
, adctx
->charctx
);
1156 fmtutil_free_SAUCE(c
, adctx
->si
);
1160 static int de_identify_aciddraw_com(deark
*c
)
1163 struct aciddraw_id_data adi
;
1165 if(c
->infile
->len
>65280) return 0;
1167 if(n1
!=0xe9 && n1
!=0xeb) return 0;
1168 aciddraw_id(c
, n1
, &adi
);
1169 if(adi
.is_aciddraw
) {
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
{
1191 struct grabber_ctx
{
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
));
1203 if(!dbuf_memcmp(c
->infile
, 1,
1204 (const void*)"\xbe\x81\x00\x8a\x4c\xff\x30\xed\x09\xc9\x74", 11)) {
1206 gi
->fmt_class
= 200;
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))
1218 gi
->fmt_class
= 300;
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))
1225 gi
->fmt_class
= 334;
1230 static void decode_grabber_com(deark
*c
, lctx
*d
, struct grabber_ctx
*gctx
)
1233 i64 pos_of_data_ptr
;
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,
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;
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) {
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;
1282 static void de_run_grabber(deark
*c
, de_module_params
*mparams
)
1284 struct grabber_ctx
*gctx
= NULL
;
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) {
1299 grabber_id_com(c
, (sig
&0xff), &gctx
->gi
);
1300 if(!gctx
->gi
.is_grabber
) {
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
) {
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
);
1326 if(d
->need_errmsg
) {
1327 de_err(c
, "Unsupported GRABBER format");
1332 de_free_charctx(c
, gctx
->charctx
);
1336 static int de_identify_grabber(deark
*c
)
1338 struct grabber_id_data gi
;
1341 if(c
->infile
->len
>65280) return 0;
1343 if(b0
!=0xe9 && b0
!=0xfb) return 0;
1345 grabber_id_com(c
, b0
, &gi
);
1346 if(gi
.is_grabber
) return 100;
1350 void de_module_grabber(deark
*c
, struct deark_module_info
*mi
)
1353 mi
->desc
= "GRABBER";
1354 mi
->run_fn
= de_run_grabber
;
1355 mi
->identify_fn
= de_identify_grabber
;
1356 mi
->flags
|= DE_MODFLAG_HIDDEN
;