bmp: Rewrote the RLE decompressor
[deark.git] / modules / reko.c
blobb300e831077740b16ae36820ede6151f490ee1d0
1 // This file is part of Deark.
2 // Copyright (C) 2024 Jason Summers
3 // See the file COPYING for terms of use.
5 // REKO cardset, etc.
7 #include <deark-private.h>
8 DE_DECLARE_MODULE(de_module_reko);
9 DE_DECLARE_MODULE(de_module_wizsolitaire);
11 //----------------------------------------------------
12 // REKO cardset
14 #define MAXCARDS 80
15 #define MINCARDWIDTH 64
16 #define MAXCARDWIDTH 100
17 #define MINCARDHEIGHT 100
18 #define MAXCARDHEIGHT 150
20 #define RKFMT_AMIGA 1
21 #define RKFMT_RKP8 4
22 #define RKFMT_RKP16 5
23 #define RKFMT_RKP24 6
25 typedef struct localctx_reko {
26 u8 fmt; // RKFMT_*
27 u8 is_pc;
28 u8 fatalerrflag;
29 u8 need_errmsg;
30 u8 suppress_size_warnings;
31 u8 combine_images;
32 i64 bodysize;
33 i64 cardsize;
34 i64 w, h;
35 i64 amiga_row_stride, amiga_plane_stride;
36 UI depth_pixel;
37 UI depth_color;
38 UI camg_mode;
39 u8 ham_flag;
40 i64 numcards;
41 i64 hdrsize;
42 i64 globalpal_nbytes;
43 i64 localpal_nbytes;
44 i64 idx_of_first_main_card;
45 de_color pal[256];
46 } lctx;
48 static void read_header_amiga(deark *c, lctx *d)
50 i64 pos = 0;
51 int saved_indent_level;
53 de_dbg_indent_save(c, &saved_indent_level);
54 de_dbg(c, "header at %"I64_FMT, pos);
55 de_dbg_indent(c, 1);
56 pos += 4;
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;
68 de_dbg_indent(c, 1);
69 de_dbg(c, "HAM: %u", (UI)d->ham_flag);
70 de_dbg_indent(c, -1);
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);
75 d->hdrsize = pos;
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);
81 d->w = 88;
82 d->suppress_size_warnings = 1;
85 if(d->depth_pixel<1 || d->depth_pixel>8) {
86 d->fatalerrflag = 1;
87 d->need_errmsg = 1;
88 goto done;
91 d->depth_color = d->depth_pixel;
92 if(d->ham_flag) {
93 if(d->depth_pixel!=6 && d->depth_pixel!=8) {
94 d->fatalerrflag = 1;
95 d->need_errmsg = 1;
96 goto done;
98 d->depth_color -= 2;
101 done:
102 de_dbg_indent_restore(c, saved_indent_level);
105 static void read_palette_amiga(deark *c, lctx *d)
107 i64 pos;
108 i64 numentries;
109 int saved_indent_level;
111 de_dbg_indent_save(c, &saved_indent_level);
112 pos = d->hdrsize;
113 de_dbg(c, "global palette at %"I64_FMT, pos);
114 de_dbg_indent(c, 1);
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)
124 i64 pos = 0;
125 int saved_indent_level;
127 de_dbg_indent_save(c, &saved_indent_level);
129 de_dbg(c, "header at %"I64_FMT, pos);
130 de_dbg_indent(c, 1);
131 pos += 8;
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);
145 d->hdrsize = pos;
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)
153 i64 pos = 0;
154 int saved_indent_level;
156 de_dbg_indent_save(c, &saved_indent_level);
158 de_dbg(c, "header at %"I64_FMT, pos);
159 de_dbg_indent(c, 1);
160 pos += 8;
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);
174 d->hdrsize = 1104;
176 de_dbg_indent_restore(c, saved_indent_level);
179 static void read_image_pc8(deark *c, lctx *d, de_bitmap *img, i64 pos1)
181 i64 pos = pos1;
182 size_t k;
183 char tmps[64];
185 de_dbg(c, "palette at %"I64_FMT, pos);
186 de_dbg_indent(c, 1);
187 de_zeromem(d->pal, sizeof(d->pal));
188 for(k=0; k<256; k++) {
189 u32 clr_raw;
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)
208 i64 ypos;
209 u8 pbit[8];
210 i64 units_per_row; // num bytes per row per plane that we will process
211 UI pixshift1;
213 de_zeromem(pbit, sizeof(pbit));
214 if(nplanes==6) {
215 pixshift1 = 4;
217 else if(nplanes==8) {
218 pixshift1 = 2;
220 else goto done;
222 units_per_row = (img->width + 7)/8;
224 for(ypos=0; ypos<img->height; ypos++) {
225 i64 n;
226 u8 cr, cg, cb;
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++) {
234 UI k;
235 UI pn;
236 u8 b;
237 i64 xpos;
239 for(pn=0; pn<(UI)nplanes; pn++) {
240 b = dbuf_getbyte(f, fpos + ypos*row_stride + pn*plane_stride + n);
241 pbit[pn] = b;
244 for(k=0; k<8; k++) {
245 de_color clr;
246 u8 pixval;
247 u8 pixval_code;
248 u8 pixval_color;
249 u8 cr2, cg2, cb2;
251 pixval = 0;
252 for(pn=0; pn<(UI)nplanes; pn++) {
253 if((pbit[pn] & (1U<<(7-k)))!=0) {
254 pixval |= 1U<<pn;
258 if(nplanes==6) {
259 pixval_code = pixval >> 4;
260 pixval_color = pixval & 0x0f;
262 else {
263 pixval_code = pixval >> 6;
264 pixval_color = pixval & 0x3f;
267 switch(pixval_code) {
268 case 0x1: // Modify blue value
269 cb = pixval_color;
270 break;
271 case 0x2: // Modify red value
272 cr = pixval_color;
273 break;
274 case 0x3: // Modify green value
275 cg = pixval_color;
276 break;
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;
282 break;
285 if(nplanes==6) {
286 cr2 = (cr<<4) | cr;
287 cg2 = (cr<<4) | cr;
288 cb2 = (cr<<4) | cr;
290 else {
291 cr2 = (cr<<2) | (cr>>4);
292 cg2 = (cg<<2) | (cg>>4);
293 cb2 = (cb<<2) | (cb>>4);
295 xpos = n*8 + (i64)k;
296 de_bitmap_setpixel_rgba(img, xpos, ypos, DE_MAKE_RGB(cr2, cg2, cb2));
301 done:
305 static void read_image_amiga(deark *c, lctx *d, de_bitmap *img, i64 pos1)
307 if(d->ham_flag) {
308 convert_image_planar_HAM(c->infile, pos1, d->depth_pixel,
309 d->amiga_row_stride, d->amiga_plane_stride, d->pal, img);
311 else {
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)
319 i64 i,j;
320 i64 pos = 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++) {
325 u32 clr_raw;
326 de_color clr;
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";
340 char nbuf[16];
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];
345 nbuf[2] = '\0';
347 else if(cardidx==0) {
348 de_strlcpy(nbuf, "back", sizeof(nbuf));
350 else {
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)
359 i64 pos;
360 int saved_indent_level;
361 i64 cardidx;
362 u8 jpeg_fmt = 0;
363 const char *ext;
364 de_finfo *fi = NULL;
366 de_dbg_indent_save(c, &saved_indent_level);
367 pos = d->hdrsize;
368 de_dbg(c, "cards at %"I64_FMT, pos);
369 de_dbg_indent(c, 1);
371 fi = de_finfo_create(c);
373 if(d->cardsize==0) {
374 jpeg_fmt = 1;
375 ext = "jpg";
377 else {
378 ext = "bmp";
381 if(d->numcards<1 || d->numcards>MAXCARDS) {
382 d->fatalerrflag = 1;
383 d->need_errmsg = 1;
384 goto done;
387 for(cardidx=0; cardidx<d->numcards; cardidx++) {
388 i64 extract_pos = 0;
389 i64 nbytes_to_extract = 0;
390 i64 this_cardsize = 0;
391 dbuf *outf;
393 de_dbg(c, "card #%"I64_FMT" at %"I64_FMT, cardidx, pos);
394 de_dbg_indent(c, 1);
396 if(jpeg_fmt) {
397 extract_pos = pos+4;
398 if(de_getu16be(extract_pos) != 0xffd8) {
399 d->fatalerrflag = 1;
400 d->need_errmsg = 1;
401 goto done;
403 nbytes_to_extract = de_getu32le(pos);
404 this_cardsize = 4+nbytes_to_extract;
406 else {
407 if(de_getu16be(pos) != 0x424d) {
408 d->fatalerrflag = 1;
409 d->need_errmsg = 1;
410 goto done;
412 extract_pos = pos;
413 nbytes_to_extract = d->cardsize;
414 this_cardsize = d->cardsize;
417 if(extract_pos+nbytes_to_extract > c->infile->len) {
418 d->fatalerrflag = 1;
419 d->need_errmsg = 1;
420 goto done;
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);
426 dbuf_close(outf);
428 pos += this_cardsize;
429 de_dbg_indent(c, -1);
432 done:
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;
440 i64 cardidx;
441 i64 expected_cardsize;
442 i64 full_cardsize;
443 de_bitmap *cardimg = NULL;
444 de_bitmap *canvas = NULL;
445 de_finfo *fi = NULL;
446 i64 cards_pos;
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);
456 de_dbg_indent(c, 1);
458 fi = de_finfo_create(c);
460 if(d->is_pc) {
461 if(d->depth_pixel!=8 && d->depth_pixel!=16) {
462 d->fatalerrflag = 1;
463 d->need_errmsg = 1;
464 goto done;
468 if(d->numcards<1 || d->numcards>MAXCARDS ||
469 d->w<MINCARDWIDTH || d->w>MAXCARDWIDTH ||
470 d->h<MINCARDHEIGHT || d->h>MAXCARDHEIGHT)
472 d->fatalerrflag = 1;
473 d->need_errmsg = 1;
474 goto done;
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);
482 if(d->is_pc) {
483 expected_cardsize = d->w * d->h * (d->depth_pixel/8);
485 else {
486 i64 bytes_per_row_per_plane;
488 // We expect the width to be a multiple of 8. Other widths are not
489 // supported.
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;
503 if(d->is_pc) {
504 localhdrsize = 4;
505 if(d->depth_pixel==8) {
506 d->localpal_nbytes = 512;
509 full_cardsize = localhdrsize + d->localpal_nbytes + d->cardsize;
511 canvas_cols = 13;
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;
515 cxpos_maincards = 0;
516 cypos_maincards = 0;
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++) {
530 u8 is_main_card;
531 i64 dstcxpos, dstcypos;
532 i64 dstxpos, dstypos;
533 i64 thiscardpos;
535 thiscardpos = cards_pos + cardidx*full_cardsize;
536 de_dbg(c, "card #%"I64_FMT" at %"I64_FMT, cardidx, thiscardpos);
537 de_dbg_indent(c, 1);
539 if(d->is_pc && !d->fatalerrflag && (thiscardpos+localhdrsize <= c->infile->len)) {
540 i64 cw_raw, ch_raw;
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);
546 d->fatalerrflag = 1;
547 // (But keep going.)
551 de_bitmap_rect(cardimg, 0, 0, d->w, d->h, DE_STOCKCOLOR_BLACK, 0);
553 if(d->fatalerrflag) {
556 else if(d->is_pc) {
557 if(d->depth_pixel==8) {
558 read_image_pc8(c, d, cardimg, thiscardpos+localhdrsize);
560 else {
561 read_image_pc16(c, d, cardimg, thiscardpos+localhdrsize);
564 else {
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));
570 if(is_main_card) {
571 dstcxpos = cxpos_maincards;
572 dstcypos = cypos_maincards;
573 cypos_maincards++;
574 if(cypos_maincards>=4) {
575 cypos_maincards = 0;
576 cxpos_maincards++;
579 else {
580 dstcxpos = cxpos_extracards;
581 dstcypos = cypos_extracards;
582 cxpos_extracards++;
583 if(cxpos_extracards>=13) {
584 cxpos_extracards = 0;
585 cypos_extracards++;
588 dstxpos = dstcxpos*(d->w+REKO_BORDER);
589 dstypos = dstcypos*(d->h+REKO_BORDER);
590 if(canvas) {
591 de_bitmap_copy_rect(cardimg, canvas, 0, 0, d->w, d->h, dstxpos, dstypos, 0);
593 else {
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");
600 d->fatalerrflag = 1;
601 // (But keep going, so we draw the full image template.)
604 if(d->fatalerrflag && !canvas) goto done;
606 de_dbg_indent(c, -1);
609 if(canvas) {
610 de_bitmap_write_to_file(canvas, NULL, 0);
613 done:
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)
622 u8 buf[8];
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) {
630 return RKFMT_AMIGA;
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)) {
638 return RKFMT_RKP24;
640 return 0;
643 static void de_run_reko(deark *c, de_module_params *mparams)
645 lctx *d = NULL;
646 const char *fmtname;
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);
652 if(d->fmt==0) {
653 de_err(c, "Unsupported REKO version");
654 goto done;
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) {
663 d->is_pc = 1;
666 if(d->is_pc) {
667 d->idx_of_first_main_card = 1;
669 else {
670 d->idx_of_first_main_card = 3;
673 if(d->fmt==RKFMT_RKP24) {
674 read_header_rkp24(c, d);
676 else if(d->is_pc) {
677 read_header_pc(c, d);
679 else {
680 read_header_amiga(c, d);
682 if(d->fatalerrflag) goto done;
684 if(!d->is_pc) {
685 read_palette_amiga(c, d);
687 if(d->fatalerrflag) goto done;
688 if(d->fmt==RKFMT_RKP24) {
689 reko_main_rkp24(c, d);
691 else {
692 reko_main(c, d);
695 done:
696 if(d) {
697 if(d->need_errmsg) {
698 de_err(c, "Bad or unsupported file");
700 de_free(c, d);
704 static int de_identify_reko(deark *c)
706 u8 fmt;
708 fmt = reko_fmt_from_sig(c);
709 if(fmt!=0) return 70;
710 return 0;
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)
720 mi->id = "reko";
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";
735 char nbuf[16];
737 if(cardval>=1 && cardval<=13 && cardsuit<=3) {
738 nbuf[0] = cnames[(UI)cardval-1];
739 nbuf[1] = snames[(UI)cardsuit];
740 nbuf[2] = '\0';
742 else if(cardidx==0) {
743 de_strlcpy(nbuf, "back", sizeof(nbuf));
745 else {
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)
754 i64 cardidx = 0;
755 u8 errflag = 0;
756 u8 need_errmsg = 0;
757 i64 pos;
758 de_finfo *fi = NULL;
759 int saved_indent_level;
761 de_dbg_indent_save(c, &saved_indent_level);
762 pos = 20;
763 fi = de_finfo_create(c);
764 while(1) {
765 i64 extralen;
766 i64 imglen;
767 u8 cardval, cardsuit;
769 if(cardidx>72) {
770 errflag = 1;
771 need_errmsg = 1;
772 goto done;
774 if(pos+18 > c->infile->len) goto done;
775 de_dbg(c, "card #%"I64_FMT" at %"I64_FMT, cardidx, pos);
776 de_dbg_indent(c, 1);
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
782 // in the header.
783 extralen = de_getu32le_p(&pos);
784 de_dbg(c, "len1: %"I64_FMT, extralen);
785 pos += extralen;
786 pos += 8;
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);
791 if(imglen==0) {
792 de_dbg(c, "[eof marker]");
793 goto done;
796 de_dbg(c, "suit %u card %u", (UI)cardsuit, (UI)cardval);
798 if(imglen<32 || pos+imglen>c->infile->len) {
799 errflag = 1;
800 need_errmsg = 1;
801 goto done;
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);
805 errflag = 1;
806 goto done;
808 set_wizsol_card_filename(c, cardidx, cardval, cardsuit, fi);
809 dbuf_create_file_from_slice(c->infile, pos, imglen, "jpg", fi, 0);
810 pos += imglen;
811 de_dbg_indent(c, -1);
812 cardidx++;
815 done:
816 de_dbg_indent_restore(c, saved_indent_level);
817 if(!errflag) {
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);
823 if(need_errmsg) {
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)) {
832 return 100;
834 return 0;
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;