1 // This file is part of Deark.
2 // Copyright (C) 2018 Jason Summers
3 // See the file COPYING for terms of use.
5 // Mac Resource [Manager] format
7 #include <deark-config.h>
8 #include <deark-private.h>
9 #include <deark-fmtutil.h>
10 DE_DECLARE_MODULE(de_module_macrsrc
);
12 #define CODE_8BIM 0x3842494dU
13 #define CODE_ANPA 0x414e5041U
14 #define CODE_CURS 0x43555253U
15 #define CODE_MeSa 0x4d655361U
16 #define CODE_PICT 0x50494354U
17 #define CODE_SICN 0x5349434eU
18 #define CODE_cicn 0x6369636eU
19 #define CODE_crsr 0x63727372U
20 #define CODE_icns 0x69636e73U
21 #define CODE_moov 0x6d6f6f76U
23 struct icns_stream_item
{
28 typedef struct localctx_struct
{
30 i64 data_offs
, map_offs
;
31 i64 data_size
, map_size
;
33 i64 typeListOffset_abs
;
34 i64 nameListOffset_abs
;
36 const char *errmsgprefix
;
38 #define MAX_ICNS_STREAMS 64
39 size_t num_icns_streams_used
;
40 struct icns_stream_item icns_strm
[MAX_ICNS_STREAMS
];
49 struct rsrcinstanceinfo
{
60 #define CODE_ICN_ 0x49434e23U // ICN#
61 #define CODE_ICON 0x49434f4eU
62 #define CODE_icl4 0x69636c34U
63 #define CODE_icl8 0x69636c38U
64 #define CODE_icm_ 0x69636d23U // icm#
65 #define CODE_icm4 0x69636d34U
66 #define CODE_icm8 0x69636d38U
67 #define CODE_ics_ 0x69637323U // ics#
68 #define CODE_ics4 0x69637334U
69 #define CODE_ics8 0x69637338U
71 // Helper function to set the filename of an finfo.
72 static void set_resource_filename(deark
*c
, lctx
*d
, de_finfo
*fi
,
73 struct rsrctypeinfo
*rti
, struct rsrcinstanceinfo
*rii
, const char *token
)
75 de_ucstring
*tmpname
= NULL
;
77 tmpname
= ucstring_create(c
);
78 if(c
->filenames_from_file
&& ucstring_isnonempty(rii
->name
)) {
79 ucstring_append_ucstring(tmpname
, rii
->name
);
80 ucstring_append_sz(tmpname
, ".", DE_ENCODING_LATIN1
);
84 ucstring_append_sz(tmpname
, token
, DE_ENCODING_LATIN1
);
87 ucstring_append_sz(tmpname
, rti
->fcc
.id_sanitized_sz
, DE_ENCODING_LATIN1
);
89 de_finfo_set_name_from_ucstring(c
, fi
, tmpname
, 0);
90 ucstring_destroy(tmpname
);
93 static int is_icns_icon(deark
*c
, lctx
*d
, struct rsrctypeinfo
*rti
)
95 // TODO: There are many more icns icon types, but it's not clear
96 // to me if any others are found in resource forks.
98 case CODE_icm_
: case CODE_icm4
: case CODE_icm8
: // 16x12
99 case CODE_ics_
: case CODE_ics4
: case CODE_ics8
: // 16x16
100 case CODE_ICN_
: case CODE_ICON
: case CODE_icl4
: case CODE_icl8
: // 32x32
106 // Some resource files have multiple icons of the same type, but I don't think
107 // an icns file is supposed to have multiple icons of the same type.
108 // So, simply writing all icons to the same icns file is not the right thing to
110 // What *is* the right thing to do is not clear to me. But separating the
111 // icons by resource ID (up to some limit) is at least an improvement.
112 static dbuf
*get_icns_stream(deark
*c
, lctx
*d
, int id
)
117 for(i
=0; i
<d
->num_icns_streams_used
; i
++) {
118 if(d
->icns_strm
[i
].id
== id
) {
124 if(d
->num_icns_streams_used
>= MAX_ICNS_STREAMS
) {
125 entry
= MAX_ICNS_STREAMS
- 1; // stream of last resort
128 entry
= d
->num_icns_streams_used
++;
129 d
->icns_strm
[entry
].id
= id
;
133 if(!d
->icns_strm
[entry
].tmpf
) {
134 d
->icns_strm
[entry
].tmpf
= dbuf_create_membuf(c
, 0, 0);
136 return d
->icns_strm
[entry
].tmpf
;
139 // Construct an .icns file from the suitable icon resources found
141 static void finalize_icns_stream(deark
*c
, lctx
*d
, dbuf
*tmpf
)
146 outf
= dbuf_create_output_file(c
, "icns", NULL
, 0);
147 dbuf_writeu32be(outf
, CODE_icns
);
148 dbuf_writeu32be(outf
, 8+tmpf
->len
);
149 dbuf_copy(tmpf
, 0, tmpf
->len
, outf
);
153 static void finalize_icns_streams(deark
*c
, lctx
*d
)
157 for(i
=0; i
<d
->num_icns_streams_used
; i
++) {
158 if(d
->icns_strm
[i
].tmpf
) {
159 finalize_icns_stream(c
, d
, d
->icns_strm
[i
].tmpf
);
160 dbuf_close(d
->icns_strm
[i
].tmpf
);
161 d
->icns_strm
[i
].tmpf
= NULL
;
166 static void open_psrc_stream(deark
*c
, lctx
*d
)
168 if(d
->psrc_stream
) return;
169 d
->psrc_stream
= dbuf_create_membuf(c
, 0, 0);
172 static void finalize_psrc_stream(deark
*c
, lctx
*d
)
174 if(!d
->psrc_stream
) return;
175 fmtutil_handle_photoshop_rsrc(c
, d
->psrc_stream
, 0, d
->psrc_stream
->len
, 0x1);
176 dbuf_close(d
->psrc_stream
);
177 d
->psrc_stream
= NULL
;
180 static void do_psrc_resource(deark
*c
, lctx
*d
, struct rsrctypeinfo
*rti
,
181 struct rsrcinstanceinfo
*rii
, i64 dpos
, i64 dlen
)
183 if(!d
->psrc_stream
) {
184 open_psrc_stream(c
, d
);
187 // Convert this exploded format to the normal Photoshop Resources format,
188 // which we will eventually write to a file.
189 // It would be messy to implement a way to directly use the decoder in the
190 // psd module. And even if we did that, the option to do this conversion
191 // might still be useful.
193 de_dbg(c
, "[Photoshop resource]");
194 dbuf_write(d
->psrc_stream
, rti
->fcc
.bytes
, 4);
195 dbuf_writei16be(d
->psrc_stream
, (i64
)rii
->id
);
197 dbuf_copy(c
->infile
, rii
->name_offset
, rii
->name_raw_len
, d
->psrc_stream
);
198 if(rii
->name_raw_len
%2) {
199 dbuf_writebyte(d
->psrc_stream
, 0); // padding byte for name
203 dbuf_write_zeroes(d
->psrc_stream
, 2);
205 dbuf_writeu32be(d
->psrc_stream
, dlen
);
206 dbuf_copy(c
->infile
, dpos
, dlen
, d
->psrc_stream
);
208 dbuf_writebyte(d
->psrc_stream
, 0); // padding byte for data
212 // Handle 'crsr' and 'CURS' cursors.
213 // The documentation of 'crsr' in ImagingWithQuickDraw seems to have only
214 // the vaguest resemblance to reality. The code here is partly based on
215 // reverse engineering.
216 static void do_crsr_CURS_resource(deark
*c
, lctx
*d
, struct rsrctypeinfo
*rti
,
217 struct rsrcinstanceinfo
*rii
, i64 pos1
, i64 dlen
)
219 struct fmtutil_macbitmap_info
*bi
= NULL
;
220 de_bitmap
*img_color
= NULL
;
221 de_bitmap
*img_bw
= NULL
;
222 de_bitmap
*img_mask
= NULL
;
226 i64 pixdata_offs
= 0;
228 i64 colortable_size
= 0;
229 int is_crsr
= (rti
->fcc
.id
==CODE_crsr
);
231 if(dlen
<68) goto done
;
232 fi
= de_finfo_create(c
);
235 // TODO: Do we need special handling for type=0x8000 (b/w cursor)?
236 n
= de_getu16be_p(&pos
);
237 de_dbg(c
, "cursor type: 0x%04x", (unsigned int)n
);
238 if(n
!=0x8000 && n
!=0x8001) {
239 de_err(c
, "Invalid or unsupported 'crsr' cursor type");;
242 pixmap_offs
= de_getu32be_p(&pos
);
243 de_dbg(c
, "offset to pixel map: %d", (int)pixmap_offs
);
244 pixdata_offs
= de_getu32be_p(&pos
);
245 de_dbg(c
, "offset to pixel data: %d", (int)pixdata_offs
);
246 pos
+= 10; // other fields
249 de_dbg(c
, "b/w foreground at %"I64_FMT
, pos
);
250 img_bw
= de_bitmap_create(c
, 16, 16, 2);
251 de_convert_image_bilevel(c
->infile
, pos
, 2, img_bw
, DE_CVTF_WHITEISZERO
);
254 de_dbg(c
, "mask at %"I64_FMT
, pos
);
255 img_mask
= de_bitmap_create(c
, 16, 16, 1);
256 de_convert_image_bilevel(c
->infile
, pos
, 2, img_mask
, 0);
259 // I'm assuming the hotspot is a QuickDraw Point structure.
260 fi
->hotspot_y
= (int)de_geti16be_p(&pos
);
261 fi
->hotspot_x
= (int)de_geti16be_p(&pos
);
263 de_dbg(c
, "hotspot: (%d,%d)", fi
->hotspot_x
, fi
->hotspot_y
);
265 de_bitmap_apply_mask(img_bw
, img_mask
, 0);
266 set_resource_filename(c
, d
, fi
, rti
, rii
, (is_crsr
?"crsr_bw":NULL
));
267 de_bitmap_write_to_file_finfo(img_bw
, fi
, 0);
268 if(!is_crsr
) goto done
;
270 bi
= de_malloc(c
, sizeof(struct fmtutil_macbitmap_info
));
271 if(pixmap_offs
>= dlen
) goto done
;
272 pos
= pos1
+pixmap_offs
;
273 fmtutil_macbitmap_read_baseaddr(c
, c
->infile
, bi
, pos
);
275 fmtutil_macbitmap_read_rowbytes_and_bounds(c
, c
->infile
, bi
, pos
);
277 fmtutil_macbitmap_read_pixmap_only_fields(c
, c
->infile
, bi
, pos
);
279 if(!de_good_image_dimensions(c
, bi
->npwidth
, bi
->height
)) goto done
;
281 if((i64
)bi
->pmTable
!= pixdata_offs
+ bi
->rowbytes
* bi
->height
) {
282 de_warn(c
, "Unexpected color table offset. "
283 "Cursor might not be decoded correctly.");
285 if(bi
->pmTable
>0 && bi
->pmTable
<dlen
) {
286 pos
= pos1
+ (i64
)bi
->pmTable
;
289 pos
= pos1
+ pixdata_offs
+ bi
->rowbytes
* bi
->height
;
291 if(!fmtutil_macbitmap_read_colortable(c
, c
->infile
, bi
, pos
, &colortable_size
)) {
295 img_color
= de_bitmap_create2(c
, bi
->npwidth
, bi
->pdwidth
, bi
->height
, 4);
297 if(pixdata_offs
>= dlen
) goto done
;
298 pos
= pos1
+ pixdata_offs
;
299 de_dbg(c
, "color pixels at %"I64_FMT
, pos
);
300 de_convert_image_paletted(c
->infile
, pos
, bi
->pixelsize
, bi
->rowbytes
,
301 bi
->pal
, img_color
, 0);
302 de_bitmap_apply_mask(img_color
, img_mask
, 0);
304 set_resource_filename(c
, d
, fi
, rti
, rii
, NULL
);
305 if(bi
->hdpi
>=1.0 && bi
->vdpi
>=1.0) {
306 fi
->density
.code
= DE_DENSITY_DPI
;
307 fi
->density
.xdens
= bi
->hdpi
;
308 fi
->density
.ydens
= bi
->vdpi
;
310 de_bitmap_write_to_file_finfo(img_color
, fi
, DE_CREATEFLAG_OPT_IMAGE
);
314 de_bitmap_destroy(img_color
);
315 de_bitmap_destroy(img_bw
);
316 de_bitmap_destroy(img_mask
);
317 de_finfo_destroy(c
, fi
);
320 // SICN - Small icons - One or more 16x16 images
321 static void do_SICN_resource(deark
*c
, lctx
*d
, struct rsrctypeinfo
*rti
,
322 struct rsrcinstanceinfo
*rii
, i64 pos1
, i64 len
)
329 fi
= de_finfo_create(c
);
330 set_resource_filename(c
, d
, fi
, rti
, rii
, NULL
);
332 for(i
=0; i
<numicons
; i
++) {
333 de_bitmap
*img
= NULL
;
335 img
= de_bitmap_create(c
, 16, 16, 1);
336 de_convert_image_bilevel(c
->infile
, pos1
+32*i
, 2, img
, DE_CVTF_WHITEISZERO
);
337 de_bitmap_write_to_file_finfo(img
, fi
, 0);
338 de_bitmap_destroy(img
);
342 static void do_cicn_resource(deark
*c
, lctx
*d
, struct rsrctypeinfo
*rti
,
343 struct rsrcinstanceinfo
*rii
, i64 dpos
, i64 dlen
)
345 struct fmtutil_macbitmap_info
*bi_fgcolor
= NULL
;
346 struct fmtutil_macbitmap_info
*bi_mask
= NULL
;
347 struct fmtutil_macbitmap_info
*bi_bw
= NULL
;
348 de_bitmap
*img_fgcolor
= NULL
;
349 de_bitmap
*img_mask
= NULL
;
350 de_bitmap
*img_bw
= NULL
;
352 i64 fgcolor_bitssize
;
356 i64 colortable_size
= 0;
361 bi_fgcolor
= de_malloc(c
, sizeof(struct fmtutil_macbitmap_info
));
362 bi_mask
= de_malloc(c
, sizeof(struct fmtutil_macbitmap_info
));
363 bi_bw
= de_malloc(c
, sizeof(struct fmtutil_macbitmap_info
));
364 fi
= de_finfo_create(c
);
366 de_dbg(c
, "[color pixmap header]");
368 fmtutil_macbitmap_read_baseaddr(c
, c
->infile
, bi_fgcolor
, pos
);
370 fmtutil_macbitmap_read_rowbytes_and_bounds(c
, c
->infile
, bi_fgcolor
, pos
);
371 if(!bi_fgcolor
->pixmap_flag
) goto done
;
373 fmtutil_macbitmap_read_pixmap_only_fields(c
, c
->infile
, bi_fgcolor
, pos
);
375 de_dbg_indent(c
, -1);
377 de_dbg(c
, "[mask bitmap header]");
379 fmtutil_macbitmap_read_baseaddr(c
, c
->infile
, bi_mask
, pos
);
381 fmtutil_macbitmap_read_rowbytes_and_bounds(c
, c
->infile
, bi_mask
, pos
);
383 de_dbg_indent(c
, -1);
385 de_dbg(c
, "[b/w bitmap header]");
387 fmtutil_macbitmap_read_baseaddr(c
, c
->infile
, bi_bw
, pos
);
389 fmtutil_macbitmap_read_rowbytes_and_bounds(c
, c
->infile
, bi_bw
, pos
);
391 de_dbg_indent(c
, -1);
393 pos
+= 4; // "icon data"
395 // This is said to be optional, but I don't know how to tell if it exists.
396 if(bi_bw
->rowbytes
&& bi_bw
->height
&& bi_bw
->npwidth
) {
400 if(!de_good_image_dimensions_noerr(c
, bi_fgcolor
->npwidth
, bi_fgcolor
->height
)) goto done
;
401 if(!de_good_image_dimensions_noerr(c
, bi_mask
->npwidth
, bi_mask
->height
)) goto done
;
402 if(bw_exists
&& !de_good_image_dimensions_noerr(c
, bi_bw
->npwidth
, bi_bw
->height
)) goto done
;
404 if(bi_fgcolor
->pixeltype
!=0) goto done
;
405 if(bi_fgcolor
->pixelsize
!=bi_fgcolor
->cmpsize
) goto done
;
406 if(bi_fgcolor
->cmpcount
!=1) goto done
;
407 if(bi_fgcolor
->pixelsize
!=1 && bi_fgcolor
->pixelsize
!=2 &&
408 bi_fgcolor
->pixelsize
!=4 && bi_fgcolor
->pixelsize
!=8)
413 mask_bitssize
= bi_mask
->rowbytes
* bi_mask
->height
;
414 if(bw_exists
) bw_bitssize
= bi_bw
->rowbytes
* bi_bw
->height
;
415 fgcolor_bitssize
= bi_fgcolor
->rowbytes
* bi_fgcolor
->height
;
417 if(pos
+mask_bitssize
> dpos
+dlen
) goto done
;
418 de_dbg(c
, "mask bitmap at %"I64_FMT
", len=%"I64_FMT
, pos
, mask_bitssize
);
419 img_mask
= de_bitmap_create2(c
, bi_mask
->npwidth
, bi_mask
->pdwidth
, bi_mask
->height
, 1);
420 de_convert_image_bilevel(c
->infile
, pos
, bi_mask
->rowbytes
, img_mask
, 0);
421 pos
+= mask_bitssize
;
424 if(pos
+bw_bitssize
> dpos
+dlen
) goto done
;
425 de_dbg(c
, "bw bitmap at %"I64_FMT
", len=%"I64_FMT
, pos
, bw_bitssize
);
426 img_bw
= de_bitmap_create2(c
, bi_bw
->npwidth
, bi_bw
->pdwidth
, bi_bw
->height
, 2);
427 de_convert_image_bilevel(c
->infile
, pos
, bi_bw
->rowbytes
, img_bw
,
428 DE_CVTF_WHITEISZERO
);
429 de_bitmap_apply_mask(img_bw
, img_mask
, 0);
430 set_resource_filename(c
, d
, fi
, rti
, rii
, "cicn_bw");
431 de_bitmap_write_to_file_finfo(img_bw
, fi
, DE_CREATEFLAG_OPT_IMAGE
);
435 de_dbg(c
, "[assuming there is no bw bitmap]");
438 if(!fmtutil_macbitmap_read_colortable(c
, c
->infile
, bi_fgcolor
,
439 pos
, &colortable_size
))
443 pos
+= colortable_size
;
445 if(pos
+fgcolor_bitssize
> dpos
+dlen
) goto done
;
446 de_dbg(c
, "color bitmap at %"I64_FMT
", len=%"I64_FMT
, pos
, fgcolor_bitssize
);
447 img_fgcolor
= de_bitmap_create2(c
, bi_fgcolor
->npwidth
, bi_fgcolor
->pdwidth
, bi_fgcolor
->height
, 4);
448 de_convert_image_paletted(c
->infile
, pos
, bi_fgcolor
->pixelsize
, bi_fgcolor
->rowbytes
,
449 bi_fgcolor
->pal
, img_fgcolor
, 0);
450 de_bitmap_apply_mask(img_fgcolor
, img_mask
, 0);
451 if(bi_fgcolor
->hdpi
>=1.0 && bi_fgcolor
->vdpi
>=1.0) {
452 fi
->density
.code
= DE_DENSITY_DPI
;
453 fi
->density
.xdens
= bi_fgcolor
->hdpi
;
454 fi
->density
.ydens
= bi_fgcolor
->vdpi
;
456 set_resource_filename(c
, d
, fi
, rti
, rii
, NULL
);
457 de_bitmap_write_to_file_finfo(img_fgcolor
, fi
, DE_CREATEFLAG_OPT_IMAGE
);
458 //pos += fgcolor_bitssize;
463 // TODO: There are a small but significant number of 'cicn' resources that
464 // appear to use a completely different format than the one I know about.
465 // (Or it could be some sort of systematic corruption.)
466 de_err(c
, "Failed to parse 'cicn' icon resource at %"I64_FMT
, dpos
);
468 de_bitmap_destroy(img_fgcolor
);
469 de_bitmap_destroy(img_mask
);
470 de_bitmap_destroy(img_bw
);
471 de_free(c
, bi_fgcolor
);
474 de_finfo_destroy(c
, fi
);
477 static int looks_like_pict(deark
*c
, lctx
*d
, struct rsrcinstanceinfo
*rii
,
480 if(len
>=12 && !dbuf_memcmp(c
->infile
, pos
+10, "\x11\x01", 2)) {
483 if(len
>=16 && !dbuf_memcmp(c
->infile
, pos
+10, "\x00\x11\x02\xff\x0c\x00", 6)) {
489 static void extract_raw_rsrc(deark
*c
, lctx
*d
, struct rsrctypeinfo
*rti
,
490 struct rsrcinstanceinfo
*rii
, i64 dpos
, i64 dlen
)
493 de_ucstring
*s
= NULL
;
495 fi
= de_finfo_create(c
);
496 s
= ucstring_create(c
);
498 ucstring_append_sz(s
, rti
->fcc
.id_sanitized_sz
, DE_ENCODING_LATIN1
);
499 ucstring_strip_trailing_spaces(s
);
500 if(rii
->attribs
&0x01) {
501 ucstring_append_sz(s
, ".cmpr", DE_ENCODING_LATIN1
);
504 ucstring_append_sz(s
, ".bin", DE_ENCODING_LATIN1
);
506 de_finfo_set_name_from_ucstring(c
, fi
, s
, 0);
507 dbuf_create_file_from_slice(c
->infile
, dpos
, dlen
, NULL
, fi
, 0x0);
509 de_finfo_destroy(c
, fi
);
513 static void do_resource_data(deark
*c
, lctx
*d
, struct rsrctypeinfo
*rti
,
514 struct rsrcinstanceinfo
*rii
)
517 const char *ext
= "bin";
522 de_dbg(c
, "resource data at %d", (int)rii
->data_offset
);
524 dlen
= de_getu32be(rii
->data_offset
);
525 dpos
= rii
->data_offset
+4;
526 de_dbg(c
, "dpos: %d, dlen: %d", (int)dpos
, (int)dlen
);
527 if(dpos
+dlen
> c
->infile
->len
) goto done
;
530 extract_raw_rsrc(c
, d
, rti
, rii
, dpos
, dlen
);
534 if(rii
->attribs
&0x01) {
535 ; // Compressed. Don't know how to handle this.
537 else if(rti
->fcc
.id
==CODE_PICT
&& looks_like_pict(c
, d
, rii
, dpos
, dlen
)) {
542 else if(rti
->fcc
.id
==CODE_icns
) {
546 else if(rti
->fcc
.id
==CODE_moov
) {
550 else if(rti
->fcc
.id
==CODE_ANPA
&& rii
->id
==10000) {
551 de_dbg(c
, "IPTC data at %"I64_FMT
, dpos
);
553 fmtutil_handle_iptc(c
, c
->infile
, dpos
, dlen
, 0x0);
554 de_dbg_indent(c
, -1);
557 else if(rti
->is_icns_type
) {
560 de_dbg(c
, "[icns resource]");
561 icns_dbuf
= get_icns_stream(c
, d
, rii
->id
);
562 dbuf_write(icns_dbuf
, rti
->fcc
.bytes
, 4);
563 dbuf_writeu32be(icns_dbuf
, 8+dlen
);
564 dbuf_copy(c
->infile
, dpos
, dlen
, icns_dbuf
);
567 else if(rti
->is_psrc_type
) {
568 do_psrc_resource(c
, d
, rti
, rii
, dpos
, dlen
);
571 else if(rti
->fcc
.id
==CODE_CURS
|| rti
->fcc
.id
==CODE_crsr
) {
572 do_crsr_CURS_resource(c
, d
, rti
, rii
, dpos
, dlen
);
575 else if(rti
->fcc
.id
==CODE_cicn
) {
576 do_cicn_resource(c
, d
, rti
, rii
, dpos
, dlen
);
579 else if(rti
->fcc
.id
==CODE_SICN
) {
580 do_SICN_resource(c
, d
, rti
, rii
, dpos
, dlen
);
588 fi
= de_finfo_create(c
);
589 set_resource_filename(c
, d
, fi
, rti
, rii
, ext
);
590 outf
= dbuf_create_output_file(c
, NULL
, fi
, 0);
591 de_finfo_destroy(c
, fi
);
593 dbuf_write_zeroes(outf
, 512);
595 dbuf_copy(c
->infile
, dpos
, dlen
, outf
);
600 if(!handled
&& c
->debug_level
>=2) {
601 de_dbg_hexdump(c
, c
->infile
, dpos
, dlen
, 256, NULL
, 0x1);
605 de_dbg_indent(c
, -1);
608 // Sets rii->name_raw_len.
610 static void do_resource_name(deark
*c
, lctx
*d
, struct rsrcinstanceinfo
*rii
)
614 nlen
= (i64
)de_getbyte(rii
->name_offset
);
615 rii
->name_raw_len
= 1+nlen
;
616 rii
->name
= ucstring_create(c
);
617 dbuf_read_to_ucstring(c
->infile
, rii
->name_offset
+1, nlen
, rii
->name
, 0, DE_ENCODING_MACROMAN
);
618 de_dbg(c
, "name: \"%s\"", ucstring_getpsz_d(rii
->name
));
621 static void do_resource_record(deark
*c
, lctx
*d
, struct rsrctypeinfo
*rti
,
627 struct rsrcinstanceinfo rii
;
629 de_zeromem(&rii
, sizeof(struct rsrcinstanceinfo
));
631 rii
.id
= (int)de_geti16be_p(&pos
);
632 de_dbg(c
, "id: %d", rii
.id
);
633 nameOffset_rel
= de_getu16be_p(&pos
);
634 if(nameOffset_rel
!=0xffff) {
636 de_dbg(c
, "nameOffset: (%d+)%d", (int)d
->nameListOffset_abs
, (int)nameOffset_rel
);
637 rii
.name_offset
= d
->nameListOffset_abs
+nameOffset_rel
;
638 do_resource_name(c
, d
, &rii
);
640 rii
.attribs
= de_getbyte_p(&pos
);
642 de_dbg(c
, "attributes: 0x%02x", (unsigned int)rii
.attribs
);
645 de_ucstring
*flags_str
;
646 flags_str
= ucstring_create(c
);
647 if(rii
.attribs
& 0x40) ucstring_append_flags_item(flags_str
, "system heap");
648 if(rii
.attribs
& 0x20) ucstring_append_flags_item(flags_str
, "purgeable");
649 if(rii
.attribs
& 0x10) ucstring_append_flags_item(flags_str
, "locked");
650 if(rii
.attribs
& 0x08) ucstring_append_flags_item(flags_str
, "read-only");
651 if(rii
.attribs
& 0x04) ucstring_append_flags_item(flags_str
, "preload");
652 if(rii
.attribs
& 0x01) ucstring_append_flags_item(flags_str
, "compressed");
653 de_dbg(c
, "attributes: 0x%02x (%s)", (unsigned int)rii
.attribs
, ucstring_getpsz_d(flags_str
));
654 ucstring_destroy(flags_str
);
657 dataOffset_rel
= dbuf_getint_ext(c
->infile
, pos
, 3, 0, 0);
658 rii
.data_offset
= d
->data_offs
+ dataOffset_rel
;
660 de_dbg(c
, "dataOffset: (%d+)%d", (int)d
->data_offs
, (int)dataOffset_rel
);
661 do_resource_data(c
, d
, rti
, &rii
);
663 if(rii
.name
) ucstring_destroy(rii
.name
);
666 static void do_resource_list(deark
*c
, lctx
*d
, struct rsrctypeinfo
*rti
,
667 i64 rsrc_list_offs
, UI count
)
670 i64 pos
= rsrc_list_offs
;
672 de_dbg(c
, "resource list at %d", (int)rsrc_list_offs
);
674 for(idx
=0; idx
<count
; idx
++) {
675 de_dbg(c
, "resource record[%u] at %"I64_FMT
" (type '%s')", idx
, pos
,
678 do_resource_record(c
, d
, rti
, pos
, idx
);
679 de_dbg_indent(c
, -1);
682 de_dbg_indent(c
, -1);
685 static void do_type_item(deark
*c
, lctx
*d
, i64 type_list_offs
,
691 struct rsrctypeinfo rti
;
693 de_zeromem(&rti
, sizeof(struct rsrctypeinfo
));
694 dbuf_read_fourcc(c
->infile
, pos
, &rti
.fcc
, 4, 0x0);
695 de_dbg(c
, "resource type: '%s'", rti
.fcc
.id_dbgstr
);
697 rti
.is_icns_type
= is_icns_icon(c
, d
, &rti
);
699 // TODO: What other signatures should we include?
700 if(rti
.fcc
.id
==CODE_8BIM
|| rti
.fcc
.id
==CODE_MeSa
) {
701 rti
.is_psrc_type
= 1;
704 count
= (UI
)(1+de_getu16be_p(&pos
));
705 de_dbg(c
, "resource count: %u", count
);
706 list_offs_rel
= de_getu16be_p(&pos
);
707 de_dbg(c
, "list offset: (%d+)%d", (int)type_list_offs
, (int)list_offs_rel
);
709 do_resource_list(c
, d
, &rti
, type_list_offs
+list_offs_rel
, count
);
712 static void do_type_list(deark
*c
, lctx
*d
)
714 i64 pos1
= d
->typeListOffset_abs
;
720 de_dbg(c
, "type list at %d", (int)pos1
);
722 type_count_raw
= de_getu16be_p(&pos
);
723 type_count
= (type_count_raw
==0xffff)?0:(type_count_raw
+1);
724 de_dbg(c
, "type count: %d", (int)type_count
);
726 for(k
=0; k
<type_count
; k
++) {
727 de_dbg(c
, "type record[%d] at %d", (int)k
, (int)pos
);
729 do_type_item(c
, d
, pos1
, k
, pos
);
731 de_dbg_indent(c
, -1);
733 de_dbg_indent(c
, -1);
736 static void do_map(deark
*c
, lctx
*d
, i64 map_offs
, i64 map_size
)
739 i64 typeListOffset_rel
, nameListOffset_rel
;
742 n
= de_getu32be(map_offs
+4);
744 de_err(c
, "%sResource map section not found, expected to be at %"I64_FMT
,
745 d
->errmsgprefix
, map_offs
);
749 de_dbg(c
, "resource map section at %d", (int)map_offs
);
752 pos
+= 16; // copy of header
753 pos
+= 4; // nextResourceMap
755 pos
+= 2; // attributes
757 typeListOffset_rel
= de_getu16be_p(&pos
);
758 de_dbg(c
, "type list offset: (%d+)%d", (int)map_offs
,
759 (int)typeListOffset_rel
);
760 d
->typeListOffset_abs
= map_offs
+ typeListOffset_rel
;
762 nameListOffset_rel
= de_getu16be_p(&pos
);
763 de_dbg(c
, "name list offset: (%d+)%d", (int)map_offs
,
764 (int)nameListOffset_rel
);
765 d
->nameListOffset_abs
= map_offs
+ nameListOffset_rel
;
767 if(typeListOffset_rel
<28) {
768 de_err(c
, "%sInvalid typeListOffset", d
->errmsgprefix
);
778 static void de_run_macrsrc(deark
*c
, de_module_params
*mparams
)
783 d
= de_malloc(c
, sizeof(lctx
));
784 if(c
->module_disposition
==DE_MODDISP_INTERNAL
) {
785 d
->errmsgprefix
= "[Resource format] ";
788 d
->errmsgprefix
= "";
791 if(de_get_ext_option(c
, "macrsrc:extractraw")) {
795 if(c
->infile
->len
<16) {
796 de_err(c
, "%sFile too small to be a valid Resource file", d
->errmsgprefix
);
801 d
->data_offs
= de_getu32be_p(&pos
);
802 d
->map_offs
= de_getu32be_p(&pos
);
803 d
->data_size
= de_getu32be_p(&pos
);
804 d
->map_size
= de_getu32be_p(&pos
);
805 de_dbg(c
, "data: pos=%"I64_FMT
", len=%"I64_FMT
, d
->data_offs
, d
->data_size
);
806 de_dbg(c
, "map: pos=%"I64_FMT
", len=%"I64_FMT
, d
->map_offs
, d
->map_size
);
807 do_map(c
, d
, d
->map_offs
, d
->map_size
);
810 finalize_icns_streams(c
, d
);
811 finalize_psrc_stream(c
, d
);
815 static int de_identify_macrsrc(deark
*c
)
821 if(de_getu32be(0)!=256) return 0;
824 n
[k
] = de_getu32be_direct(&b
[4*k
]);
826 if(n
[0]+n
[2]>n
[1]) return 0; // data can't go past map start
827 if(n
[3]<30) return 0; // minimum map len
828 if(n
[1]+n
[3]>c
->infile
->len
) return 0; // map can't go past eof
829 // map should start with a copy of the header
830 if(dbuf_memcmp(c
->infile
, n
[1], (const void*)b
, 16)) return 0;
831 if(n
[1]+n
[3]==c
->infile
->len
) return 100;
835 static void de_help_macrsrc(deark
*c
)
837 de_msg(c
, "-opt macrsrc:extractraw : Extract all resources to files");
840 void de_module_macrsrc(deark
*c
, struct deark_module_info
*mi
)
843 mi
->desc
= "Macintosh Resource Manager";
844 mi
->run_fn
= de_run_macrsrc
;
845 mi
->identify_fn
= de_identify_macrsrc
;
846 mi
->help_fn
= de_help_macrsrc
;