nrg: Improved support for v2
[deark.git] / modules / rsc.c
blobd09543a2ef9d75726edea7990629e984a6b8c57b
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 } lctx;
41 struct iconinfo {
42 i64 width, height;
43 i64 mono_rowspan;
44 i64 nplanes;
45 de_ucstring *icon_text;
48 static int is_valid_segment_pos(deark *c, lctx *d, i64 pos, i64 len, const char *name)
50 int ok_start = 1;
51 int ok_end = 1;
53 if(pos<36 && len>0) ok_start = 0;
54 if(pos>d->avail_file_size) ok_start = 0;
55 if(!d->allow_unaligned_offsets && (pos%2)) ok_start = 0;
56 if(pos+len > d->avail_file_size) ok_end = 0;
58 if(ok_start && ok_end) return 1;
59 if(ok_start && !ok_end && len>=2) {
60 de_err(c, "Invalid %s location: %"I64_FMT"-%"I64_FMT, name, pos, pos+len-1);
62 else {
63 de_err(c, "Invalid %s location: %"I64_FMT, name, pos);
65 return 0;
68 static void destroy_iconinfo(deark *c, struct iconinfo *ii)
70 if(!ii) return;
71 if(ii->icon_text) {
72 ucstring_destroy(ii->icon_text);
74 de_free(c, ii);
77 static i64 gem_getu16(lctx *d, i64 pos)
79 return dbuf_getu16x(d->c->infile, pos, d->is_le);
82 static i64 gem_getu16m1(lctx *d, i64 pos)
84 i64 n = gem_getu16(d, pos);
85 if(n==0xffff) n = -1;
86 return n;
89 static i64 gem_getu32(lctx *d, i64 pos)
91 return dbuf_getu32x(d->c->infile, pos, d->is_le);
94 static i64 gem_getu32m1(lctx *d, i64 pos)
96 i64 n = gem_getu32(d, pos);
97 if(n==0xffffffffLL) n = -1;
98 return n;
101 static void do_decode_bilevel_image(deark *c, lctx *d, de_bitmap *img, i64 bits_pos,
102 i64 rowspan)
104 i64 i, j;
105 i64 rowspan_in_16bit_chunks;
106 UI k;
107 UI n;
109 rowspan_in_16bit_chunks = (rowspan+1)/2;
111 for(j=0; j<img->height; j++) {
112 for(i=0; i<rowspan_in_16bit_chunks; i++) {
113 n = (UI)gem_getu16(d, bits_pos + j*rowspan + i*2);
114 for(k=0; k<16; k++) {
115 u8 clr;
117 clr = (n & (1U<<(15-k))) ? 0 : 0xff;
118 de_bitmap_setpixel_gray(img, i*16+(i64)k, j, clr);
124 static void do_decode_and_write_bilevel_image(deark *c, lctx *d, i64 bits_pos,
125 i64 rowspan, i64 width, i64 height)
127 de_bitmap *img = NULL;
129 if(!is_valid_segment_pos(c, d, bits_pos, rowspan*height, "bitmap")) {
130 goto done;
132 if(!de_good_image_dimensions(c, width, height)) {
133 goto done;
136 img = de_bitmap_create(c, width, height, 1);
137 do_decode_bilevel_image(c, d, img, bits_pos, rowspan);
138 de_bitmap_write_to_file(img, NULL, 0);
139 done:
140 de_bitmap_destroy(img);
143 static int do_scan_iconblk(deark *c, lctx *d, i64 pos1, struct iconinfo *ii)
145 i64 pos;
147 // TODO: Refactor this code to better share it with old- and new-style RSC.
149 pos = pos1;
150 ii->width = gem_getu16(d, pos+22);
151 ii->height = gem_getu16(d, pos+24);
152 de_dbg_dimensions(c, ii->width, ii->height);
153 if(ii->width<1 || ii->width>MAX_RSC_ICON_WIDTH ||
154 ii->height<1 || ii->height>MAX_RSC_ICON_HEIGHT)
156 de_dbg(c, "bad or unexpected icon dimensions");
157 return 0;
159 return 1;
162 static void set_icon_finfo(deark *c, lctx *d, de_finfo *fi, struct iconinfo *ii,
163 const char *token)
165 de_ucstring *s = NULL;
167 s = ucstring_create(c);
169 if(ucstring_isnonempty(ii->icon_text)) {
170 ucstring_append_ucstring(s, ii->icon_text);
173 if(token) {
174 if(ucstring_isnonempty(s)) {
175 ucstring_append_char(s, '.');
177 ucstring_append_sz(s, token, DE_ENCODING_UTF8);
180 de_finfo_set_name_from_ucstring(c, fi, s, 0x0);
181 ucstring_destroy(s);
184 static void do_bilevel_icon(deark *c, lctx *d, struct iconinfo *ii, i64 fg_pos,
185 i64 mask_pos, const char *token)
187 de_bitmap *img = NULL;
188 de_bitmap *mask = NULL;
189 de_finfo *fi = NULL;
191 if(!is_valid_segment_pos(c, d, fg_pos, ii->height*ii->mono_rowspan, "bitmap")) {
192 goto done;
194 if(!is_valid_segment_pos(c, d, mask_pos, ii->height*ii->mono_rowspan, "mask")) {
195 goto done;
197 if(!de_good_image_dimensions(c, ii->width, ii->height)) {
198 goto done;
201 img = de_bitmap_create(c, ii->width, ii->height, 2);
202 mask = de_bitmap_create(c, ii->width, ii->height, 1);
203 do_decode_bilevel_image(c, d, img, fg_pos, ii->mono_rowspan);
204 do_decode_bilevel_image(c, d, mask, mask_pos, ii->mono_rowspan);
205 de_bitmap_apply_mask(img, mask, DE_BITMAPFLAG_WHITEISTRNS);
206 fi = de_finfo_create(c);
207 set_icon_finfo(c, d, fi, ii, token);
208 de_bitmap_write_to_file_finfo(img, fi, 0);
210 done:
211 de_bitmap_destroy(img);
212 de_bitmap_destroy(mask);
213 de_finfo_destroy(c, fi);
216 static int do_old_iconblk(deark *c, lctx *d, i64 pos)
218 i64 mask_pos, fg_pos;
219 int retval = 0;
220 struct iconinfo *ii = NULL;
221 int saved_indent_level;
223 de_dbg_indent_save(c, &saved_indent_level);
224 ii = de_malloc(c, sizeof(struct iconinfo));
226 de_dbg(c, "ICONBLK at %"I64_FMT, pos);
227 de_dbg_indent(c, 1);
228 if(!do_scan_iconblk(c, d, pos, ii)) goto done;
230 mask_pos = gem_getu32(d, pos);
231 fg_pos = gem_getu32(d, pos+4);
232 de_dbg(c, "fg at %"I64_FMT, fg_pos);
233 de_dbg(c, "mask at %"I64_FMT, mask_pos);
235 ii->mono_rowspan = ((ii->width+15)/16)*2;
236 do_bilevel_icon(c, d, ii, fg_pos, mask_pos, "1");
238 retval = 1;
239 done:
240 destroy_iconinfo(c, ii);
241 de_dbg_indent_restore(c, saved_indent_level);
242 return retval;
245 // TODO: This palette may not be correct.
246 static const u32 pal16[16] = {
247 0xffffff,0xff0000,0x00ff00,0xffff00,0x0000ff,0xff00ff,0x00ffff,0xc0c0c0,
248 0x808080,0xff8080,0x80ff80,0xffff80,0x8080ff,0xff80ff,0x80ffff,0x000000
251 // FIXME: This palette is incomplete, and probably inaccurate.
252 static const u32 supplpal1[16] = {
253 0xffffff,0xef0000,0x00e700,0xffff00,0x0000ef,0xcd05cd,0xcd06cd,0xd6d6d6, // 00-07
254 0x808080,0x7b0000,0x008000,0xb5a531,0x000080,0x7f007f,0x007b7b,0x101810 // 08-ff
257 static const u32 supplpal2[26] = {
258 0xef0000,0xe70000, // e6-e7
259 0xbd0000,0xad0000,0x7b0000,0x4a0000,0x100000,0xcdedcd,0xcdeecd,0x00bd00, // e8-ef
260 0x00b500,0xcdf1cd,0x004a00,0x001800,0x000010,0x00004f,0xcdf6cd,0x0000af, // f0-f7
261 0x293194,0x0000e0,0xeff7ef,0xe7e7e7,0xc0c0c0,0xadb5ad,0x4a4a4a,0x000000 // f8-ff
264 static u32 getpal16(unsigned int k)
266 if(k>=16) return 0;
267 return pal16[k];
270 static u32 getpal256(unsigned int k)
272 unsigned int x;
273 u8 r, g, b;
275 if(k<=15) {
276 // first 16 entries
277 return supplpal1[k];
279 else if(k<=229) {
280 // next 214 entries
281 x = k-15;
282 r = (u8)((x/36)*0x33);
283 g = ((x%36)/6)*0x33;
284 b = (x%6)*0x33;
285 return DE_MAKE_RGB(r,g,b);
287 else if(k<=255) {
288 // last 26 entries
289 return supplpal2[k-230];
291 return 0;
294 // FIXME: This probably doesn't work for PC format (little-endian).
295 static void do_color_icon(deark *c, lctx *d, struct iconinfo *ii, i64 fg_pos,
296 i64 mask_pos, const char *token)
298 i64 i, j;
299 u8 a;
300 de_bitmap *img = NULL;
301 de_finfo *fi = NULL;
302 i64 plane;
303 i64 planespan;
304 u8 b;
305 unsigned int v;
306 u32 clr;
308 if(ii->nplanes!=4 && ii->nplanes!=8) {
309 de_warn(c, "%d-plane icons not supported", (int)ii->nplanes);
310 goto done;
312 if(!de_good_image_dimensions(c, ii->width, ii->height)) {
313 goto done;
316 img = de_bitmap_create(c, ii->width, ii->height, 4);
318 planespan = ii->mono_rowspan * ii->height;
320 for(j=0; j<ii->height; j++) {
321 for(i=0; i<ii->width; i++) {
322 v = 0;
323 for(plane=0; plane<ii->nplanes; plane++) {
324 b = de_get_bits_symbol(c->infile, 1,
325 fg_pos + j*ii->mono_rowspan + plane*(planespan), i);
326 if(b) v |= 1<<plane;
328 if(ii->nplanes==4)
329 clr = getpal16(v);
330 else
331 clr = getpal256(v);
333 a = de_get_bits_symbol(c->infile, 1, mask_pos + j*ii->mono_rowspan, i);
334 a = a ? 255 : 0;
336 de_bitmap_setpixel_rgba(img, i, j, DE_SET_ALPHA(clr, a));
340 fi = de_finfo_create(c);
341 set_icon_finfo(c, d, fi, ii, token);
342 de_bitmap_write_to_file_finfo(img, fi, 0);
344 done:
345 de_bitmap_destroy(img);
346 de_finfo_destroy(c, fi);
349 static int do_ciconblk_struct(deark *c, lctx *d, i64 icon_idx, i64 pos1,
350 i64 *bytes_consumed)
352 struct iconinfo *ii = NULL;
353 i64 pos;
354 i64 n_cicons;
355 i64 mono_bitmapsize;
356 i64 color_bitmapsize;
357 i64 next_res;
358 i64 sel_data_flag;
359 int retval = 0;
360 i64 i;
361 i64 mono_fgpos, mono_maskpos;
362 int saved_indent_level;
363 char token[16];
365 de_dbg_indent_save(c, &saved_indent_level);
366 de_dbg(c, "CICONBLK[%d] at %"I64_FMT, (int)icon_idx, pos1);
367 de_dbg_indent(c, 1);
369 ii = de_malloc(c, sizeof(struct iconinfo));
371 pos = pos1;
372 if(!do_scan_iconblk(c, d, pos, ii)) {
373 goto done;
375 pos+=34;
377 n_cicons = gem_getu32(d, pos);
378 de_dbg(c, "number of color depths for this icon: %d", (int)n_cicons);
379 pos += 4;
381 ii->mono_rowspan = ((ii->width+15)/16)*2; // guess
383 de_dbg2(c, "bilevel image data at %"I64_FMT" (deferred)", pos);
384 mono_bitmapsize = ii->mono_rowspan * ii->height;
385 mono_fgpos = pos;
386 pos += mono_bitmapsize; // foreground
387 mono_maskpos = pos;
388 pos += mono_bitmapsize; // mask
389 de_dbg2(c, "bilevel image data ends at %"I64_FMT, pos);
391 if(!ii->icon_text) {
392 ii->icon_text = ucstring_create(c);
394 ucstring_empty(ii->icon_text);
395 dbuf_read_to_ucstring(c->infile, pos, 12, ii->icon_text, DE_CONVFLAG_STOP_AT_NUL,
396 d->input_encoding);
397 de_dbg(c, "icon text: \"%s\"", ucstring_getpsz_d(ii->icon_text));
398 pos += 12;
400 // Go back and read the bilevel icon. (We wanted to read the icon text first.)
401 de_dbg(c, "bilevel image data at %"I64_FMT, mono_fgpos);
402 de_dbg_indent(c, 1);
403 de_dbg(c, "fg at %"I64_FMT, mono_fgpos);
404 de_dbg(c, "mask at %"I64_FMT, mono_maskpos);
405 do_bilevel_icon(c, d, ii, mono_fgpos, mono_maskpos, "1");
406 de_dbg_indent(c, -1);
408 for(i=0; i<n_cicons; i++) {
409 if(pos >= c->infile->len) goto done;
410 de_dbg(c, "color depth %d of %d, at %"I64_FMT, (int)(i+1), (int)n_cicons, pos);
411 de_dbg_indent(c, 1);
413 ii->nplanes = gem_getu16(d, pos);
414 de_dbg(c, "planes: %d", (int)ii->nplanes);
415 pos += 2;
417 pos += 4; // col_data (placeholder)
418 pos += 4; // col_mask (placeholder)
420 sel_data_flag = gem_getu32(d, pos);
421 de_dbg(c, "sel_data flag: %d", (int)sel_data_flag);
422 pos += 4; // sel_data
424 pos += 4; // sel_mask (placeholder)
426 next_res = gem_getu32(d, pos);
427 de_dbg(c, "next_res flag: %d", (int)next_res);
428 pos += 4;
430 color_bitmapsize = mono_bitmapsize * ii->nplanes;
432 de_dbg(c, "unselected image at %"I64_FMT, pos);
433 de_dbg_indent(c, 1);
434 de_dbg(c, "fg at %"I64_FMT, pos);
435 de_dbg(c, "mask at %"I64_FMT, pos+color_bitmapsize);
436 de_snprintf(token, sizeof(token), "%d", (int)ii->nplanes);
437 do_color_icon(c, d, ii, pos, pos+color_bitmapsize, token);
438 pos += color_bitmapsize; // color_data
439 pos += mono_bitmapsize; // color_mask
440 de_dbg_indent(c, -1);
442 if(sel_data_flag) {
443 de_dbg(c, "selected image at %"I64_FMT, pos);
444 de_dbg_indent(c, 1);
445 de_dbg(c, "fg at %"I64_FMT, pos);
446 de_dbg(c, "mask at %"I64_FMT, pos+color_bitmapsize);
447 de_snprintf(token, sizeof(token), "%d.sel", (int)ii->nplanes);
448 do_color_icon(c, d, ii, pos, pos+color_bitmapsize, token);
449 pos += color_bitmapsize; // select_data
450 pos += mono_bitmapsize; // select_mask
451 de_dbg_indent(c, -1);
454 *bytes_consumed = pos - pos1;
456 de_dbg_indent(c, -1);
459 retval = 1;
460 done:
461 destroy_iconinfo(c, ii);
462 de_dbg_indent_restore(c, saved_indent_level);
463 return retval;
466 static int do_cicon_ptr_table(deark *c, lctx *d, i64 pos1, i64 *bytes_consumed)
468 i64 n;
469 i64 count = 0;
470 i64 pos;
471 int retval = 0;
472 int saved_indent_level;
474 de_dbg_indent_save(c, &saved_indent_level);
475 pos = pos1;
476 *bytes_consumed = 0;
478 de_dbg(c, "CICONBLK pointer table at %"I64_FMT, pos1);
479 de_dbg_indent(c, 1);
481 while(1) {
482 if(pos>=d->avail_file_size) {
483 // error
484 goto done;
487 // Values are expected to be 0. We just have to find the -1 that marks
488 // the end of this scratch space.
489 n = gem_getu32m1(d, pos);
490 de_dbg3(c, "item[%d]: %"I64_FMT, (int)count, n);
491 pos+=4;
493 if(n<0) {
494 break;
496 count++;
499 d->num_ciconblk = count;
500 de_dbg(c, "count of CICONBLKs: %d", (int)d->num_ciconblk);
501 *bytes_consumed = pos - pos1;
502 retval = 1;
504 done:
505 de_dbg_indent_restore(c, saved_indent_level);
506 return retval;
509 static int do_cicon(deark *c, lctx *d)
511 i64 bytes_consumed;
512 int ret;
513 i64 pos1;
514 i64 pos;
515 i64 i;
516 int retval = 0;
517 int saved_indent_level;
519 de_dbg_indent_save(c, &saved_indent_level);
520 if(!is_valid_segment_pos(c, d, d->cicon_offs, 1, "CICON segment")) {
521 goto done;
523 pos1 = d->cicon_offs;
524 de_dbg(c, "CICON file segment at %"I64_FMT, pos1);
525 de_dbg_indent(c, 1);
526 pos = pos1;
527 ret = do_cicon_ptr_table(c, d, pos, &bytes_consumed);
528 if(!ret) goto done;
529 pos += bytes_consumed;
531 for(i=0; i<d->num_ciconblk; i++) {
532 if(pos>=d->avail_file_size) goto done;
533 ret = do_ciconblk_struct(c, d, i, pos, &bytes_consumed);
534 if(!ret) goto done;
535 pos += bytes_consumed;
537 retval = 1;
539 done:
540 de_dbg_indent_restore(c, saved_indent_level);
541 return retval;
544 static void do_extension_array(deark *c, lctx *d)
546 i64 pos;
548 pos = d->rssize;
549 de_dbg(c, "extension array at %"I64_FMT, d->rssize);
550 de_dbg_indent(c, 1);
551 if(!is_valid_segment_pos(c, d, d->rssize, 8, "Extension Array")) goto done;
553 d->reported_file_size = gem_getu32(d, pos);
554 de_dbg(c, "reported file size: %"I64_FMT, d->reported_file_size);
555 d->avail_file_size = de_min_int(d->reported_file_size, c->infile->len);
557 d->cicon_offs = gem_getu32m1(d, pos+4);
558 if(d->cicon_offs==0) goto done;
559 de_dbg(c, "CICON offset: %"I64_FMT, d->cicon_offs);
561 done:
562 de_dbg_indent(c, -1);
565 #define OBJTYPE_IMAGE 23
566 #define OBJTYPE_ICON 31
567 #define OBJTYPE_CLRICON 33
569 static const char *get_obj_type_name(u8 t)
571 const char *s = NULL;
573 switch(t) {
574 case 20: s="box"; break;
575 case 21: s="formatted text"; break;
576 case 22: s="formatted text in a box"; break;
577 case OBJTYPE_IMAGE: s="image"; break;
578 case 24: s="programmer-defined object"; break;
579 case 25: s="invisible box"; break;
580 case 26: s="push button w/string"; break;
581 case 27: s="character in a box"; break;
582 case 28: s="unformatted text"; break;
583 case 29: s="editable formatted text"; break;
584 case 30: s="editable formatted text in a box"; break;
585 case OBJTYPE_ICON: s="icon"; break;
586 case 32: s="menu title"; break;
587 case OBJTYPE_CLRICON: s="clricon"; break;
589 return s?s:"?";
592 // The OBJECT table contains references to the bitmaps and icons in the file.
593 // It's not clear if we have to read it, because there are also pointers in
594 // the file header.
595 // TODO: Do we need to read it to get the true width of BITBLK images?
596 // TODO: We may need to read it to identify color icons in old-style RSC.
597 static int do_object(deark *c, lctx *d, i64 obj_index, i64 pos)
599 i64 obj_type_orig;
600 u8 obj_type;
601 i64 next_sibling, first_child, last_child;
602 i64 ob_spec;
603 i64 width, height;
605 de_dbg(c, "OBJECT #%d at %d", (int)obj_index, (int)pos);
606 de_dbg_indent(c, 1);
608 next_sibling = gem_getu16m1(d, pos);
609 first_child = gem_getu16m1(d, pos+2);
610 last_child = gem_getu16m1(d, pos+4);
611 de_dbg(c, "next sibling: %d, first child: %d, last child: %d",
612 (int)next_sibling, (int)first_child, (int)last_child);
614 obj_type_orig = gem_getu16(d, pos+6);
615 obj_type = (u8)(obj_type_orig&0xff);
617 de_dbg(c, "type: 0x%04x (%u; %s)", (unsigned int)obj_type_orig,
618 (unsigned int)obj_type, get_obj_type_name(obj_type));
620 ob_spec = gem_getu32(d, pos+12);
621 de_dbg(c, "ob_spec: %u (0x%08x)", (unsigned int)ob_spec, (unsigned int)ob_spec);
623 // Note: This does not seem to read the width and height fields correctly.
624 // Don't know what I'm doing wrong.
625 // (Fortunately, we don't necessarily need them.)
626 width = gem_getu16(d, pos+20);
627 height = gem_getu16(d, pos+22);
628 de_dbg_dimensions(c, width, height);
630 de_dbg_indent(c, -1);
631 return 1;
634 static int do_bitblk(deark *c, lctx *d, i64 pos)
636 i64 bits_pos;
637 i64 width_in_bytes;
638 i64 width, height;
639 i64 fgcol;
641 de_dbg(c, "BITBLK at %"I64_FMT, pos);
642 de_dbg_indent(c, 1);
644 bits_pos = gem_getu32(d, pos);
645 de_dbg(c, "bitmap pos: %"I64_FMT, bits_pos);
646 width_in_bytes = gem_getu16(d, pos+4);
647 width = width_in_bytes*8;
648 de_dbg(c, "width in bytes: %d", (int)width_in_bytes);
649 height = gem_getu16(d, pos+6);
650 de_dbg_dimensions(c, width, height);
651 fgcol = gem_getu16(d, pos+12);
652 de_dbg(c, "foreground color: 0x%04x", (unsigned int)fgcol);
653 // TODO: Can we do anything with the foreground color?
655 do_decode_and_write_bilevel_image(c, d, bits_pos, width_in_bytes, width, height);
657 de_dbg_indent(c, -1);
658 return 1;
661 static void do_OBJECTs(deark *c, lctx *d)
663 i64 i;
665 if(d->object_num<=0) return;
666 de_dbg(c, "OBJECTs at %"I64_FMT, d->object_offs);
667 if(!is_valid_segment_pos(c, d, d->object_offs, 24*d->object_num, "OBJECT table")) {
668 return;
670 if(!d->decode_objects) return;
672 de_dbg_indent(c, 1);
673 for(i=0; i<d->object_num; i++) {
674 if(d->object_offs + 24*(i+1) > d->avail_file_size) break;
675 do_object(c, d, i, d->object_offs + 24*i);
677 de_dbg_indent(c, -1);
680 static void do_BITBLKs(deark *c, lctx *d)
682 i64 i;
684 if(d->bitblk_num<=0) return;
685 de_dbg(c, "BITBLKs at %"I64_FMT, d->bitblk_offs);
686 if(!is_valid_segment_pos(c, d, d->bitblk_offs, 14*d->bitblk_num, "BITBLK table")) {
687 return;
689 de_dbg_indent(c, 1);
690 for(i=0; i<d->bitblk_num; i++) {
691 if(d->bitblk_offs + 14*(i+1) > d->avail_file_size) break;
692 do_bitblk(c, d, d->bitblk_offs + 14*i);
694 de_dbg_indent(c, -1);
697 static void do_ICONBLKs(deark *c, lctx *d)
699 i64 i;
701 if(d->iconblk_num<=0) return;
702 de_dbg(c, "ICONBLKs at %"I64_FMT, d->iconblk_offs);
703 if(!is_valid_segment_pos(c, d, d->iconblk_offs, 34*d->iconblk_num, "ICONBLK table")) {
704 return;
706 de_dbg_indent(c, 1);
707 for(i=0; i<d->iconblk_num; i++) {
708 if(d->iconblk_offs + 34*(i+1) > d->avail_file_size) break;
709 do_old_iconblk(c, d, d->iconblk_offs + 34*i);
711 de_dbg_indent(c, -1);
714 static void detect_rsc_format(deark *c, lctx *d)
716 i64 n_be, n_le;
717 i64 pos;
719 // Check the version number. Assumes PC format is always 0.
720 n_be = de_getu16be(0);
721 if(n_be != 0) {
722 d->fmt = RSCFMT_ATARI;
723 return;
726 // Check the (old-style) file size field
727 n_le = de_getu16le(34);
728 n_be = de_getu16be(34);
729 if(n_le != n_be) {
730 if(n_be==c->infile->len) {
731 d->fmt = RSCFMT_ATARI;
732 return;
734 if(n_le==c->infile->len) {
735 d->fmt = RSCFMT_PC;
736 return;
740 // Check some file offsets
741 for(pos=2; pos<=18; pos+=2) {
742 n_le = de_getu16le(pos);
743 if(n_le==0 || n_le==0xffff) continue;
744 n_be = de_getu16be(pos);
745 if(n_le > c->infile->len) {
746 d->fmt = RSCFMT_ATARI;
747 return;
749 if(n_be > c->infile->len) {
750 d->fmt = RSCFMT_PC;
751 return;
753 // Offsets should be even, I think.
754 if(n_le&0x1) {
755 d->fmt = RSCFMT_ATARI;
756 return;
758 if(n_be&0x1) {
759 d->fmt = RSCFMT_PC;
760 return;
764 // TODO: Is it worth doing more checks?
767 static void de_run_rsc(deark *c, de_module_params *mparams)
769 lctx *d = NULL;
770 const char *tmps;
772 d = de_malloc(c, sizeof(lctx));
773 d->c = c;
774 d->avail_file_size = c->infile->len; // Starting value. Will be adjusted later.
776 d->fmt = RSCFMT_UNKNOWN;
777 tmps = de_get_ext_option(c, "rsc:fmt");
778 if(tmps) {
779 if(!de_strcmp(tmps, "pc")) {
780 d->fmt = RSCFMT_PC;
782 else if(!de_strcmp(tmps, "atari")) {
783 d->fmt = RSCFMT_ATARI;
787 if(d->fmt==RSCFMT_UNKNOWN) {
788 detect_rsc_format(c, d);
791 if(d->fmt==RSCFMT_UNKNOWN) {
792 d->fmt = RSCFMT_ATARI;
795 if(d->fmt==RSCFMT_PC) {
796 de_declare_fmt(c, "GEM RSC, PC");
797 d->is_le = 1;
799 else {
800 de_declare_fmt(c, "GEM RSC, Atari");
803 d->input_encoding = de_get_input_encoding(c, NULL, DE_ENCODING_UNKNOWN);
804 if(d->input_encoding==DE_ENCODING_UNKNOWN) {
805 if(d->fmt==RSCFMT_ATARI) {
806 d->input_encoding = DE_ENCODING_ATARIST;
808 else {
809 // TODO?: This should probably be the "GEM character set", but we don't
810 // support that.
811 d->input_encoding = DE_ENCODING_ASCII;
815 d->decode_objects = 1;
816 // TODO: For Atari format, maybe we can disallow unaligned offsets.
817 d->allow_unaligned_offsets = 1;
819 de_dbg(c, "header at %d", 0);
820 de_dbg_indent(c, 1);
821 d->version = gem_getu16(d, 0);
822 de_dbg(c, "version: 0x%04x", (int)d->version);
824 d->object_offs = gem_getu16(d, 2);
825 d->iconblk_offs = gem_getu16(d, 6);
826 d->bitblk_offs = gem_getu16(d, 8);
827 d->imagedata_offs = gem_getu16(d, 14);
828 d->imagepointertable_offs = gem_getu16(d, 16);
829 d->object_num = gem_getu16(d, 20);
830 d->objecttree_num = gem_getu16(d, 22);
831 d->iconblk_num = gem_getu16(d, 26);
832 d->bitblk_num = gem_getu16(d, 28);
833 d->rssize = gem_getu16(d, 34);
835 de_dbg(c, "OBJECT: %d at %d", (int)d->object_num, (int)d->object_offs);
836 de_dbg(c, "num object trees: %d", (int)d->objecttree_num);
837 de_dbg(c, "ICONBLK: %d at %d", (int)d->iconblk_num, (int)d->iconblk_offs);
838 de_dbg(c, "BITBLK: %d at %d", (int)d->bitblk_num, (int)d->bitblk_offs);
839 de_dbg(c, "imagedata: at %d", (int)d->imagedata_offs);
840 de_dbg(c, "imagepointertable: at %d", (int)d->imagepointertable_offs);
841 if(d->version & 0x0004) {
842 de_dbg(c, "extension array offset: %"I64_FMT, d->rssize);
844 else {
845 de_dbg(c, "reported file size: %"I64_FMT, d->rssize);
846 d->reported_file_size = d->rssize;
847 d->avail_file_size = de_min_int(d->reported_file_size, c->infile->len);
849 de_dbg_indent(c, -1);
851 if(d->version & 0x0004) {
852 do_extension_array(c, d);
854 else if(d->version==0 || d->version==1) {
857 else {
858 de_err(c, "Unknown or unsupported version of RSC");
859 goto done;
862 do_OBJECTs(c, d);
863 do_BITBLKs(c, d);
864 do_ICONBLKs(c, d);
865 if(d->version & 0x0004) {
866 do_cicon(c, d);
869 done:
870 de_free(c, d);
873 // TODO: This needs to be improved, but it's complicated.
874 static int de_identify_rsc(deark *c)
876 i64 ver;
878 if(!de_input_file_has_ext(c, "rsc")) return 0;
879 ver = de_getu16be(0);
880 if(ver==0 || ver==1 || ver==4) return 70;
881 return 0;
884 static void de_help_rsc(deark *c)
886 de_msg(c, "-opt rsc:fmt=<atari|pc> : Use this byte order");
889 void de_module_rsc(deark *c, struct deark_module_info *mi)
891 mi->id = "rsc";
892 mi->desc = "GEM resource file";
893 mi->run_fn = de_run_rsc;
894 mi->identify_fn = de_identify_rsc;
895 mi->help_fn = de_help_rsc;