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
9 #include <deark-config.h>
10 #include <deark-private.h>
11 #include <deark-fmtutil.h>
12 DE_DECLARE_MODULE(de_module_xbin
);
13 DE_DECLARE_MODULE(de_module_bintext
);
14 DE_DECLARE_MODULE(de_module_artworx_adf
);
15 DE_DECLARE_MODULE(de_module_icedraw
);
17 typedef struct localctx_struct
{
18 i64 width_in_chars
, height_in_chars
;
20 u8 has_palette
, has_font
, compression
, nonblink
, has_512chars
;
25 struct de_bitmap_font
*font
;
28 static void do_bin_main(deark
*c
, lctx
*d
, dbuf
*unc_data
, struct de_char_context
*charctx
)
33 struct de_char_screen
*screen
;
34 struct de_encconv_state es
;
36 charctx
->nscreens
= 1;
37 charctx
->screens
= de_mallocarray(c
, charctx
->nscreens
, sizeof(struct de_char_screen
*));
38 charctx
->screens
[0] = de_malloc(c
, sizeof(struct de_char_screen
));
39 screen
= charctx
->screens
[0];
40 screen
->width
= d
->width_in_chars
;
41 screen
->height
= d
->height_in_chars
;
42 screen
->cell_rows
= de_mallocarray(c
, d
->height_in_chars
, sizeof(struct de_char_cell
*));
43 de_encconv_init(&es
, DE_ENCODING_CP437_G
);
45 for(j
=0; j
<d
->height_in_chars
; j
++) {
46 screen
->cell_rows
[j
] = de_mallocarray(c
, d
->width_in_chars
, sizeof(struct de_char_cell
));
48 for(i
=0; i
<d
->width_in_chars
; i
++) {
49 ccode
= dbuf_getbyte(unc_data
, j
*d
->width_in_chars
*2 + i
*2);
50 acode
= dbuf_getbyte(unc_data
, j
*d
->width_in_chars
*2 + i
*2 + 1);
52 if((acode
&0x80) && !d
->nonblink
) {
53 screen
->cell_rows
[j
][i
].blink
= 1;
57 fgcol
= (acode
& 0x0f);
58 bgcol
= (acode
& 0xf0) >> 4;
60 screen
->cell_rows
[j
][i
].fgcol
= (u32
)fgcol
;
61 screen
->cell_rows
[j
][i
].bgcol
= (u32
)bgcol
;
62 screen
->cell_rows
[j
][i
].codepoint
= (i32
)ccode
;
63 screen
->cell_rows
[j
][i
].codepoint_unicode
= de_char_to_unicode_ex((i32
)ccode
, &es
);
67 de_char_output_to_file(c
, charctx
);
70 static void do_uncompress_data(deark
*c
, lctx
*d
, i64 pos1
, dbuf
*unc_data
)
84 while(pos
< c
->infile
->len
) {
85 if(xpos
>= d
->width_in_chars
) {
89 if(ypos
>= d
->height_in_chars
) {
96 count
= (i64
)(b
&0x3f) +1;
99 case 0: // Uncompressed
100 dbuf_copy(c
->infile
, pos
, count
*2, unc_data
);
103 case 1: // Character compression
104 b1
= de_getbyte(pos
++); // character code
105 for(k
=0; k
<count
; k
++) {
106 b2
= de_getbyte(pos
++); // attribute code
107 dbuf_writebyte(unc_data
, b1
);
108 dbuf_writebyte(unc_data
, b2
);
111 case 2: // Attribute compression
112 b2
= de_getbyte(pos
++); // attribute code
113 for(k
=0; k
<count
; k
++) {
114 b1
= de_getbyte(pos
++); // character code
115 dbuf_writebyte(unc_data
, b1
);
116 dbuf_writebyte(unc_data
, b2
);
119 case 3: // Character/Attribute compression
120 b1
= de_getbyte(pos
++); // character code
121 b2
= de_getbyte(pos
++); // attribute code
122 for(k
=0; k
<count
; k
++) {
123 dbuf_writebyte(unc_data
, b1
);
124 dbuf_writebyte(unc_data
, b2
);
133 static void do_read_palette(deark
*c
, lctx
*d
,struct de_char_context
*charctx
,
134 i64 pos
, int adf_style
)
142 de_dbg(c
, "palette at %d", (int)pos
);
144 for(k
=0; k
<16; k
++) {
149 else if(k
==6) idx
= 20;
152 cr1
= de_getbyte(cpos
);
153 cg1
= de_getbyte(cpos
+1);
154 cb1
= de_getbyte(cpos
+2);
155 cr2
= de_scale_63_to_255(cr1
);
156 cg2
= de_scale_63_to_255(cg1
);
157 cb2
= de_scale_63_to_255(cb1
);
158 charctx
->pal
[k
] = DE_MAKE_RGB(cr2
, cg2
, cb2
);
159 de_snprintf(tmps
, sizeof(tmps
), "(%2d,%2d,%2d) "DE_CHAR_RIGHTARROW
" ",
160 (int)cr1
, (int)cg1
, (int)cb1
);
161 de_dbg_pal_entry2(c
, k
, charctx
->pal
[k
], tmps
, NULL
, NULL
);
165 static void do_default_palette(deark
*c
, lctx
*d
, struct de_char_context
*charctx
)
169 for(k
=0; k
<16; k
++) {
170 charctx
->pal
[k
] = de_palette_pc16(k
);
174 static void do_extract_font(deark
*c
, lctx
*d
)
178 if(!d
->has_font
|| !d
->font
) return;
179 fi
= de_finfo_create(c
);
180 de_finfo_set_name_from_sz(c
, fi
, "font", 0, DE_ENCODING_ASCII
);
182 de_font_bitmap_font_to_image(c
, d
->font
, fi
, DE_CREATEFLAG_IS_AUX
);
184 de_finfo_destroy(c
, fi
);
187 static void do_read_font_data(deark
*c
, lctx
*d
, i64 pos
)
190 struct de_crcobj
*crco
;
192 de_dbg(c
, "font at %d, %d bytes", (int)pos
, (int)d
->font_data_len
);
194 d
->font_data
= de_malloc(c
, d
->font_data_len
);
195 de_read(d
->font_data
, pos
, d
->font_data_len
);
197 crco
= de_crcobj_create(c
, DE_CRCOBJ_CRC32_IEEE
);
198 de_crcobj_addbuf(crco
, d
->font_data
, d
->font_data_len
);
199 crc
= de_crcobj_getval(crco
);
200 de_crcobj_destroy(crco
);
202 d
->is_standard_font
= de_font_is_standard_vga_font(c
, crc
);
203 de_dbg(c
, "font crc: 0x%08x (%s)", (unsigned int)crc
,
204 d
->is_standard_font
?"known CP437 font":"unrecognized");
206 if(de_get_ext_option(c
, "font:dumpvgafont")) {
208 df
= dbuf_create_output_file(c
, "font.dat", NULL
, DE_CREATEFLAG_IS_AUX
);
209 dbuf_write(df
, d
->font_data
, d
->font_data_len
);
212 de_dbg_indent(c
, -1);
215 // Finish populating the d->font struct.
216 static int do_generate_font(deark
*c
, lctx
*d
)
219 struct de_encconv_state es
;
221 if(!d
->font
) return 0;
222 if(d
->font
->num_chars
!=256) {
223 de_err(c
, "Only 256-character fonts are supported");
226 if(d
->font_data_len
!=d
->font
->num_chars
*d
->font_height
) {
227 de_err(c
, "Incorrect font data size");
230 d
->font
->nominal_width
= 8;
231 d
->font
->nominal_height
= (int)d
->font_height
;
232 d
->font
->char_array
= de_mallocarray(c
, d
->font
->num_chars
, sizeof(struct de_bitmap_font_char
));
233 de_encconv_init(&es
, DE_ENCODING_CP437_G
);
235 for(i
=0; i
<d
->font
->num_chars
; i
++) {
236 d
->font
->char_array
[i
].codepoint_nonunicode
= (i32
)i
;
237 d
->font
->char_array
[i
].codepoint_unicode
= de_char_to_unicode_ex((i32
)i
, &es
);
238 d
->font
->char_array
[i
].width
= d
->font
->nominal_width
;
239 d
->font
->char_array
[i
].height
= d
->font
->nominal_height
;
240 d
->font
->char_array
[i
].rowspan
= 1;
241 d
->font
->char_array
[i
].bitmap
= &d
->font_data
[i
*d
->font_height
];
247 static void free_lctx(deark
*c
, lctx
*d
)
250 de_free(c
, d
->font
->char_array
);
251 de_destroy_bitmap_font(c
, d
->font
);
253 de_free(c
, d
->font_data
);
257 static void de_run_xbin(deark
*c
, de_module_params
*mparams
)
260 struct de_char_context
*charctx
= NULL
;
261 struct de_SAUCE_detection_data sdd
;
262 struct de_SAUCE_info
*si
= NULL
;
265 dbuf
*unc_data
= NULL
;
267 d
= de_malloc(c
, sizeof(lctx
));
269 charctx
= de_create_charctx(c
, 0);
270 charctx
->prefer_image_output
= 1;
271 de_char_decide_output_format(c
, charctx
);
273 fmtutil_detect_SAUCE(c
, c
->infile
, &sdd
, 0x1);
275 si
= fmtutil_create_SAUCE(c
);
278 fmtutil_handle_SAUCE(c
, c
->infile
, si
);
279 de_dbg_indent(c
, -1);
281 charctx
->title
= si
->title
;
282 charctx
->artist
= si
->artist
;
283 charctx
->organization
= si
->organization
;
284 charctx
->creation_date
= si
->creation_date
;
285 charctx
->comment
= si
->comment
;
288 d
->width_in_chars
= de_getu16le(5);
289 d
->height_in_chars
= de_getu16le(7);
290 d
->font_height
= (i64
)de_getbyte(9);
292 flags
= de_getbyte(10);
293 de_dbg(c
, "dimensions: %d"DE_CHAR_TIMES
"%d characters", (int)d
->width_in_chars
, (int)d
->height_in_chars
);
294 de_dbg(c
, "font height: %d", (int)d
->font_height
);
295 de_dbg(c
, "flags: 0x%02x", (unsigned int)flags
);
296 d
->has_palette
= (flags
&0x01)?1:0;
297 d
->has_font
= (flags
&0x02)?1:0;
298 d
->compression
= (flags
&0x04)?1:0;
299 d
->nonblink
= (flags
&0x08)?1:0;
300 d
->has_512chars
= (flags
&0x10)?1:0;
301 de_dbg(c
, " has palette: %d", (int)d
->has_palette
);
302 de_dbg(c
, " has font: %d", (int)d
->has_font
);
303 de_dbg(c
, " compression: %d", (int)d
->compression
);
304 de_dbg(c
, " non-blink mode: %d", (int)d
->nonblink
);
305 de_dbg(c
, " 512 character mode: %d", (int)d
->has_512chars
);
307 if(d
->has_font
&& (d
->font_height
<1 || d
->font_height
>32)) {
308 de_err(c
, "Invalid font height: %d", (int)d
->font_height
);
314 do_read_palette(c
, d
, charctx
, pos
, 0);
318 de_dbg(c
, "using default palette");
319 do_default_palette(c
, d
, charctx
);
323 d
->font
= de_create_bitmap_font(c
);
324 d
->font
->has_nonunicode_codepoints
= 1;
325 d
->font
->has_unicode_codepoints
= 1;
326 d
->font
->prefer_unicode
= 0;
327 d
->font
->num_chars
= d
->has_512chars
? 512 : 256;
328 d
->font_data_len
= d
->font
->num_chars
* d
->font_height
;
329 if(d
->font
->num_chars
!=256) {
330 de_err(c
, "%d-character mode is not supported", (int)d
->font
->num_chars
);
334 do_read_font_data(c
, d
, pos
);
335 pos
+= d
->font_data_len
;
337 if(d
->is_standard_font
) {
338 charctx
->suppress_custom_font_warning
= 1;
341 if(!do_generate_font(c
, d
)) goto done
;
343 if(c
->extract_level
>=2) {
344 do_extract_font(c
, d
);
347 charctx
->font
= d
->font
;
352 if(d
->has_512chars
) {
353 de_err(c
, "This type of XBIN file is not supported.");
357 if(d
->font_height
==0) {
358 // Not really legal, but we'll let it mean "default".
360 else if(d
->font_height
!=16) {
361 if(charctx
->outfmt
==1) { // image output
362 de_warn(c
, "Incompatible font height (%d), using 16 instead.", (int)d
->font_height
);
368 de_dbg(c
, "image data at %d", (int)pos
);
371 unc_data
= dbuf_create_membuf(c
, d
->width_in_chars
* d
->height_in_chars
* 2, 1);
372 do_uncompress_data(c
, d
, pos
, unc_data
);
375 unc_data
= dbuf_open_input_subfile(c
->infile
, pos
, c
->infile
->len
-pos
);
377 do_bin_main(c
, d
, unc_data
, charctx
);
380 dbuf_close(unc_data
);
381 de_free_charctx_screens(c
, charctx
);
382 de_destroy_charctx(c
, charctx
);
383 fmtutil_free_SAUCE(c
, si
);
387 static int de_identify_xbin(deark
*c
)
389 if(!dbuf_memcmp(c
->infile
, 0, "XBIN\x1a", 5))
394 static void de_help_xbin(deark
*c
)
396 de_msg(c
, "-opt char:output=html : Write HTML instead of an image file");
397 de_msg(c
, "-opt char:charwidth=<8|9> : Width of a character cell");
400 void de_module_xbin(deark
*c
, struct deark_module_info
*mi
)
403 mi
->desc
= "XBIN character graphics";
404 mi
->run_fn
= de_run_xbin
;
405 mi
->identify_fn
= de_identify_xbin
;
406 mi
->help_fn
= de_help_xbin
;
409 ////////////////////// Binary Text //////////////////////
411 static void de_run_bintext(deark
*c
, de_module_params
*mparams
)
414 struct de_char_context
*charctx
= NULL
;
415 struct de_SAUCE_detection_data sdd
;
416 struct de_SAUCE_info
*si
= NULL
;
417 dbuf
*unc_data
= NULL
;
418 i64 effective_file_size
= 0;
423 d
= de_malloc(c
, sizeof(lctx
));
425 charctx
= de_malloc(c
, sizeof(struct de_char_context
));
426 charctx
->prefer_image_output
= 0;
428 s
=de_get_ext_option(c
, "char:width");
430 width_req
= de_atoi(s
);
433 fmtutil_detect_SAUCE(c
, c
->infile
, &sdd
, 0x1);
435 si
= fmtutil_create_SAUCE(c
);
438 fmtutil_handle_SAUCE(c
, c
->infile
, si
);
439 de_dbg_indent(c
, -1);
441 charctx
->title
= si
->title
;
442 charctx
->artist
= si
->artist
;
443 charctx
->organization
= si
->organization
;
444 charctx
->creation_date
= si
->creation_date
;
445 charctx
->comment
= si
->comment
;
447 effective_file_size
= si
->original_file_size
;
449 if(si
->data_type
==5) {
452 if(si
->file_type
==1 && si
->tinfo1
>0) {
453 // Some files created by ACiDDraw do this.
454 d
->width_in_chars
= 2*(i64
)si
->tinfo1
;
457 // For BinText, the FileType field is inexplicably used for the width (usually).
458 d
->width_in_chars
= 2*(i64
)si
->file_type
;
461 if(si
->tflags
& 0x01) {
464 if((si
->tflags
& 0x18)>>3 == 0x02) {
465 // Square pixels requested
466 charctx
->no_density
= 1;
468 if((si
->tflags
& 0x06)>>1 == 0x02) {
469 charctx
->prefer_9col_mode
= 1;
475 d
->width_in_chars
= 160;
476 effective_file_size
= c
->infile
->len
;
479 if(width_req
>0) d
->width_in_chars
= width_req
;
481 if(d
->width_in_chars
<1) d
->width_in_chars
=160;
482 if(effective_file_size
%(d
->width_in_chars
*2)) {
483 de_warn(c
, "File does not contain a whole number of rows. The width may "
484 "be wrong. Try \"-opt char:width=...\".");
486 d
->height_in_chars
= effective_file_size
/ (d
->width_in_chars
*2);
488 de_dbg(c
, "width: %d chars", (int)d
->width_in_chars
);
489 de_dbg(c
, "calculated height: %d chars", (int)d
->height_in_chars
);
495 do_default_palette(c
, d
, charctx
);
497 unc_data
= dbuf_open_input_subfile(c
->infile
, 0, effective_file_size
);
498 do_bin_main(c
, d
, unc_data
, charctx
);
500 dbuf_close(unc_data
);
501 de_free_charctx(c
, charctx
);
502 fmtutil_free_SAUCE(c
, si
);
506 static int de_identify_bintext(deark
*c
)
508 if(!c
->detection_data
->SAUCE_detection_attempted
) {
509 // FIXME?: This is known to happen if "-disablemods sauce" was used.
510 de_err(c
, "bintext detection requires sauce module");
513 if(c
->detection_data
->sauce
.has_SAUCE
) {
514 if(c
->detection_data
->sauce
.data_type
==5)
522 static void de_help_bintext(deark
*c
)
524 de_msg(c
, "-opt char:output=image : Write an image file instead of HTML");
525 de_msg(c
, " -opt char:charwidth=<8|9> : Width of a character cell");
526 de_msg(c
, "-opt char:width=<n> : Number of characters per row");
529 void de_module_bintext(deark
*c
, struct deark_module_info
*mi
)
532 mi
->desc
= "Binary Text character graphics";
533 mi
->run_fn
= de_run_bintext
;
534 mi
->identify_fn
= de_identify_bintext
;
535 mi
->help_fn
= de_help_bintext
;
538 ////////////////////// ArtWorx Data Format (ADF) //////////////////////
540 static void de_run_artworx_adf(deark
*c
, de_module_params
*mparams
)
543 struct de_char_context
*charctx
= NULL
;
544 dbuf
*unc_data
= NULL
;
548 d
= de_malloc(c
, sizeof(lctx
));
550 // TODO: ADF files can probably have SAUCE records, so we should read
551 // the SAUCE data if present. But there does not seem to be a defined
552 // SAUCE file type for ADF.
554 charctx
= de_malloc(c
, sizeof(struct de_char_context
));
555 charctx
->prefer_image_output
= 1;
557 data_start
= 1+192+4096;
558 data_len
= c
->infile
->len
- data_start
;
559 if(data_len
<0) goto done
;
561 d
->width_in_chars
= 80;
562 d
->height_in_chars
= data_len
/ (d
->width_in_chars
*2);
564 de_dbg(c
, "guessed width: %d chars", (int)d
->width_in_chars
);
565 de_dbg(c
, "calculated height: %d chars", (int)d
->height_in_chars
);
566 if(d
->height_in_chars
<1) goto done
;
573 do_read_palette(c
, d
, charctx
, 1, 1);
576 // TODO: This duplicates a lot of the xbin code.
578 d
->font
= de_create_bitmap_font(c
);
579 d
->font
->has_nonunicode_codepoints
= 1;
580 d
->font
->has_unicode_codepoints
= 1;
581 d
->font
->prefer_unicode
= 0;
582 d
->font
->num_chars
= 256;
584 d
->font_data_len
= d
->font
->num_chars
* d
->font_height
;
586 do_read_font_data(c
, d
, 1+192);
588 if(d
->is_standard_font
) {
589 charctx
->suppress_custom_font_warning
= 1;
592 if(!do_generate_font(c
, d
)) goto done
;
594 if(c
->extract_level
>=2) {
595 do_extract_font(c
, d
);
598 charctx
->font
= d
->font
;
601 unc_data
= dbuf_open_input_subfile(c
->infile
, data_start
, data_len
);
602 do_bin_main(c
, d
, unc_data
, charctx
);
605 dbuf_close(unc_data
);
606 de_free_charctx(c
, charctx
);
610 static int de_identify_artworx_adf(deark
*c
)
614 // TODO: This detection algorithm will fail if there is a SAUCE record.
616 if(c
->infile
->len
< 1+192+4096+160) {
619 if((c
->infile
->len
- (1+192+4096))%160 != 0) {
622 if(!de_input_file_has_ext(c
, "adf")) return 0;
624 // I don't know what version numbers are allowed, but I'll assume the
625 // version number should be small.
630 static void de_help_artworx_adf(deark
*c
)
632 de_msg(c
, "-opt char:output=html : Write HTML instead of an image file");
633 de_msg(c
, "-opt char:charwidth=<8|9> : Width of a character cell");
634 de_msg(c
, "-opt char:width=<n> : Number of characters per row");
637 void de_module_artworx_adf(deark
*c
, struct deark_module_info
*mi
)
639 mi
->id
= "artworx_adf";
640 mi
->desc
= "ArtWorx Data Format (ADF)";
641 mi
->run_fn
= de_run_artworx_adf
;
642 mi
->identify_fn
= de_identify_artworx_adf
;
643 mi
->help_fn
= de_help_artworx_adf
;
646 ////////////////////// iCEDraw format (.idf) //////////////////////
648 // This module is not yet implemented. This stub exists because it seemed
649 // like the simplest way to accomplish multiple goals:
650 // * Avoid having iCEDraw mis-identified as ANSI Art.
651 // * Avoid an error message from the SAUCE module implying that ANSI
652 // Art is not a supported format.
653 // * Print debugging info about the SAUCE record, if present.
654 // * Print the same error message whether or not a SAUCE record is present.
656 static void de_run_icedraw(deark
*c
, de_module_params
*mparams
)
658 struct de_SAUCE_detection_data sdd
;
660 fmtutil_detect_SAUCE(c
, c
->infile
, &sdd
, 0x1);
662 // Read the SAUCE record if present, just for the debugging info.
663 struct de_SAUCE_info
*si
= NULL
;
664 si
= fmtutil_create_SAUCE(c
);
667 fmtutil_handle_SAUCE(c
, c
->infile
, si
);
668 de_dbg_indent(c
, -1);
670 fmtutil_free_SAUCE(c
, si
);
673 de_err(c
, "iCEDraw format is not supported");
676 static int de_identify_icedraw(deark
*c
)
678 if(!dbuf_memcmp(c
->infile
, 0, "\x04\x31\x2e\x34", 4)) {
684 void de_module_icedraw(deark
*c
, struct deark_module_info
*mi
)
687 mi
->desc
= "iCEDraw character graphics format";
688 mi
->run_fn
= de_run_icedraw
;
689 mi
->identify_fn
= de_identify_icedraw
;
690 mi
->flags
|= DE_MODFLAG_NONWORKING
;