1 // This file is part of Deark.
2 // Copyright (C) 2024 Jason Summers
3 // See the file COPYING for terms of use.
7 #include <deark-private.h>
8 DE_DECLARE_MODULE(de_module_reko
);
9 DE_DECLARE_MODULE(de_module_wizsolitaire
);
11 //----------------------------------------------------
15 #define MINCARDWIDTH 64
16 #define MAXCARDWIDTH 100
17 #define MINCARDHEIGHT 100
18 #define MAXCARDHEIGHT 150
25 typedef struct localctx_reko
{
30 u8 suppress_size_warnings
;
35 i64 amiga_row_stride
, amiga_plane_stride
;
44 i64 idx_of_first_main_card
;
48 static void read_header_amiga(deark
*c
, lctx
*d
)
51 int saved_indent_level
;
53 de_dbg_indent_save(c
, &saved_indent_level
);
54 de_dbg(c
, "header at %"I64_FMT
, pos
);
58 d
->bodysize
= de_getu32be_p(&pos
);
59 de_dbg(c
, "body size: %"I64_FMT
, d
->bodysize
);
60 d
->cardsize
= de_getu32be_p(&pos
);
61 de_dbg(c
, "card size: %"I64_FMT
, d
->cardsize
);
62 d
->h
= de_getu16be_p(&pos
);
63 d
->w
= de_getu16be_p(&pos
);
64 de_dbg_dimensions(c
, d
->w
, d
->h
);
65 d
->camg_mode
= (UI
)de_getu32be_p(&pos
);
66 de_dbg(c
, "CAMG mode: 0x%08x", d
->camg_mode
);
67 if(d
->camg_mode
& 0x0800) d
->ham_flag
= 1;
69 de_dbg(c
, "HAM: %u", (UI
)d
->ham_flag
);
71 d
->depth_pixel
= (UI
)de_getbyte_p(&pos
);
72 de_dbg(c
, "depth: %u", d
->depth_pixel
);
73 d
->numcards
= (i64
)de_getbyte_p(&pos
);
74 de_dbg(c
, "num cards: %"I64_FMT
, d
->numcards
);
76 if(d
->w
==96 && d
->depth_pixel
==4) {
77 ; // Hack. Found a file that's really like this.
79 else if(d
->w
>88 && d
->w
<=96) {
80 de_warn(c
, "Unexpected width %d; assuming it should be 88", (int)d
->w
);
82 d
->suppress_size_warnings
= 1;
85 if(d
->depth_pixel
<1 || d
->depth_pixel
>8) {
91 d
->depth_color
= d
->depth_pixel
;
93 if(d
->depth_pixel
!=6 && d
->depth_pixel
!=8) {
102 de_dbg_indent_restore(c
, saved_indent_level
);
105 static void read_palette_amiga(deark
*c
, lctx
*d
)
109 int saved_indent_level
;
111 de_dbg_indent_save(c
, &saved_indent_level
);
113 de_dbg(c
, "global palette at %"I64_FMT
, pos
);
115 numentries
= 1LL<<d
->depth_color
;
116 d
->globalpal_nbytes
= numentries
*3;
117 de_read_simple_palette(c
, c
->infile
, pos
, numentries
, 3, d
->pal
, 256,
118 DE_RDPALTYPE_24BIT
, DE_RDPALFLAG_NOHEADER
);
119 de_dbg_indent_restore(c
, saved_indent_level
);
122 static void read_header_pc(deark
*c
, lctx
*d
)
125 int saved_indent_level
;
127 de_dbg_indent_save(c
, &saved_indent_level
);
129 de_dbg(c
, "header at %"I64_FMT
, pos
);
133 d
->bodysize
= de_getu32le_p(&pos
);
134 de_dbg(c
, "body size: %"I64_FMT
, d
->bodysize
);
135 d
->cardsize
= de_getu32le_p(&pos
);
136 de_dbg(c
, "card size: %"I64_FMT
, d
->cardsize
);
137 d
->w
= de_getu16le_p(&pos
);
138 d
->h
= de_getu16le_p(&pos
);
139 de_dbg_dimensions(c
, d
->w
, d
->h
);
140 d
->depth_pixel
= (UI
)de_getbyte_p(&pos
);
141 de_dbg(c
, "depth: %u", d
->depth_pixel
);
142 d
->depth_color
= d
->depth_pixel
;
143 d
->numcards
= (i64
)de_getbyte_p(&pos
);
144 de_dbg(c
, "num cards: %"I64_FMT
, d
->numcards
);
147 de_dbg_indent_restore(c
, saved_indent_level
);
150 // TODO?: Support for RKP 24 is quick and dirty.
151 static void read_header_rkp24(deark
*c
, lctx
*d
)
154 int saved_indent_level
;
156 de_dbg_indent_save(c
, &saved_indent_level
);
158 de_dbg(c
, "header at %"I64_FMT
, pos
);
162 d
->cardsize
= de_getu32le_p(&pos
);
163 de_dbg(c
, "card size: %"I64_FMT
, d
->cardsize
);
164 d
->bodysize
= de_getu32le_p(&pos
);
165 de_dbg(c
, "body size: %"I64_FMT
, d
->bodysize
);
166 d
->w
= de_getu32le_p(&pos
);
167 d
->h
= de_getu32le_p(&pos
);
168 de_dbg_dimensions(c
, d
->w
, d
->h
);
169 d
->depth_pixel
= (UI
)de_getu32le_p(&pos
);
170 de_dbg(c
, "depth: %u", d
->depth_pixel
);
171 d
->depth_color
= d
->depth_pixel
;
172 d
->numcards
= (i64
)de_getu32le_p(&pos
);
173 de_dbg(c
, "num cards: %"I64_FMT
, d
->numcards
);
176 de_dbg_indent_restore(c
, saved_indent_level
);
179 static void read_image_pc8(deark
*c
, lctx
*d
, de_bitmap
*img
, i64 pos1
)
185 de_dbg(c
, "palette at %"I64_FMT
, pos
);
187 de_zeromem(d
->pal
, sizeof(d
->pal
));
188 for(k
=0; k
<256; k
++) {
191 clr_raw
= (u32
)de_getu16le_p(&pos
);
192 d
->pal
[k
] = de_rgb555_to_888(clr_raw
);
193 if(c
->debug_level
>=2) {
194 de_snprintf(tmps
, sizeof(tmps
), "0x%04x "DE_CHAR_RIGHTARROW
" ", (UI
)clr_raw
);
195 de_dbg_pal_entry2(c
, k
, d
->pal
[k
], tmps
, NULL
, NULL
);
198 de_dbg_indent(c
, -1);
200 de_dbg(c
, "image at %"I64_FMT
, pos
);
201 de_convert_image_paletted(c
->infile
, pos
, 8, d
->w
, d
->pal
, img
, 0);
204 // TODO?: Make this a library function, and consolidate with the ILBM functions.
205 static void convert_image_planar_HAM(dbuf
*f
, i64 fpos
, i64 nplanes
,
206 i64 row_stride
, i64 plane_stride
, const de_color
*pal
, de_bitmap
*img
)
210 i64 units_per_row
; // num bytes per row per plane that we will process
213 de_zeromem(pbit
, sizeof(pbit
));
217 else if(nplanes
==8) {
222 units_per_row
= (img
->width
+ 7)/8;
224 for(ypos
=0; ypos
<img
->height
; ypos
++) {
228 cr
= DE_COLOR_R(pal
[0]) >> pixshift1
;
229 cg
= DE_COLOR_G(pal
[0]) >> pixshift1
;
230 cb
= DE_COLOR_B(pal
[0]) >> pixshift1
;
232 // Read 8 bits from each plane, then rearrange to make 8 output pixels.
233 for(n
=0; n
<units_per_row
; n
++) {
239 for(pn
=0; pn
<(UI
)nplanes
; pn
++) {
240 b
= dbuf_getbyte(f
, fpos
+ ypos
*row_stride
+ pn
*plane_stride
+ n
);
252 for(pn
=0; pn
<(UI
)nplanes
; pn
++) {
253 if((pbit
[pn
] & (1U<<(7-k
)))!=0) {
259 pixval_code
= pixval
>> 4;
260 pixval_color
= pixval
& 0x0f;
263 pixval_code
= pixval
>> 6;
264 pixval_color
= pixval
& 0x3f;
267 switch(pixval_code
) {
268 case 0x1: // Modify blue value
271 case 0x2: // Modify red value
274 case 0x3: // Modify green value
277 default: // 0: Use colormap value
278 clr
= pal
[(UI
)pixval_color
];
279 cr
= DE_COLOR_R(clr
) >> pixshift1
;
280 cg
= DE_COLOR_G(clr
) >> pixshift1
;
281 cb
= DE_COLOR_B(clr
) >> pixshift1
;
291 cr2
= (cr
<<2) | (cr
>>4);
292 cg2
= (cg
<<2) | (cg
>>4);
293 cb2
= (cb
<<2) | (cb
>>4);
296 de_bitmap_setpixel_rgba(img
, xpos
, ypos
, DE_MAKE_RGB(cr2
, cg2
, cb2
));
305 static void read_image_amiga(deark
*c
, lctx
*d
, de_bitmap
*img
, i64 pos1
)
308 convert_image_planar_HAM(c
->infile
, pos1
, d
->depth_pixel
,
309 d
->amiga_row_stride
, d
->amiga_plane_stride
, d
->pal
, img
);
312 de_convert_image_paletted_planar(c
->infile
, pos1
, d
->depth_pixel
,
313 d
->amiga_row_stride
, d
->amiga_plane_stride
, d
->pal
, img
, 0x02);
317 static void read_image_pc16(deark
*c
, lctx
*d
, de_bitmap
*img
, i64 pos1
)
322 de_dbg(c
, "image at %"I64_FMT
, pos
);
323 for(j
=0; j
<d
->h
; j
++) {
324 for(i
=0; i
<d
->w
; i
++) {
328 clr_raw
= (u32
)de_getu16le_p(&pos
);
329 clr
= de_rgb555_to_888(clr_raw
);
330 de_bitmap_setpixel_rgb(img
, i
, j
, clr
);
335 static void set_reko_card_filename(deark
*c
, i64 idx_of_first_main_card
,
336 i64 cardidx
, de_finfo
*fi
)
338 static const char *cnames
= "a23456789tjqk";
339 static const char *snames
= "cdhs";
342 if(cardidx
>=idx_of_first_main_card
&& cardidx
<idx_of_first_main_card
+52) {
343 nbuf
[0] = cnames
[(cardidx
-idx_of_first_main_card
)/4];
344 nbuf
[1] = snames
[(cardidx
-idx_of_first_main_card
)%4];
347 else if(cardidx
==0) {
348 de_strlcpy(nbuf
, "back", sizeof(nbuf
));
351 de_strlcpy(nbuf
, "other", sizeof(nbuf
));
354 de_finfo_set_name_from_sz(c
, fi
, nbuf
, 0, DE_ENCODING_LATIN1
);
357 static void reko_main_rkp24(deark
*c
, lctx
*d
)
360 int saved_indent_level
;
366 de_dbg_indent_save(c
, &saved_indent_level
);
368 de_dbg(c
, "cards at %"I64_FMT
, pos
);
371 fi
= de_finfo_create(c
);
381 if(d
->numcards
<1 || d
->numcards
>MAXCARDS
) {
387 for(cardidx
=0; cardidx
<d
->numcards
; cardidx
++) {
389 i64 nbytes_to_extract
= 0;
390 i64 this_cardsize
= 0;
393 de_dbg(c
, "card #%"I64_FMT
" at %"I64_FMT
, cardidx
, pos
);
398 if(de_getu16be(extract_pos
) != 0xffd8) {
403 nbytes_to_extract
= de_getu32le(pos
);
404 this_cardsize
= 4+nbytes_to_extract
;
407 if(de_getu16be(pos
) != 0x424d) {
413 nbytes_to_extract
= d
->cardsize
;
414 this_cardsize
= d
->cardsize
;
417 if(extract_pos
+nbytes_to_extract
> c
->infile
->len
) {
423 set_reko_card_filename(c
, d
->idx_of_first_main_card
, cardidx
, fi
);
424 outf
= dbuf_create_output_file(c
, ext
, fi
, 0);
425 dbuf_copy(c
->infile
, extract_pos
, nbytes_to_extract
, outf
);
428 pos
+= this_cardsize
;
429 de_dbg_indent(c
, -1);
433 de_finfo_destroy(c
, fi
);
434 de_dbg_indent_restore(c
, saved_indent_level
);
437 static void reko_main(deark
*c
, lctx
*d
)
439 int saved_indent_level
;
441 i64 expected_cardsize
;
443 de_bitmap
*cardimg
= NULL
;
444 de_bitmap
*canvas
= NULL
;
447 i64 localhdrsize
= 0;
448 i64 canvas_cols
, canvas_rows
;
449 i64 canvas_w
, canvas_h
;
450 i64 cxpos_maincards
, cypos_maincards
;
451 i64 cxpos_extracards
, cypos_extracards
;
453 de_dbg_indent_save(c
, &saved_indent_level
);
454 cards_pos
= d
->hdrsize
+ d
->globalpal_nbytes
;
455 de_dbg(c
, "cards at %"I64_FMT
, cards_pos
);
458 fi
= de_finfo_create(c
);
461 if(d
->depth_pixel
!=8 && d
->depth_pixel
!=16) {
468 if(d
->numcards
<1 || d
->numcards
>MAXCARDS
||
469 d
->w
<MINCARDWIDTH
|| d
->w
>MAXCARDWIDTH
||
470 d
->h
<MINCARDHEIGHT
|| d
->h
>MAXCARDHEIGHT
)
477 if(d
->numcards
< d
->idx_of_first_main_card
+52) {
478 de_warn(c
, "Expected at least %"I64_FMT
" cards; only found %"I64_FMT
,
479 d
->idx_of_first_main_card
+52, d
->numcards
);
483 expected_cardsize
= d
->w
* d
->h
* (d
->depth_pixel
/8);
486 i64 bytes_per_row_per_plane
;
488 // We expect the width to be a multiple of 8. Other widths are not
490 bytes_per_row_per_plane
= (d
->w
)/8;
491 d
->amiga_plane_stride
= bytes_per_row_per_plane
;
492 d
->amiga_row_stride
= bytes_per_row_per_plane
* d
->depth_pixel
;
493 expected_cardsize
= (d
->w
* d
->h
* d
->depth_pixel
)/8;
496 if(d
->cardsize
!=expected_cardsize
&& !d
->suppress_size_warnings
) {
497 de_warn(c
, "Reported cardsize is %"I64_FMT
"; expected %"I64_FMT
,
498 d
->cardsize
, expected_cardsize
);
499 d
->suppress_size_warnings
= 1;
502 d
->localpal_nbytes
= 0;
505 if(d
->depth_pixel
==8) {
506 d
->localpal_nbytes
= 512;
509 full_cardsize
= localhdrsize
+ d
->localpal_nbytes
+ d
->cardsize
;
512 canvas_rows
= (d
->numcards
+12) / 13;
513 // There has to be at least 1 extra card, so we need at least 5 rows.
514 if(canvas_rows
<5) canvas_rows
= 5;
517 cxpos_extracards
= 0;
518 cypos_extracards
= 4;
520 #define REKO_BORDER 2
521 canvas_w
= canvas_cols
*(d
->w
+REKO_BORDER
)-REKO_BORDER
;
522 canvas_h
= canvas_rows
*(d
->h
+REKO_BORDER
)-REKO_BORDER
;
523 if(d
->combine_images
) {
524 canvas
= de_bitmap_create(c
, canvas_w
, canvas_h
, 4);
527 cardimg
= de_bitmap_create(c
, d
->w
, d
->h
, 3);
529 for(cardidx
=0; cardidx
<d
->numcards
; cardidx
++) {
531 i64 dstcxpos
, dstcypos
;
532 i64 dstxpos
, dstypos
;
535 thiscardpos
= cards_pos
+ cardidx
*full_cardsize
;
536 de_dbg(c
, "card #%"I64_FMT
" at %"I64_FMT
, cardidx
, thiscardpos
);
539 if(d
->is_pc
&& !d
->fatalerrflag
&& (thiscardpos
+localhdrsize
<= c
->infile
->len
)) {
542 cw_raw
= de_getu16le(thiscardpos
);
543 ch_raw
= de_getu16le(thiscardpos
+2);
544 if((cw_raw
+1 != d
->w
) || (ch_raw
+1 != d
->h
)) {
545 de_err(c
, "Card #%d: Bad card header", (int)cardidx
);
551 de_bitmap_rect(cardimg
, 0, 0, d
->w
, d
->h
, DE_STOCKCOLOR_BLACK
, 0);
553 if(d
->fatalerrflag
) {
557 if(d
->depth_pixel
==8) {
558 read_image_pc8(c
, d
, cardimg
, thiscardpos
+localhdrsize
);
561 read_image_pc16(c
, d
, cardimg
, thiscardpos
+localhdrsize
);
565 read_image_amiga(c
, d
, cardimg
, thiscardpos
);
568 is_main_card
= (cardidx
>=d
->idx_of_first_main_card
&&
569 cardidx
<(d
->idx_of_first_main_card
+52));
571 dstcxpos
= cxpos_maincards
;
572 dstcypos
= cypos_maincards
;
574 if(cypos_maincards
>=4) {
580 dstcxpos
= cxpos_extracards
;
581 dstcypos
= cypos_extracards
;
583 if(cxpos_extracards
>=13) {
584 cxpos_extracards
= 0;
588 dstxpos
= dstcxpos
*(d
->w
+REKO_BORDER
);
589 dstypos
= dstcypos
*(d
->h
+REKO_BORDER
);
591 de_bitmap_copy_rect(cardimg
, canvas
, 0, 0, d
->w
, d
->h
, dstxpos
, dstypos
, 0);
594 set_reko_card_filename(c
, d
->idx_of_first_main_card
, cardidx
, fi
);
595 de_bitmap_write_to_file_finfo(cardimg
, fi
, 0);
598 if(!d
->fatalerrflag
&& (thiscardpos
+full_cardsize
> c
->infile
->len
)) {
599 de_err(c
, "Premature end of file");
601 // (But keep going, so we draw the full image template.)
604 if(d
->fatalerrflag
&& !canvas
) goto done
;
606 de_dbg_indent(c
, -1);
610 de_bitmap_write_to_file(canvas
, NULL
, 0);
614 de_bitmap_destroy(cardimg
);
615 de_bitmap_destroy(canvas
);
616 de_finfo_destroy(c
, fi
);
617 de_dbg_indent_restore(c
, saved_indent_level
);
620 static u8
reko_fmt_from_sig(deark
*c
)
624 de_read(buf
, 0, sizeof(buf
));
626 if(!de_memcmp(buf
, (const void*)"REKO", 4)) {
627 // Just to screen out text files, I think we can assume the high byte
628 // of the cardsize field is 0.
629 if(de_getbyte(8) == 0) {
633 if(!de_memcmp(buf
, (const void*)"PCREKO", 6)) {
634 if(buf
[6]=='D' && buf
[7]==0x20) return RKFMT_RKP8
;
635 if(buf
[6]==0 && buf
[7]==0) return RKFMT_RKP16
;
637 if(!de_memcmp(buf
, (const void*)"PCRKP\0", 6)) {
643 static void de_run_reko(deark
*c
, de_module_params
*mparams
)
648 d
= de_malloc(c
, sizeof(lctx
));
649 d
->combine_images
= (u8
)de_get_ext_option_bool(c
, "reko:combine", 1);
651 d
->fmt
= reko_fmt_from_sig(c
);
653 de_err(c
, "Unsupported REKO version");
656 if(d
->fmt
==RKFMT_RKP8
) fmtname
= "RKP 8";
657 else if(d
->fmt
==RKFMT_RKP16
) fmtname
= "RKP 16";
658 else if(d
->fmt
==RKFMT_RKP24
) fmtname
= "RKP 24";
659 else fmtname
= "Amiga";
660 de_declare_fmtf(c
, "REKO cardset (%s)", fmtname
);
662 if(d
->fmt
==RKFMT_RKP8
|| d
->fmt
==RKFMT_RKP16
|| d
->fmt
==RKFMT_RKP24
) {
667 d
->idx_of_first_main_card
= 1;
670 d
->idx_of_first_main_card
= 3;
673 if(d
->fmt
==RKFMT_RKP24
) {
674 read_header_rkp24(c
, d
);
677 read_header_pc(c
, d
);
680 read_header_amiga(c
, d
);
682 if(d
->fatalerrflag
) goto done
;
685 read_palette_amiga(c
, d
);
687 if(d
->fatalerrflag
) goto done
;
688 if(d
->fmt
==RKFMT_RKP24
) {
689 reko_main_rkp24(c
, d
);
698 de_err(c
, "Bad or unsupported file");
704 static int de_identify_reko(deark
*c
)
708 fmt
= reko_fmt_from_sig(c
);
709 if(fmt
!=0) return 70;
713 static void de_help_reko(deark
*c
)
715 de_msg(c
, "-opt reko:combine=0 : Always write each card to its own file");
718 void de_module_reko(deark
*c
, struct deark_module_info
*mi
)
721 mi
->desc
= "REKO cardset";
722 mi
->run_fn
= de_run_reko
;
723 mi
->identify_fn
= de_identify_reko
;
724 mi
->help_fn
= de_help_reko
;
727 //----------------------------------------------------
728 // Wiz Solitaire deck
730 static void set_wizsol_card_filename(deark
*c
, i64 cardidx
, u8 cardval
,
731 u8 cardsuit
, de_finfo
*fi
)
733 static const char *cnames
= "a23456789tjqk";
734 static const char *snames
= "cdhs";
737 if(cardval
>=1 && cardval
<=13 && cardsuit
<=3) {
738 nbuf
[0] = cnames
[(UI
)cardval
-1];
739 nbuf
[1] = snames
[(UI
)cardsuit
];
742 else if(cardidx
==0) {
743 de_strlcpy(nbuf
, "back", sizeof(nbuf
));
746 de_strlcpy(nbuf
, "other", sizeof(nbuf
));
749 de_finfo_set_name_from_sz(c
, fi
, nbuf
, 0, DE_ENCODING_LATIN1
);
752 static void de_run_wizsolitaire(deark
*c
, de_module_params
*mparams
)
759 int saved_indent_level
;
761 de_dbg_indent_save(c
, &saved_indent_level
);
763 fi
= de_finfo_create(c
);
767 u8 cardval
, cardsuit
;
774 if(pos
+18 > c
->infile
->len
) goto done
;
775 de_dbg(c
, "card #%"I64_FMT
" at %"I64_FMT
, cardidx
, pos
);
778 // Card headers start with 00 00 00 00, except the 54th which starts
779 // with 04 00 00 00 00 00 00 00.
780 // I don't know how to interpret this. But the 04 does not seem to be the
781 // number of extra cards, so I'm assuming it's the number of extra bytes
783 extralen
= de_getu32le_p(&pos
);
784 de_dbg(c
, "len1: %"I64_FMT
, extralen
);
787 cardval
= de_getbyte_p(&pos
);
788 cardsuit
= de_getbyte_p(&pos
);
789 imglen
= de_getu32le_p(&pos
);
790 de_dbg(c
, "img len: %"I64_FMT
, imglen
);
792 de_dbg(c
, "[eof marker]");
796 de_dbg(c
, "suit %u card %u", (UI
)cardsuit
, (UI
)cardval
);
798 if(imglen
<32 || pos
+imglen
>c
->infile
->len
) {
803 if(dbuf_memcmp(c
->infile
, pos
, (const void*)"\xff\xd8\xff", 3)) {
804 de_err(c
, "Expected image not found at %"I64_FMT
, pos
);
808 set_wizsol_card_filename(c
, cardidx
, cardval
, cardsuit
, fi
);
809 dbuf_create_file_from_slice(c
->infile
, pos
, imglen
, "jpg", fi
, 0);
811 de_dbg_indent(c
, -1);
816 de_dbg_indent_restore(c
, saved_indent_level
);
818 de_dbg(c
, "number of cards: %"I64_FMT
, cardidx
);
819 if(cardidx
<53 && !errflag
) {
820 de_warn(c
, "Expected at least 53 cards, found %"I64_FMT
, cardidx
);
824 de_err(c
, "Bad or unsupported file");
826 de_finfo_destroy(c
, fi
);
829 static int de_identify_wizsolitaire(deark
*c
)
831 if(!dbuf_memcmp(c
->infile
, 0, (const void*)"WizSolitaireDeck", 16)) {
837 void de_module_wizsolitaire(deark
*c
, struct deark_module_info
*mi
)
839 mi
->id
= "wizsolitaire";
840 mi
->desc
= "Wiz Solitaire deck";
841 mi
->run_fn
= de_run_wizsolitaire
;
842 mi
->identify_fn
= de_identify_wizsolitaire
;