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
16 #define MAX_RSC_ICON_WIDTH 1024
17 #define MAX_RSC_ICON_HEIGHT 1024
19 typedef struct localctx_struct
{
21 de_ext_encoding input_encoding
;
25 u8 allow_unaligned_offsets
;
27 i64 object_offs
, object_num
;
29 i64 iconblk_offs
, iconblk_num
;
30 i64 bitblk_offs
, bitblk_num
;
32 i64 imagepointertable_offs
;
35 i64 reported_file_size
;
48 de_ucstring
*icon_text
;
51 static int is_valid_segment_pos(deark
*c
, lctx
*d
, i64 pos
, i64 len
, const char *name
)
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);
66 de_err(c
, "Invalid %s location: %"I64_FMT
, name
, pos
);
71 static void destroy_iconinfo(deark
*c
, struct iconinfo
*ii
)
75 ucstring_destroy(ii
->icon_text
);
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
);
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;
104 static void do_decode_bilevel_image(deark
*c
, lctx
*d
, de_bitmap
*img
, i64 bits_pos
,
108 i64 rowspan_in_16bit_chunks
;
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
++) {
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")) {
135 if(!de_good_image_dimensions(c
, width
, height
)) {
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
);
143 de_bitmap_destroy(img
);
146 static int do_scan_iconblk(deark
*c
, lctx
*d
, i64 pos1
, struct iconinfo
*ii
)
150 // TODO: Refactor this code to better share it with old- and new-style RSC.
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");
165 static void set_icon_finfo(deark
*c
, lctx
*d
, de_finfo
*fi
, struct iconinfo
*ii
,
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
);
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);
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
;
194 if(!is_valid_segment_pos(c
, d
, fg_pos
, ii
->height
*ii
->mono_rowspan
, "bitmap")) {
197 if(!is_valid_segment_pos(c
, d
, mask_pos
, ii
->height
*ii
->mono_rowspan
, "mask")) {
200 if(!de_good_image_dimensions(c
, ii
->width
, ii
->height
)) {
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
);
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
;
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
);
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");
243 destroy_iconinfo(c
, ii
);
244 de_dbg_indent_restore(c
, saved_indent_level
);
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
)
273 static de_color
getpal256(unsigned int k
)
285 r
= (u8
)((x
/36)*0x33);
288 return DE_MAKE_RGB(r
,g
,b
);
292 return supplpal2
[k
-230];
297 static void construct_palettes(deark
*c
, lctx
*d
)
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
;
319 const de_color
*pal_to_use
;
321 // TODO: Images with 5, 6, or 7 planes exist, but I don't know what palettes
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.
328 de_warn(c
, "%d-plane icons not supported", (int)ii
->nplanes
);
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
);
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
,
366 struct iconinfo
*ii
= NULL
;
370 i64 color_bitmapsize
;
375 i64 mono_fgpos
, mono_maskpos
;
376 int saved_indent_level
;
379 de_dbg_indent_save(c
, &saved_indent_level
);
380 de_dbg(c
, "CICONBLK[%d] at %"I64_FMT
, (int)icon_idx
, pos1
);
383 ii
= de_malloc(c
, sizeof(struct iconinfo
));
386 if(!do_scan_iconblk(c
, d
, pos
, ii
)) {
391 n_cicons
= gem_getu32(d
, pos
);
392 de_dbg(c
, "number of color depths for this icon: %d", (int)n_cicons
);
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
;
400 pos
+= mono_bitmapsize
; // foreground
402 pos
+= mono_bitmapsize
; // mask
403 de_dbg2(c
, "bilevel image data ends at %"I64_FMT
, pos
);
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
,
411 de_dbg(c
, "icon text: \"%s\"", ucstring_getpsz_d(ii
->icon_text
));
414 if(!de_good_image_dimensions(c
, ii
->width
, ii
->height
)) {
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
);
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
);
431 ii
->nplanes
= gem_getu16(d
, pos
);
432 de_dbg(c
, "planes: %d", (int)ii
->nplanes
);
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
);
448 color_bitmapsize
= mono_bitmapsize
* ii
->nplanes
;
450 de_dbg(c
, "unselected image at %"I64_FMT
, pos
);
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);
461 de_dbg(c
, "selected image at %"I64_FMT
, pos
);
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);
479 destroy_iconinfo(c
, ii
);
480 de_dbg_indent_restore(c
, saved_indent_level
);
484 static int do_cicon_ptr_table(deark
*c
, lctx
*d
, i64 pos1
, i64
*bytes_consumed
)
490 int saved_indent_level
;
492 de_dbg_indent_save(c
, &saved_indent_level
);
496 de_dbg(c
, "CICONBLK pointer table at %"I64_FMT
, pos1
);
500 if(pos
>=d
->avail_file_size
) {
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
);
517 d
->num_ciconblk
= count
;
518 de_dbg(c
, "count of CICONBLKs: %d", (int)d
->num_ciconblk
);
519 *bytes_consumed
= pos
- pos1
;
523 de_dbg_indent_restore(c
, saved_indent_level
);
527 static int do_cicon(deark
*c
, lctx
*d
)
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")) {
541 pos1
= d
->cicon_offs
;
542 de_dbg(c
, "CICON file segment at %"I64_FMT
, pos1
);
545 ret
= do_cicon_ptr_table(c
, d
, pos
, &bytes_consumed
);
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
);
553 pos
+= bytes_consumed
;
558 de_dbg_indent_restore(c
, saved_indent_level
);
562 static void do_extension_array(deark
*c
, lctx
*d
)
567 de_dbg(c
, "extension array at %"I64_FMT
, d
->rssize
);
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
);
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
;
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;
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
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
)
619 i64 next_sibling
, first_child
, last_child
;
623 de_dbg(c
, "OBJECT #%d at %d", (int)obj_index
, (int)pos
);
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);
652 static int do_bitblk(deark
*c
, lctx
*d
, i64 pos
)
659 de_dbg(c
, "BITBLK at %"I64_FMT
, pos
);
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);
679 static void do_OBJECTs(deark
*c
, lctx
*d
)
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")) {
688 if(!d
->decode_objects
) return;
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
)
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")) {
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
)
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")) {
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
)
737 // Check the version number. Assumes PC format is always 0.
738 n_be
= de_getu16be(0);
740 d
->fmt
= RSCFMT_ATARI
;
744 // Check the (old-style) file size field
745 n_le
= de_getu16le(34);
746 n_be
= de_getu16be(34);
748 if(n_be
==c
->infile
->len
) {
749 d
->fmt
= RSCFMT_ATARI
;
752 if(n_le
==c
->infile
->len
) {
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
;
767 if(n_be
> c
->infile
->len
) {
771 // Offsets should be even, I think.
773 d
->fmt
= RSCFMT_ATARI
;
782 // TODO: Is it worth doing more checks?
785 static void de_run_rsc(deark
*c
, de_module_params
*mparams
)
790 d
= de_malloc(c
, sizeof(lctx
));
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");
797 if(!de_strcmp(tmps
, "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");
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
;
827 // TODO?: This should probably be the "GEM character set", but we don't
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);
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
);
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) {
876 de_err(c
, "Unknown or unsupported version of RSC");
883 if(d
->version
& 0x0004) {
891 // TODO: This needs to be improved, but it's complicated.
892 static int de_identify_rsc(deark
*c
)
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;
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
)
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
;