Minor refactoring of the IFF and box-format parsers
[deark.git] / modules / macrsrc.c
blob449b323825d0bfd7f57156cbfc2a375fda051a14
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 {
24 int id;
25 dbuf *tmpf;
28 typedef struct localctx_struct {
29 u8 extract_raw;
30 i64 data_offs, map_offs;
31 i64 data_size, map_size;
33 i64 typeListOffset_abs;
34 i64 nameListOffset_abs;
35 dbuf *psrc_stream;
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];
41 } lctx;
43 struct rsrctypeinfo {
44 struct de_fourcc fcc;
45 int is_icns_type;
46 int is_psrc_type;
49 struct rsrcinstanceinfo {
50 int id;
51 UI idx;
52 u8 attribs;
53 u8 has_name;
54 i64 data_offset;
55 i64 name_offset;
56 i64 name_raw_len;
57 de_ucstring *name;
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);
83 if(token) {
84 ucstring_append_sz(tmpname, token, DE_ENCODING_LATIN1);
86 else {
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.
97 switch(rti->fcc.id) {
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
101 return 1;
103 return 0;
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
109 // do.
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)
114 size_t entry = 0;
115 size_t i;
117 for(i=0; i<d->num_icns_streams_used; i++) {
118 if(d->icns_strm[i].id == id) {
119 entry = i;
120 goto found;
124 if(d->num_icns_streams_used >= MAX_ICNS_STREAMS) {
125 entry = MAX_ICNS_STREAMS - 1; // stream of last resort
127 else {
128 entry = d->num_icns_streams_used++;
129 d->icns_strm[entry].id = id;
132 found:
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
140 // in this file.
141 static void finalize_icns_stream(deark *c, lctx *d, dbuf *tmpf)
143 dbuf *outf = NULL;
145 if(!tmpf) return;
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);
150 dbuf_close(outf);
153 static void finalize_icns_streams(deark *c, lctx *d)
155 size_t i;
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);
196 if(rii->has_name) {
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
202 else {
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);
207 if(dlen%2) {
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;
223 de_finfo *fi = NULL;
224 i64 pos = pos1;
225 i64 pixmap_offs = 0;
226 i64 pixdata_offs = 0;
227 i64 n;
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);
234 if(is_crsr) {
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");;
240 goto done;
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);
252 pos += 32;
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);
257 pos += 32;
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);
262 fi->has_hotspot = 1;
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);
274 pos += 4;
275 fmtutil_macbitmap_read_rowbytes_and_bounds(c, c->infile, bi, pos);
276 pos += 10;
277 fmtutil_macbitmap_read_pixmap_only_fields(c, c->infile, bi, pos);
278 pos += 36;
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;
288 else {
289 pos = pos1 + pixdata_offs + bi->rowbytes * bi->height;
291 if(!fmtutil_macbitmap_read_colortable(c, c->infile, bi, pos, &colortable_size)) {
292 goto done;
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);
312 done:
313 de_free(c, bi);
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)
324 de_finfo *fi = NULL;
325 i64 numicons;
326 i64 i;
328 numicons = len/32;
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;
351 de_finfo *fi = NULL;
352 i64 fgcolor_bitssize;
353 i64 mask_bitssize;
354 i64 bw_bitssize = 0;
355 int bw_exists = 0;
356 i64 colortable_size = 0;
357 i64 pos = dpos;
358 int needmsg = 1;
359 int ok = 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]");
367 de_dbg_indent(c, 1);
368 fmtutil_macbitmap_read_baseaddr(c, c->infile, bi_fgcolor, pos);
369 pos += 4;
370 fmtutil_macbitmap_read_rowbytes_and_bounds(c, c->infile, bi_fgcolor, pos);
371 if(!bi_fgcolor->pixmap_flag) goto done;
372 pos += 10;
373 fmtutil_macbitmap_read_pixmap_only_fields(c, c->infile, bi_fgcolor, pos);
374 pos += 36;
375 de_dbg_indent(c, -1);
377 de_dbg(c, "[mask bitmap header]");
378 de_dbg_indent(c, 1);
379 fmtutil_macbitmap_read_baseaddr(c, c->infile, bi_mask, pos);
380 pos += 4;
381 fmtutil_macbitmap_read_rowbytes_and_bounds(c, c->infile, bi_mask, pos);
382 pos += 10;
383 de_dbg_indent(c, -1);
385 de_dbg(c, "[b/w bitmap header]");
386 de_dbg_indent(c, 1);
387 fmtutil_macbitmap_read_baseaddr(c, c->infile, bi_bw, pos);
388 pos += 4;
389 fmtutil_macbitmap_read_rowbytes_and_bounds(c, c->infile, bi_bw, pos);
390 pos += 10;
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) {
397 bw_exists = 1;
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)
410 goto done;
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;
423 if(bw_exists) {
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);
432 pos += bw_bitssize;
434 else {
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))
441 goto done;
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;
459 ok = 1;
461 done:
462 if(!ok && needmsg) {
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);
472 de_free(c, bi_mask);
473 de_free(c, bi_bw);
474 de_finfo_destroy(c, fi);
477 static int looks_like_pict(deark *c, lctx *d, struct rsrcinstanceinfo *rii,
478 i64 pos, i64 len)
480 if(len>=12 && !dbuf_memcmp(c->infile, pos+10, "\x11\x01", 2)) {
481 return 1; // PICTv1
483 if(len>=16 && !dbuf_memcmp(c->infile, pos+10, "\x00\x11\x02\xff\x0c\x00", 6)) {
484 return 1; // PICTv2
486 return 0;
489 static void extract_raw_rsrc(deark *c, lctx *d, struct rsrctypeinfo *rti,
490 struct rsrcinstanceinfo *rii, i64 dpos, i64 dlen)
492 de_finfo *fi = NULL;
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);
503 else {
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);
510 ucstring_destroy(s);
513 static void do_resource_data(deark *c, lctx *d, struct rsrctypeinfo *rti,
514 struct rsrcinstanceinfo *rii)
516 i64 dpos, dlen;
517 const char *ext = "bin";
518 int extr_flag = 0;
519 int is_pict = 0;
520 int handled = 0;
522 de_dbg(c, "resource data at %d", (int)rii->data_offset);
523 de_dbg_indent(c, 1);
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;
529 if(d->extract_raw) {
530 extract_raw_rsrc(c, d, rti, rii, dpos, dlen);
531 goto done;
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)) {
538 ext = "pict";
539 extr_flag = 1;
540 is_pict = 1;
542 else if(rti->fcc.id==CODE_icns) {
543 ext = "icns";
544 extr_flag = 1;
546 else if(rti->fcc.id==CODE_moov) {
547 ext = "mov";
548 extr_flag = 1;
550 else if(rti->fcc.id==CODE_ANPA && rii->id==10000) {
551 de_dbg(c, "IPTC data at %"I64_FMT, dpos);
552 de_dbg_indent(c, 1);
553 fmtutil_handle_iptc(c, c->infile, dpos, dlen, 0x0);
554 de_dbg_indent(c, -1);
555 handled = 1;
557 else if(rti->is_icns_type) {
558 dbuf *icns_dbuf;
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);
565 handled = 1;
567 else if(rti->is_psrc_type) {
568 do_psrc_resource(c, d, rti, rii, dpos, dlen);
569 handled = 1;
571 else if(rti->fcc.id==CODE_CURS || rti->fcc.id==CODE_crsr) {
572 do_crsr_CURS_resource(c, d, rti, rii, dpos, dlen);
573 handled = 1;
575 else if(rti->fcc.id==CODE_cicn) {
576 do_cicn_resource(c, d, rti, rii, dpos, dlen);
577 handled = 1;
579 else if(rti->fcc.id==CODE_SICN) {
580 do_SICN_resource(c, d, rti, rii, dpos, dlen);
581 handled = 1;
584 if(extr_flag) {
585 dbuf *outf = NULL;
586 de_finfo *fi = NULL;
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);
592 if(is_pict) {
593 dbuf_write_zeroes(outf, 512);
595 dbuf_copy(c->infile, dpos, dlen, outf);
596 dbuf_close(outf);
597 handled = 1;
600 if(!handled && c->debug_level>=2) {
601 de_dbg_hexdump(c, c->infile, dpos, dlen, 256, NULL, 0x1);
604 done:
605 de_dbg_indent(c, -1);
608 // Sets rii->name_raw_len.
609 // Sets rii->name.
610 static void do_resource_name(deark *c, lctx *d, struct rsrcinstanceinfo *rii)
612 i64 nlen;
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,
622 i64 pos1, UI idx)
624 i64 dataOffset_rel;
625 i64 nameOffset_rel;
626 i64 pos = pos1;
627 struct rsrcinstanceinfo rii;
629 de_zeromem(&rii, sizeof(struct rsrcinstanceinfo));
630 rii.idx = idx;
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) {
635 rii.has_name = 1;
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);
641 if(rii.attribs==0) {
642 de_dbg(c, "attributes: 0x%02x", (unsigned int)rii.attribs);
644 else {
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;
659 pos += 3;
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)
669 UI idx;
670 i64 pos = rsrc_list_offs;
672 de_dbg(c, "resource list at %d", (int)rsrc_list_offs);
673 de_dbg_indent(c, 1);
674 for(idx=0; idx<count; idx++) {
675 de_dbg(c, "resource record[%u] at %"I64_FMT" (type '%s')", idx, pos,
676 rti->fcc.id_dbgstr);
677 de_dbg_indent(c, 1);
678 do_resource_record(c, d, rti, pos, idx);
679 de_dbg_indent(c, -1);
680 pos += 12;
682 de_dbg_indent(c, -1);
685 static void do_type_item(deark *c, lctx *d, i64 type_list_offs,
686 i64 idx, i64 pos1)
688 i64 pos = pos1;
689 UI count;
690 i64 list_offs_rel;
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);
696 pos += 4;
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;
715 i64 pos = pos1;
716 i64 type_count_raw;
717 i64 type_count;
718 i64 k;
720 de_dbg(c, "type list at %d", (int)pos1);
721 de_dbg_indent(c, 1);
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);
728 de_dbg_indent(c, 1);
729 do_type_item(c, d, pos1, k, pos);
730 pos += 8;
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)
738 i64 pos = map_offs;
739 i64 typeListOffset_rel, nameListOffset_rel;
740 i64 n;
742 n = de_getu32be(map_offs+4);
743 if(n!=map_offs) {
744 de_err(c, "%sResource map section not found, expected to be at %"I64_FMT,
745 d->errmsgprefix, map_offs);
746 return;
749 de_dbg(c, "resource map section at %d", (int)map_offs);
750 de_dbg_indent(c, 1);
752 pos += 16; // copy of header
753 pos += 4; // nextResourceMap
754 pos += 2; // fileRef
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);
769 goto done;
772 do_type_list(c, d);
774 done:
775 de_dbg_indent(c, 1);
778 static void de_run_macrsrc(deark *c, de_module_params *mparams)
780 lctx *d = NULL;
781 i64 pos;
783 d = de_malloc(c, sizeof(lctx));
784 if(c->module_disposition==DE_MODDISP_INTERNAL) {
785 d->errmsgprefix = "[Resource format] ";
787 else {
788 d->errmsgprefix = "";
791 if(de_get_ext_option(c, "macrsrc:extractraw")) {
792 d->extract_raw = 1;
795 if(c->infile->len<16) {
796 de_err(c, "%sFile too small to be a valid Resource file", d->errmsgprefix);
797 goto done;
800 pos = 0;
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);
809 done:
810 finalize_icns_streams(c, d);
811 finalize_psrc_stream(c, d);
812 de_free(c, d);
815 static int de_identify_macrsrc(deark *c)
817 u8 b[16];
818 i64 n[4];
819 size_t k;
821 if(de_getu32be(0)!=256) return 0;
822 de_read(b, 0, 16);
823 for(k=0; k<4; k++) {
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;
832 return 75;
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)
842 mi->id = "macrsrc";
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;