bmp: Rewrote the RLE decompressor
[deark.git] / modules / rsc.c
blobe221ac110f8d3fb2671d00e2fd4a2f52e11110c6
1 // This file is part of Deark.
2 // Copyright (C) 2016 Jason Summers
3 // See the file COPYING for terms of use.
5 // GEM (Atari) .RSC resource file
7 #include <deark-config.h>
8 #include <deark-private.h>
9 DE_DECLARE_MODULE(de_module_rsc);
11 #define RSCFMT_UNKNOWN 0
12 // ATARI means Atari-style (big-endian). Not necessarily limited to that platform.
13 #define RSCFMT_ATARI 1
14 #define RSCFMT_PC 2
16 #define MAX_RSC_ICON_WIDTH 1024
17 #define MAX_RSC_ICON_HEIGHT 1024
19 typedef struct localctx_struct {
20 deark *c;
21 de_ext_encoding input_encoding;
22 int fmt;
23 int is_le;
24 u8 decode_objects;
25 u8 allow_unaligned_offsets;
26 i64 version;
27 i64 object_offs, object_num;
28 i64 objecttree_num;
29 i64 iconblk_offs, iconblk_num;
30 i64 bitblk_offs, bitblk_num;
31 i64 imagedata_offs;
32 i64 imagepointertable_offs;
33 i64 rssize;
34 i64 cicon_offs;
35 i64 reported_file_size;
36 i64 avail_file_size;
38 i64 num_ciconblk;
39 de_color pal2[2];
40 de_color pal16[16];
41 de_color pal256[256];
42 } lctx;
44 struct iconinfo {
45 i64 width, height;
46 i64 mono_rowspan;
47 i64 nplanes;
48 de_ucstring *icon_text;
51 static int is_valid_segment_pos(deark *c, lctx *d, i64 pos, i64 len, const char *name)
53 int ok_start = 1;
54 int ok_end = 1;
56 if(pos<36 && len>0) ok_start = 0;
57 if(pos>d->avail_file_size) ok_start = 0;
58 if(!d->allow_unaligned_offsets && (pos%2)) ok_start = 0;
59 if(pos+len > d->avail_file_size) ok_end = 0;
61 if(ok_start && ok_end) return 1;
62 if(ok_start && !ok_end && len>=2) {
63 de_err(c, "Invalid %s location: %"I64_FMT"-%"I64_FMT, name, pos, pos+len-1);
65 else {
66 de_err(c, "Invalid %s location: %"I64_FMT, name, pos);
68 return 0;
71 static void destroy_iconinfo(deark *c, struct iconinfo *ii)
73 if(!ii) return;
74 if(ii->icon_text) {
75 ucstring_destroy(ii->icon_text);
77 de_free(c, ii);
80 static i64 gem_getu16(lctx *d, i64 pos)
82 return dbuf_getu16x(d->c->infile, pos, d->is_le);
85 static i64 gem_getu16m1(lctx *d, i64 pos)
87 i64 n = gem_getu16(d, pos);
88 if(n==0xffff) n = -1;
89 return n;
92 static i64 gem_getu32(lctx *d, i64 pos)
94 return dbuf_getu32x(d->c->infile, pos, d->is_le);
97 static i64 gem_getu32m1(lctx *d, i64 pos)
99 i64 n = gem_getu32(d, pos);
100 if(n==0xffffffffLL) n = -1;
101 return n;
104 static void do_decode_bilevel_image(deark *c, lctx *d, de_bitmap *img, i64 bits_pos,
105 i64 rowspan)
107 i64 i, j;
108 i64 rowspan_in_16bit_chunks;
109 UI k;
110 UI n;
112 rowspan_in_16bit_chunks = (rowspan+1)/2;
114 for(j=0; j<img->height; j++) {
115 for(i=0; i<rowspan_in_16bit_chunks; i++) {
116 n = (UI)gem_getu16(d, bits_pos + j*rowspan + i*2);
117 for(k=0; k<16; k++) {
118 u8 clr;
120 clr = (n & (1U<<(15-k))) ? 0 : 0xff;
121 de_bitmap_setpixel_gray(img, i*16+(i64)k, j, clr);
127 static void do_decode_and_write_bilevel_image(deark *c, lctx *d, i64 bits_pos,
128 i64 rowspan, i64 width, i64 height)
130 de_bitmap *img = NULL;
132 if(!is_valid_segment_pos(c, d, bits_pos, rowspan*height, "bitmap")) {
133 goto done;
135 if(!de_good_image_dimensions(c, width, height)) {
136 goto done;
139 img = de_bitmap_create(c, width, height, 1);
140 do_decode_bilevel_image(c, d, img, bits_pos, rowspan);
141 de_bitmap_write_to_file(img, NULL, DE_CREATEFLAG_IS_BWIMG);
142 done:
143 de_bitmap_destroy(img);
146 static int do_scan_iconblk(deark *c, lctx *d, i64 pos1, struct iconinfo *ii)
148 i64 pos;
150 // TODO: Refactor this code to better share it with old- and new-style RSC.
152 pos = pos1;
153 ii->width = gem_getu16(d, pos+22);
154 ii->height = gem_getu16(d, pos+24);
155 de_dbg_dimensions(c, ii->width, ii->height);
156 if(ii->width<1 || ii->width>MAX_RSC_ICON_WIDTH ||
157 ii->height<1 || ii->height>MAX_RSC_ICON_HEIGHT)
159 de_dbg(c, "bad or unexpected icon dimensions");
160 return 0;
162 return 1;
165 static void set_icon_finfo(deark *c, lctx *d, de_finfo *fi, struct iconinfo *ii,
166 const char *token)
168 de_ucstring *s = NULL;
170 s = ucstring_create(c);
172 if(ucstring_isnonempty(ii->icon_text)) {
173 ucstring_append_ucstring(s, ii->icon_text);
176 if(token) {
177 if(ucstring_isnonempty(s)) {
178 ucstring_append_char(s, '.');
180 ucstring_append_sz(s, token, DE_ENCODING_UTF8);
183 de_finfo_set_name_from_ucstring(c, fi, s, 0x0);
184 ucstring_destroy(s);
187 static void do_bilevel_icon(deark *c, lctx *d, struct iconinfo *ii, i64 fg_pos,
188 i64 mask_pos, const char *token)
190 de_bitmap *img = NULL;
191 de_bitmap *mask = NULL;
192 de_finfo *fi = NULL;
194 if(!is_valid_segment_pos(c, d, fg_pos, ii->height*ii->mono_rowspan, "bitmap")) {
195 goto done;
197 if(!is_valid_segment_pos(c, d, mask_pos, ii->height*ii->mono_rowspan, "mask")) {
198 goto done;
200 if(!de_good_image_dimensions(c, ii->width, ii->height)) {
201 goto done;
204 img = de_bitmap_create(c, ii->width, ii->height, 2);
205 mask = de_bitmap_create(c, ii->width, ii->height, 1);
206 do_decode_bilevel_image(c, d, img, fg_pos, ii->mono_rowspan);
207 do_decode_bilevel_image(c, d, mask, mask_pos, ii->mono_rowspan);
208 de_bitmap_apply_mask(img, mask, DE_BITMAPFLAG_WHITEISTRNS);
209 fi = de_finfo_create(c);
210 set_icon_finfo(c, d, fi, ii, token);
211 de_bitmap_write_to_file_finfo(img, fi, DE_CREATEFLAG_OPT_IMAGE);
213 done:
214 de_bitmap_destroy(img);
215 de_bitmap_destroy(mask);
216 de_finfo_destroy(c, fi);
219 static int do_old_iconblk(deark *c, lctx *d, i64 pos)
221 i64 mask_pos, fg_pos;
222 int retval = 0;
223 struct iconinfo *ii = NULL;
224 int saved_indent_level;
226 de_dbg_indent_save(c, &saved_indent_level);
227 ii = de_malloc(c, sizeof(struct iconinfo));
229 de_dbg(c, "ICONBLK at %"I64_FMT, pos);
230 de_dbg_indent(c, 1);
231 if(!do_scan_iconblk(c, d, pos, ii)) goto done;
233 mask_pos = gem_getu32(d, pos);
234 fg_pos = gem_getu32(d, pos+4);
235 de_dbg(c, "fg at %"I64_FMT, fg_pos);
236 de_dbg(c, "mask at %"I64_FMT, mask_pos);
238 ii->mono_rowspan = ((ii->width+15)/16)*2;
239 do_bilevel_icon(c, d, ii, fg_pos, mask_pos, "1");
241 retval = 1;
242 done:
243 destroy_iconinfo(c, ii);
244 de_dbg_indent_restore(c, saved_indent_level);
245 return retval;
248 // TODO: This palette may not be correct.
249 static const de_color pal16[16] = {
250 0xffffff,0xff0000,0x00ff00,0xffff00,0x0000ff,0xff00ff,0x00ffff,0xc0c0c0,
251 0x808080,0xff8080,0x80ff80,0xffff80,0x8080ff,0xff80ff,0x80ffff,0x000000
254 // FIXME: This palette is incomplete, and probably inaccurate.
255 static const de_color supplpal1[16] = {
256 0xffffff,0xef0000,0x00e700,0xffff00,0x0000ef,0xcd05cd,0xcd06cd,0xd6d6d6, // 00-07
257 0x808080,0x7b0000,0x008000,0xb5a531,0x000080,0x7f007f,0x007b7b,0x101810 // 08-ff
260 static const de_color supplpal2[26] = {
261 0xef0000,0xe70000, // e6-e7
262 0xbd0000,0xad0000,0x7b0000,0x4a0000,0x100000,0xcdedcd,0xcdeecd,0x00bd00, // e8-ef
263 0x00b500,0xcdf1cd,0x004a00,0x001800,0x000010,0x00004f,0xcdf6cd,0x0000af, // f0-f7
264 0x293194,0x0000e0,0xeff7ef,0xe7e7e7,0xc0c0c0,0xadb5ad,0x4a4a4a,0x000000 // f8-ff
267 static de_color getpal16(unsigned int k)
269 if(k>=16) return 0;
270 return pal16[k];
273 static de_color getpal256(unsigned int k)
275 unsigned int x;
276 u8 r, g, b;
278 if(k<=15) {
279 // first 16 entries
280 return supplpal1[k];
282 else if(k<=229) {
283 // next 214 entries
284 x = k-15;
285 r = (u8)((x/36)*0x33);
286 g = ((x%36)/6)*0x33;
287 b = (x%6)*0x33;
288 return DE_MAKE_RGB(r,g,b);
290 else if(k<=255) {
291 // last 26 entries
292 return supplpal2[k-230];
294 return 0;
297 static void construct_palettes(deark *c, lctx *d)
299 UI k;
301 d->pal2[0] = DE_STOCKCOLOR_WHITE;
302 d->pal2[1] = DE_STOCKCOLOR_BLACK;
303 for(k=0; k<16; k++) {
304 d->pal16[k] = DE_MAKE_OPAQUE(getpal16(k));
306 for(k=0; k<256; k++) {
307 d->pal256[k] = DE_MAKE_OPAQUE(getpal256(k));
311 // FIXME: This probably doesn't work for PC format (little-endian).
312 static void do_color_icon(deark *c, lctx *d, struct iconinfo *ii, i64 fg_pos,
313 i64 mask_pos, const char *token, int is_sel)
315 de_bitmap *img = NULL;
316 de_bitmap *mask = NULL;
317 de_finfo *fi = NULL;
318 i64 planespan;
319 const de_color *pal_to_use;
321 // TODO: Images with 5, 6, or 7 planes exist, but I don't know what palettes
322 // they use.
324 if(ii->nplanes!=1 && ii->nplanes!=4 && ii->nplanes!=8) {
325 // The non-selected and selected images have the same # of planes, so
326 // suppress the duplicate warning message.
327 if(!is_sel) {
328 de_warn(c, "%d-plane icons not supported", (int)ii->nplanes);
330 goto done;
333 if(d->pal16[0]==0) {
334 construct_palettes(c, d);
337 switch(ii->nplanes) {
338 case 1: pal_to_use = d->pal2; break;
339 case 4: pal_to_use = d->pal16; break;
340 default: pal_to_use = d->pal256;
343 img = de_bitmap_create(c, ii->width, ii->height, 4);
344 mask = de_bitmap_create(c, ii->width, ii->height, 1);
346 planespan = ii->mono_rowspan * ii->height;
347 de_convert_image_paletted_planar(c->infile, fg_pos, ii->nplanes,
348 ii->mono_rowspan, planespan, pal_to_use, img, 0x2);
350 do_decode_bilevel_image(c, d, mask, mask_pos, ii->mono_rowspan);
351 de_bitmap_apply_mask(img, mask, DE_BITMAPFLAG_WHITEISTRNS);
353 fi = de_finfo_create(c);
354 set_icon_finfo(c, d, fi, ii, token);
355 de_bitmap_write_to_file_finfo(img, fi, DE_CREATEFLAG_OPT_IMAGE);
357 done:
358 de_bitmap_destroy(img);
359 de_bitmap_destroy(mask);
360 de_finfo_destroy(c, fi);
363 static int do_ciconblk_struct(deark *c, lctx *d, i64 icon_idx, i64 pos1,
364 i64 *bytes_consumed)
366 struct iconinfo *ii = NULL;
367 i64 pos;
368 i64 n_cicons;
369 i64 mono_bitmapsize;
370 i64 color_bitmapsize;
371 i64 next_res;
372 i64 sel_data_flag;
373 int retval = 0;
374 i64 i;
375 i64 mono_fgpos, mono_maskpos;
376 int saved_indent_level;
377 char token[16];
379 de_dbg_indent_save(c, &saved_indent_level);
380 de_dbg(c, "CICONBLK[%d] at %"I64_FMT, (int)icon_idx, pos1);
381 de_dbg_indent(c, 1);
383 ii = de_malloc(c, sizeof(struct iconinfo));
385 pos = pos1;
386 if(!do_scan_iconblk(c, d, pos, ii)) {
387 goto done;
389 pos+=34;
391 n_cicons = gem_getu32(d, pos);
392 de_dbg(c, "number of color depths for this icon: %d", (int)n_cicons);
393 pos += 4;
395 ii->mono_rowspan = ((ii->width+15)/16)*2; // guess
397 de_dbg2(c, "bilevel image data at %"I64_FMT" (deferred)", pos);
398 mono_bitmapsize = ii->mono_rowspan * ii->height;
399 mono_fgpos = pos;
400 pos += mono_bitmapsize; // foreground
401 mono_maskpos = pos;
402 pos += mono_bitmapsize; // mask
403 de_dbg2(c, "bilevel image data ends at %"I64_FMT, pos);
405 if(!ii->icon_text) {
406 ii->icon_text = ucstring_create(c);
408 ucstring_empty(ii->icon_text);
409 dbuf_read_to_ucstring(c->infile, pos, 12, ii->icon_text, DE_CONVFLAG_STOP_AT_NUL,
410 d->input_encoding);
411 de_dbg(c, "icon text: \"%s\"", ucstring_getpsz_d(ii->icon_text));
412 pos += 12;
414 if(!de_good_image_dimensions(c, ii->width, ii->height)) {
415 goto done;
418 // Go back and read the bilevel icon. (We wanted to read the icon text first.)
419 de_dbg(c, "bilevel image data at %"I64_FMT, mono_fgpos);
420 de_dbg_indent(c, 1);
421 de_dbg(c, "fg at %"I64_FMT, mono_fgpos);
422 de_dbg(c, "mask at %"I64_FMT, mono_maskpos);
423 do_bilevel_icon(c, d, ii, mono_fgpos, mono_maskpos, "1");
424 de_dbg_indent(c, -1);
426 for(i=0; i<n_cicons; i++) {
427 if(pos >= c->infile->len) goto done;
428 de_dbg(c, "color depth %d of %d, at %"I64_FMT, (int)(i+1), (int)n_cicons, pos);
429 de_dbg_indent(c, 1);
431 ii->nplanes = gem_getu16(d, pos);
432 de_dbg(c, "planes: %d", (int)ii->nplanes);
433 pos += 2;
435 pos += 4; // col_data (placeholder)
436 pos += 4; // col_mask (placeholder)
438 sel_data_flag = gem_getu32(d, pos);
439 de_dbg(c, "sel_data flag: %d", (int)sel_data_flag);
440 pos += 4; // sel_data
442 pos += 4; // sel_mask (placeholder)
444 next_res = gem_getu32(d, pos);
445 de_dbg(c, "next_res flag: %d", (int)next_res);
446 pos += 4;
448 color_bitmapsize = mono_bitmapsize * ii->nplanes;
450 de_dbg(c, "unselected image at %"I64_FMT, pos);
451 de_dbg_indent(c, 1);
452 de_dbg(c, "fg at %"I64_FMT, pos);
453 de_dbg(c, "mask at %"I64_FMT, pos+color_bitmapsize);
454 de_snprintf(token, sizeof(token), "%d", (int)ii->nplanes);
455 do_color_icon(c, d, ii, pos, pos+color_bitmapsize, token, 0);
456 pos += color_bitmapsize; // color_data
457 pos += mono_bitmapsize; // color_mask
458 de_dbg_indent(c, -1);
460 if(sel_data_flag) {
461 de_dbg(c, "selected image at %"I64_FMT, pos);
462 de_dbg_indent(c, 1);
463 de_dbg(c, "fg at %"I64_FMT, pos);
464 de_dbg(c, "mask at %"I64_FMT, pos+color_bitmapsize);
465 de_snprintf(token, sizeof(token), "%d.sel", (int)ii->nplanes);
466 do_color_icon(c, d, ii, pos, pos+color_bitmapsize, token, 1);
467 pos += color_bitmapsize; // select_data
468 pos += mono_bitmapsize; // select_mask
469 de_dbg_indent(c, -1);
472 *bytes_consumed = pos - pos1;
474 de_dbg_indent(c, -1);
477 retval = 1;
478 done:
479 destroy_iconinfo(c, ii);
480 de_dbg_indent_restore(c, saved_indent_level);
481 return retval;
484 static int do_cicon_ptr_table(deark *c, lctx *d, i64 pos1, i64 *bytes_consumed)
486 i64 n;
487 i64 count = 0;
488 i64 pos;
489 int retval = 0;
490 int saved_indent_level;
492 de_dbg_indent_save(c, &saved_indent_level);
493 pos = pos1;
494 *bytes_consumed = 0;
496 de_dbg(c, "CICONBLK pointer table at %"I64_FMT, pos1);
497 de_dbg_indent(c, 1);
499 while(1) {
500 if(pos>=d->avail_file_size) {
501 // error
502 goto done;
505 // Values are expected to be 0. We just have to find the -1 that marks
506 // the end of this scratch space.
507 n = gem_getu32m1(d, pos);
508 de_dbg3(c, "item[%d]: %"I64_FMT, (int)count, n);
509 pos+=4;
511 if(n<0) {
512 break;
514 count++;
517 d->num_ciconblk = count;
518 de_dbg(c, "count of CICONBLKs: %d", (int)d->num_ciconblk);
519 *bytes_consumed = pos - pos1;
520 retval = 1;
522 done:
523 de_dbg_indent_restore(c, saved_indent_level);
524 return retval;
527 static int do_cicon(deark *c, lctx *d)
529 i64 bytes_consumed;
530 int ret;
531 i64 pos1;
532 i64 pos;
533 i64 i;
534 int retval = 0;
535 int saved_indent_level;
537 de_dbg_indent_save(c, &saved_indent_level);
538 if(!is_valid_segment_pos(c, d, d->cicon_offs, 1, "CICON segment")) {
539 goto done;
541 pos1 = d->cicon_offs;
542 de_dbg(c, "CICON file segment at %"I64_FMT, pos1);
543 de_dbg_indent(c, 1);
544 pos = pos1;
545 ret = do_cicon_ptr_table(c, d, pos, &bytes_consumed);
546 if(!ret) goto done;
547 pos += bytes_consumed;
549 for(i=0; i<d->num_ciconblk; i++) {
550 if(pos>=d->avail_file_size) goto done;
551 ret = do_ciconblk_struct(c, d, i, pos, &bytes_consumed);
552 if(!ret) goto done;
553 pos += bytes_consumed;
555 retval = 1;
557 done:
558 de_dbg_indent_restore(c, saved_indent_level);
559 return retval;
562 static void do_extension_array(deark *c, lctx *d)
564 i64 pos;
566 pos = d->rssize;
567 de_dbg(c, "extension array at %"I64_FMT, d->rssize);
568 de_dbg_indent(c, 1);
569 if(!is_valid_segment_pos(c, d, d->rssize, 8, "Extension Array")) goto done;
571 d->reported_file_size = gem_getu32(d, pos);
572 de_dbg(c, "reported file size: %"I64_FMT, d->reported_file_size);
573 d->avail_file_size = de_min_int(d->reported_file_size, c->infile->len);
575 d->cicon_offs = gem_getu32m1(d, pos+4);
576 if(d->cicon_offs==0) goto done;
577 de_dbg(c, "CICON offset: %"I64_FMT, d->cicon_offs);
579 done:
580 de_dbg_indent(c, -1);
583 #define OBJTYPE_IMAGE 23
584 #define OBJTYPE_ICON 31
585 #define OBJTYPE_CLRICON 33
587 static const char *get_obj_type_name(u8 t)
589 const char *s = NULL;
591 switch(t) {
592 case 20: s="box"; break;
593 case 21: s="formatted text"; break;
594 case 22: s="formatted text in a box"; break;
595 case OBJTYPE_IMAGE: s="image"; break;
596 case 24: s="programmer-defined object"; break;
597 case 25: s="invisible box"; break;
598 case 26: s="push button w/string"; break;
599 case 27: s="character in a box"; break;
600 case 28: s="unformatted text"; break;
601 case 29: s="editable formatted text"; break;
602 case 30: s="editable formatted text in a box"; break;
603 case OBJTYPE_ICON: s="icon"; break;
604 case 32: s="menu title"; break;
605 case OBJTYPE_CLRICON: s="clricon"; break;
607 return s?s:"?";
610 // The OBJECT table contains references to the bitmaps and icons in the file.
611 // It's not clear if we have to read it, because there are also pointers in
612 // the file header.
613 // TODO: Do we need to read it to get the true width of BITBLK images?
614 // TODO: We may need to read it to identify color icons in old-style RSC.
615 static int do_object(deark *c, lctx *d, i64 obj_index, i64 pos)
617 i64 obj_type_orig;
618 u8 obj_type;
619 i64 next_sibling, first_child, last_child;
620 i64 ob_spec;
621 i64 width, height;
623 de_dbg(c, "OBJECT #%d at %d", (int)obj_index, (int)pos);
624 de_dbg_indent(c, 1);
626 next_sibling = gem_getu16m1(d, pos);
627 first_child = gem_getu16m1(d, pos+2);
628 last_child = gem_getu16m1(d, pos+4);
629 de_dbg(c, "next sibling: %d, first child: %d, last child: %d",
630 (int)next_sibling, (int)first_child, (int)last_child);
632 obj_type_orig = gem_getu16(d, pos+6);
633 obj_type = (u8)(obj_type_orig&0xff);
635 de_dbg(c, "type: 0x%04x (%u; %s)", (unsigned int)obj_type_orig,
636 (unsigned int)obj_type, get_obj_type_name(obj_type));
638 ob_spec = gem_getu32(d, pos+12);
639 de_dbg(c, "ob_spec: %u (0x%08x)", (unsigned int)ob_spec, (unsigned int)ob_spec);
641 // Note: This does not seem to read the width and height fields correctly.
642 // Don't know what I'm doing wrong.
643 // (Fortunately, we don't necessarily need them.)
644 width = gem_getu16(d, pos+20);
645 height = gem_getu16(d, pos+22);
646 de_dbg_dimensions(c, width, height);
648 de_dbg_indent(c, -1);
649 return 1;
652 static int do_bitblk(deark *c, lctx *d, i64 pos)
654 i64 bits_pos;
655 i64 width_in_bytes;
656 i64 width, height;
657 i64 fgcol;
659 de_dbg(c, "BITBLK at %"I64_FMT, pos);
660 de_dbg_indent(c, 1);
662 bits_pos = gem_getu32(d, pos);
663 de_dbg(c, "bitmap pos: %"I64_FMT, bits_pos);
664 width_in_bytes = gem_getu16(d, pos+4);
665 width = width_in_bytes*8;
666 de_dbg(c, "width in bytes: %d", (int)width_in_bytes);
667 height = gem_getu16(d, pos+6);
668 de_dbg_dimensions(c, width, height);
669 fgcol = gem_getu16(d, pos+12);
670 de_dbg(c, "foreground color: 0x%04x", (unsigned int)fgcol);
671 // TODO: Can we do anything with the foreground color?
673 do_decode_and_write_bilevel_image(c, d, bits_pos, width_in_bytes, width, height);
675 de_dbg_indent(c, -1);
676 return 1;
679 static void do_OBJECTs(deark *c, lctx *d)
681 i64 i;
683 if(d->object_num<=0) return;
684 de_dbg(c, "OBJECTs at %"I64_FMT, d->object_offs);
685 if(!is_valid_segment_pos(c, d, d->object_offs, 24*d->object_num, "OBJECT table")) {
686 return;
688 if(!d->decode_objects) return;
690 de_dbg_indent(c, 1);
691 for(i=0; i<d->object_num; i++) {
692 if(d->object_offs + 24*(i+1) > d->avail_file_size) break;
693 do_object(c, d, i, d->object_offs + 24*i);
695 de_dbg_indent(c, -1);
698 static void do_BITBLKs(deark *c, lctx *d)
700 i64 i;
702 if(d->bitblk_num<=0) return;
703 de_dbg(c, "BITBLKs at %"I64_FMT, d->bitblk_offs);
704 if(!is_valid_segment_pos(c, d, d->bitblk_offs, 14*d->bitblk_num, "BITBLK table")) {
705 return;
707 de_dbg_indent(c, 1);
708 for(i=0; i<d->bitblk_num; i++) {
709 if(d->bitblk_offs + 14*(i+1) > d->avail_file_size) break;
710 do_bitblk(c, d, d->bitblk_offs + 14*i);
712 de_dbg_indent(c, -1);
715 static void do_ICONBLKs(deark *c, lctx *d)
717 i64 i;
719 if(d->iconblk_num<=0) return;
720 de_dbg(c, "ICONBLKs at %"I64_FMT, d->iconblk_offs);
721 if(!is_valid_segment_pos(c, d, d->iconblk_offs, 34*d->iconblk_num, "ICONBLK table")) {
722 return;
724 de_dbg_indent(c, 1);
725 for(i=0; i<d->iconblk_num; i++) {
726 if(d->iconblk_offs + 34*(i+1) > d->avail_file_size) break;
727 do_old_iconblk(c, d, d->iconblk_offs + 34*i);
729 de_dbg_indent(c, -1);
732 static void detect_rsc_format(deark *c, lctx *d)
734 i64 n_be, n_le;
735 i64 pos;
737 // Check the version number. Assumes PC format is always 0.
738 n_be = de_getu16be(0);
739 if(n_be != 0) {
740 d->fmt = RSCFMT_ATARI;
741 return;
744 // Check the (old-style) file size field
745 n_le = de_getu16le(34);
746 n_be = de_getu16be(34);
747 if(n_le != n_be) {
748 if(n_be==c->infile->len) {
749 d->fmt = RSCFMT_ATARI;
750 return;
752 if(n_le==c->infile->len) {
753 d->fmt = RSCFMT_PC;
754 return;
758 // Check some file offsets
759 for(pos=2; pos<=18; pos+=2) {
760 n_le = de_getu16le(pos);
761 if(n_le==0 || n_le==0xffff) continue;
762 n_be = de_getu16be(pos);
763 if(n_le > c->infile->len) {
764 d->fmt = RSCFMT_ATARI;
765 return;
767 if(n_be > c->infile->len) {
768 d->fmt = RSCFMT_PC;
769 return;
771 // Offsets should be even, I think.
772 if(n_le&0x1) {
773 d->fmt = RSCFMT_ATARI;
774 return;
776 if(n_be&0x1) {
777 d->fmt = RSCFMT_PC;
778 return;
782 // TODO: Is it worth doing more checks?
785 static void de_run_rsc(deark *c, de_module_params *mparams)
787 lctx *d = NULL;
788 const char *tmps;
790 d = de_malloc(c, sizeof(lctx));
791 d->c = c;
792 d->avail_file_size = c->infile->len; // Starting value. Will be adjusted later.
794 d->fmt = RSCFMT_UNKNOWN;
795 tmps = de_get_ext_option(c, "rsc:fmt");
796 if(tmps) {
797 if(!de_strcmp(tmps, "pc")) {
798 d->fmt = RSCFMT_PC;
800 else if(!de_strcmp(tmps, "atari")) {
801 d->fmt = RSCFMT_ATARI;
805 if(d->fmt==RSCFMT_UNKNOWN) {
806 detect_rsc_format(c, d);
809 if(d->fmt==RSCFMT_UNKNOWN) {
810 d->fmt = RSCFMT_ATARI;
813 if(d->fmt==RSCFMT_PC) {
814 de_declare_fmt(c, "GEM RSC, PC");
815 d->is_le = 1;
817 else {
818 de_declare_fmt(c, "GEM RSC, Atari");
821 d->input_encoding = de_get_input_encoding(c, NULL, DE_ENCODING_UNKNOWN);
822 if(d->input_encoding==DE_ENCODING_UNKNOWN) {
823 if(d->fmt==RSCFMT_ATARI) {
824 d->input_encoding = DE_ENCODING_ATARIST;
826 else {
827 // TODO?: This should probably be the "GEM character set", but we don't
828 // support that.
829 d->input_encoding = DE_ENCODING_ASCII;
833 d->decode_objects = 1;
834 // TODO: For Atari format, maybe we can disallow unaligned offsets.
835 d->allow_unaligned_offsets = 1;
837 de_dbg(c, "header at %d", 0);
838 de_dbg_indent(c, 1);
839 d->version = gem_getu16(d, 0);
840 de_dbg(c, "version: 0x%04x", (int)d->version);
842 d->object_offs = gem_getu16(d, 2);
843 d->iconblk_offs = gem_getu16(d, 6);
844 d->bitblk_offs = gem_getu16(d, 8);
845 d->imagedata_offs = gem_getu16(d, 14);
846 d->imagepointertable_offs = gem_getu16(d, 16);
847 d->object_num = gem_getu16(d, 20);
848 d->objecttree_num = gem_getu16(d, 22);
849 d->iconblk_num = gem_getu16(d, 26);
850 d->bitblk_num = gem_getu16(d, 28);
851 d->rssize = gem_getu16(d, 34);
853 de_dbg(c, "OBJECT: %d at %d", (int)d->object_num, (int)d->object_offs);
854 de_dbg(c, "num object trees: %d", (int)d->objecttree_num);
855 de_dbg(c, "ICONBLK: %d at %d", (int)d->iconblk_num, (int)d->iconblk_offs);
856 de_dbg(c, "BITBLK: %d at %d", (int)d->bitblk_num, (int)d->bitblk_offs);
857 de_dbg(c, "imagedata: at %d", (int)d->imagedata_offs);
858 de_dbg(c, "imagepointertable: at %d", (int)d->imagepointertable_offs);
859 if(d->version & 0x0004) {
860 de_dbg(c, "extension array offset: %"I64_FMT, d->rssize);
862 else {
863 de_dbg(c, "reported file size: %"I64_FMT, d->rssize);
864 d->reported_file_size = d->rssize;
865 d->avail_file_size = de_min_int(d->reported_file_size, c->infile->len);
867 de_dbg_indent(c, -1);
869 if(d->version & 0x0004) {
870 do_extension_array(c, d);
872 else if(d->version==0 || d->version==1) {
875 else {
876 de_err(c, "Unknown or unsupported version of RSC");
877 goto done;
880 do_OBJECTs(c, d);
881 do_BITBLKs(c, d);
882 do_ICONBLKs(c, d);
883 if(d->version & 0x0004) {
884 do_cicon(c, d);
887 done:
888 de_free(c, d);
891 // TODO: This needs to be improved, but it's complicated.
892 static int de_identify_rsc(deark *c)
894 i64 ver;
896 if(!de_input_file_has_ext(c, "rsc")) return 0;
897 ver = de_getu16be(0);
898 if(ver==0 || ver==1 || ver==4) return 70;
899 return 0;
902 static void de_help_rsc(deark *c)
904 de_msg(c, "-opt rsc:fmt=<atari|pc> : Use this byte order");
907 void de_module_rsc(deark *c, struct deark_module_info *mi)
909 mi->id = "rsc";
910 mi->desc = "GEM resource file";
911 mi->run_fn = de_run_rsc;
912 mi->identify_fn = de_identify_rsc;
913 mi->help_fn = de_help_rsc;