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
;
45 de_ucstring
*icon_text
;
48 static int is_valid_segment_pos(deark
*c
, lctx
*d
, i64 pos
, i64 len
, const char *name
)
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);
63 de_err(c
, "Invalid %s location: %"I64_FMT
, name
, pos
);
68 static void destroy_iconinfo(deark
*c
, struct iconinfo
*ii
)
72 ucstring_destroy(ii
->icon_text
);
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
);
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;
101 static void do_decode_bilevel_image(deark
*c
, lctx
*d
, de_bitmap
*img
, i64 bits_pos
,
105 i64 rowspan_in_16bit_chunks
;
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
++) {
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")) {
132 if(!de_good_image_dimensions(c
, width
, height
)) {
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);
140 de_bitmap_destroy(img
);
143 static int do_scan_iconblk(deark
*c
, lctx
*d
, i64 pos1
, struct iconinfo
*ii
)
147 // TODO: Refactor this code to better share it with old- and new-style RSC.
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");
162 static void set_icon_finfo(deark
*c
, lctx
*d
, de_finfo
*fi
, struct iconinfo
*ii
,
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
);
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);
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
;
191 if(!is_valid_segment_pos(c
, d
, fg_pos
, ii
->height
*ii
->mono_rowspan
, "bitmap")) {
194 if(!is_valid_segment_pos(c
, d
, mask_pos
, ii
->height
*ii
->mono_rowspan
, "mask")) {
197 if(!de_good_image_dimensions(c
, ii
->width
, ii
->height
)) {
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);
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
;
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
);
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");
240 destroy_iconinfo(c
, ii
);
241 de_dbg_indent_restore(c
, saved_indent_level
);
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
)
270 static u32
getpal256(unsigned int k
)
282 r
= (u8
)((x
/36)*0x33);
285 return DE_MAKE_RGB(r
,g
,b
);
289 return supplpal2
[k
-230];
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
)
300 de_bitmap
*img
= NULL
;
308 if(ii
->nplanes
!=4 && ii
->nplanes
!=8) {
309 de_warn(c
, "%d-plane icons not supported", (int)ii
->nplanes
);
312 if(!de_good_image_dimensions(c
, ii
->width
, ii
->height
)) {
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
++) {
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
);
333 a
= de_get_bits_symbol(c
->infile
, 1, mask_pos
+ j
*ii
->mono_rowspan
, i
);
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);
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
,
352 struct iconinfo
*ii
= NULL
;
356 i64 color_bitmapsize
;
361 i64 mono_fgpos
, mono_maskpos
;
362 int saved_indent_level
;
365 de_dbg_indent_save(c
, &saved_indent_level
);
366 de_dbg(c
, "CICONBLK[%d] at %"I64_FMT
, (int)icon_idx
, pos1
);
369 ii
= de_malloc(c
, sizeof(struct iconinfo
));
372 if(!do_scan_iconblk(c
, d
, pos
, ii
)) {
377 n_cicons
= gem_getu32(d
, pos
);
378 de_dbg(c
, "number of color depths for this icon: %d", (int)n_cicons
);
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
;
386 pos
+= mono_bitmapsize
; // foreground
388 pos
+= mono_bitmapsize
; // mask
389 de_dbg2(c
, "bilevel image data ends at %"I64_FMT
, pos
);
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
,
397 de_dbg(c
, "icon text: \"%s\"", ucstring_getpsz_d(ii
->icon_text
));
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
);
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
);
413 ii
->nplanes
= gem_getu16(d
, pos
);
414 de_dbg(c
, "planes: %d", (int)ii
->nplanes
);
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
);
430 color_bitmapsize
= mono_bitmapsize
* ii
->nplanes
;
432 de_dbg(c
, "unselected image at %"I64_FMT
, pos
);
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);
443 de_dbg(c
, "selected image at %"I64_FMT
, pos
);
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);
461 destroy_iconinfo(c
, ii
);
462 de_dbg_indent_restore(c
, saved_indent_level
);
466 static int do_cicon_ptr_table(deark
*c
, lctx
*d
, i64 pos1
, i64
*bytes_consumed
)
472 int saved_indent_level
;
474 de_dbg_indent_save(c
, &saved_indent_level
);
478 de_dbg(c
, "CICONBLK pointer table at %"I64_FMT
, pos1
);
482 if(pos
>=d
->avail_file_size
) {
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
);
499 d
->num_ciconblk
= count
;
500 de_dbg(c
, "count of CICONBLKs: %d", (int)d
->num_ciconblk
);
501 *bytes_consumed
= pos
- pos1
;
505 de_dbg_indent_restore(c
, saved_indent_level
);
509 static int do_cicon(deark
*c
, lctx
*d
)
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")) {
523 pos1
= d
->cicon_offs
;
524 de_dbg(c
, "CICON file segment at %"I64_FMT
, pos1
);
527 ret
= do_cicon_ptr_table(c
, d
, pos
, &bytes_consumed
);
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
);
535 pos
+= bytes_consumed
;
540 de_dbg_indent_restore(c
, saved_indent_level
);
544 static void do_extension_array(deark
*c
, lctx
*d
)
549 de_dbg(c
, "extension array at %"I64_FMT
, d
->rssize
);
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
);
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
;
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;
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
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
)
601 i64 next_sibling
, first_child
, last_child
;
605 de_dbg(c
, "OBJECT #%d at %d", (int)obj_index
, (int)pos
);
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);
634 static int do_bitblk(deark
*c
, lctx
*d
, i64 pos
)
641 de_dbg(c
, "BITBLK at %"I64_FMT
, pos
);
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);
661 static void do_OBJECTs(deark
*c
, lctx
*d
)
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")) {
670 if(!d
->decode_objects
) return;
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
)
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")) {
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
)
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")) {
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
)
719 // Check the version number. Assumes PC format is always 0.
720 n_be
= de_getu16be(0);
722 d
->fmt
= RSCFMT_ATARI
;
726 // Check the (old-style) file size field
727 n_le
= de_getu16le(34);
728 n_be
= de_getu16be(34);
730 if(n_be
==c
->infile
->len
) {
731 d
->fmt
= RSCFMT_ATARI
;
734 if(n_le
==c
->infile
->len
) {
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
;
749 if(n_be
> c
->infile
->len
) {
753 // Offsets should be even, I think.
755 d
->fmt
= RSCFMT_ATARI
;
764 // TODO: Is it worth doing more checks?
767 static void de_run_rsc(deark
*c
, de_module_params
*mparams
)
772 d
= de_malloc(c
, sizeof(lctx
));
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");
779 if(!de_strcmp(tmps
, "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");
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
;
809 // TODO?: This should probably be the "GEM character set", but we don't
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);
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
);
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) {
858 de_err(c
, "Unknown or unsupported version of RSC");
865 if(d
->version
& 0x0004) {
873 // TODO: This needs to be improved, but it's complicated.
874 static int de_identify_rsc(deark
*c
)
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;
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
)
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
;