1 // This file is part of Deark.
2 // Copyright (C) 2018 Jason Summers
3 // See the file COPYING for terms of use.
7 #include <deark-config.h>
8 #include <deark-private.h>
9 #include <deark-fmtutil.h>
10 DE_DECLARE_MODULE(de_module_macbinary
);
12 typedef struct localctx_struct
{
20 struct de_stringreaderdata
*filename_srd
;
21 struct de_timestamp create_time
;
22 struct de_timestamp mod_time
;
27 u8 extract_error_flag
;
33 struct fork_info fki_data
;
34 struct fork_info fki_rsrc
;
37 static const char *fork_name(int is_rsrc
, int capitalize
)
40 return capitalize
?"Resource":"resource";
42 return capitalize
?"Data":"data";
45 static void do_header(deark
*c
, lctx
*d
, struct de_advfile
*advf
)
52 u32 crc_reported
, crc_calc
;
53 struct de_fourcc type4cc
;
54 struct de_fourcc creator4cc
;
55 char timestamp_buf
[64];
57 d
->oldver
= de_getbyte_p(&pos
);
58 de_dbg(c
, "original version: %u", (unsigned int)d
->oldver
);
60 de_warn(c
, "Unsupported MacBinary version");
64 d
->extver
= de_getbyte(122);
65 de_dbg(c
, "extended version: %u", (unsigned int)d
->extver
);
66 if(d
->extver
==129 || d
->extver
==130) {
69 if(d
->extver
>= 129) {
70 d
->extver_minneeded
= de_getbyte(123);
71 de_dbg(c
, "extended version, min needed: %u", (unsigned int)d
->extver_minneeded
);
74 namelen
= (i64
)de_getbyte_p(&pos
);
75 if(namelen
>=1 && namelen
<=63) {
76 // Required to be 1-63 by MacBinary II spec.
77 // Original spec has no written requirements.
78 // Not supposed to be NUL terminated, but such files exist.
79 d
->filename_srd
= dbuf_read_string(c
->infile
, pos
, namelen
, namelen
,
80 DE_CONVFLAG_STOP_AT_NUL
, DE_ENCODING_MACROMAN
);
81 de_dbg(c
, "filename: \"%s\"", ucstring_getpsz(d
->filename_srd
->str
));
84 de_warn(c
, "Bad MacBinary filename length (%d)", (int)namelen
);
88 de_dbg(c
, "finder info:");
91 dbuf_read_fourcc(c
->infile
, pos
, &type4cc
, 4, 0x0);
92 de_dbg(c
, "type: '%s'", type4cc
.id_dbgstr
);
93 de_memcpy(advf
->typecode
, type4cc
.bytes
, 4);
94 advf
->has_typecode
= 1;
96 dbuf_read_fourcc(c
->infile
, pos
, &creator4cc
, 4, 0x0);
97 de_dbg(c
, "creator: '%s'", creator4cc
.id_dbgstr
);
98 de_memcpy(advf
->creatorcode
, creator4cc
.bytes
, 4);
99 advf
->has_creatorcode
= 1;
102 advf
->has_finderflags
= 1;
106 fflags_hibyte
= de_getbyte_p(&pos
);
107 de_dbg(c
, "finder flags (high byte): 0x%02x__", (unsigned int)fflags_hibyte
);
109 advf
->finderflags
= (u16
)fflags_hibyte
<< 8;
112 advf
->finderflags
= (u16
)de_getu16be_p(&pos
);
113 de_dbg(c
, "finder flags: 0x%04x", (unsigned int)advf
->finderflags
);
116 n
= de_geti16be_p(&pos
);
117 n2
= de_geti16be_p(&pos
);
118 de_dbg(c
, "position in window: %d,%d", (int)n2
, (int)n
);
120 n
= de_getu16be_p(&pos
);
121 de_dbg(c
, "window/folder id: %d", (int)n
);
122 de_dbg_indent(c
, -1);
124 b
= de_getbyte_p(&pos
);
125 de_dbg(c
, "protected: 0x%02x", (unsigned int)b
);
129 d
->dflen
= de_getu32be_p(&pos
);
130 de_dbg(c
, "data fork len: %u", (unsigned int)d
->dflen
);
131 d
->rflen
= de_getu32be_p(&pos
);
132 de_dbg(c
, "resource fork len: %u", (unsigned int)d
->rflen
);
134 n
= de_getu32be_p(&pos
);
136 d
->create_time
.is_valid
= 0;
137 de_strlcpy(timestamp_buf
, "unknown", sizeof(timestamp_buf
));
140 de_mac_time_to_timestamp(n
, &d
->create_time
);
141 d
->create_time
.tzcode
= DE_TZCODE_LOCAL
;
142 de_timestamp_to_string(&d
->create_time
, timestamp_buf
, sizeof(timestamp_buf
), 0);
144 de_dbg(c
, "create date: %"I64_FMT
" (%s)", n
, timestamp_buf
);
146 mod_time_raw
= de_getu32be_p(&pos
);
147 if(mod_time_raw
==0) {
148 d
->mod_time
.is_valid
= 0;
149 de_strlcpy(timestamp_buf
, "unknown", sizeof(timestamp_buf
));
152 de_mac_time_to_timestamp(mod_time_raw
, &d
->mod_time
);
153 d
->mod_time
.tzcode
= DE_TZCODE_LOCAL
;
154 de_timestamp_to_string(&d
->mod_time
, timestamp_buf
, sizeof(timestamp_buf
), 0);
156 de_dbg(c
, "mod date: %"I64_FMT
" (%s)", mod_time_raw
, timestamp_buf
);
158 pos
+= 2; // length of Get Info comment
163 fflags_lobyte
= de_getbyte(pos
);
164 de_dbg(c
, "finder flags (low byte): 0x__%02x", (unsigned int)fflags_lobyte
);
165 advf
->finderflags
|= (u16
)fflags_lobyte
;
170 pos
+= 4; // unpacked total length
173 n
= de_getu16be(pos
);
174 de_dbg(c
, "length of secondary header: %u", (unsigned int)n
);
178 pos
+= 1; // version number, already read
179 pos
+= 1; // version number, already read
181 crc_reported
= (u32
)de_getu16be_p(&pos
);
182 if(d
->is_v23
|| crc_reported
!=0) {
183 struct de_crcobj
*crco
;
185 de_dbg(c
, "crc of header (reported%s): 0x%04x",
186 (d
->is_v23
)?"":", hypothetical", (unsigned int)crc_reported
);
187 crco
= de_crcobj_create(c
, DE_CRCOBJ_CRC16_XMODEM
);
188 de_crcobj_addslice(crco
, c
->infile
, 0, 124);
189 crc_calc
= de_crcobj_getval(crco
);
190 de_crcobj_destroy(crco
);
191 de_dbg(c
, "crc of header (calculated): 0x%04x", (unsigned int)crc_calc
);
193 if(d
->is_v23
&& crc_reported
!=0 && crc_calc
!=crc_reported
) {
194 de_warn(c
, "MacBinary header CRC check failed");
198 pos
+= 2; // Reserved for computer type and OS ID
203 // If a fork is going to be extracted, call this to set up some things.
204 // Caller must first set advfki->fork_len, among other things.
205 // Sets fki->extract_error_flag if there was a problem that would prevent the
206 // fork from being extracted.
207 static void do_prepare_one_fork(deark
*c
, lctx
*d
, struct de_advfile_forkinfo
*advfki
,
208 struct fork_info
*fki
)
210 de_dbg(c
, "%s fork at %"I64_FMT
", len=%"I64_FMT
, fork_name(fki
->is_rsrc
, 0),
211 fki
->pos
, advfki
->fork_len
);
213 if(fki
->pos
+advfki
->fork_len
>c
->infile
->len
) {
214 de_err(c
, "%s fork at %"I64_FMT
" goes beyond end of file.",
215 fork_name(fki
->is_rsrc
, 1), fki
->pos
);
216 if(fki
->pos
+advfki
->fork_len
> c
->infile
->len
+1024) {
217 fki
->extract_error_flag
= 1;
226 static int my_advfile_cbfn(deark
*c
, struct de_advfile
*advf
,
227 struct de_advfile_cbparams
*afp
)
229 struct extract_ctx
*ectx
= (struct extract_ctx
*)advf
->userdata
;
231 if(afp
->whattodo
== DE_ADVFILE_WRITEMAIN
) {
232 dbuf_copy(c
->infile
, ectx
->fki_data
.pos
, advf
->mainfork
.fork_len
, afp
->outf
);
234 else if(afp
->whattodo
== DE_ADVFILE_WRITERSRC
) {
235 dbuf_copy(c
->infile
, ectx
->fki_rsrc
.pos
, advf
->rsrcfork
.fork_len
, afp
->outf
);
240 static void run_macbinary_internal(deark
*c
, lctx
*d
)
243 struct de_advfile
*advf
= NULL
;
244 struct extract_ctx
*ectx
= NULL
;
246 ectx
= de_malloc(c
, sizeof(struct extract_ctx
));
247 advf
= de_advfile_create(c
);
249 do_header(c
, d
, advf
);
250 if(d
->filename_srd
&& ucstring_isnonempty(d
->filename_srd
->str
)) {
251 ucstring_append_ucstring(advf
->filename
, d
->filename_srd
->str
);
252 advf
->original_filename_flag
= 1;
254 if(d
->filename_srd
) {
255 de_advfile_set_orig_filename(advf
, d
->filename_srd
->sz
,
256 d
->filename_srd
->sz_strlen
);
258 advf
->mainfork
.fi
->timestamp
[DE_TIMESTAMPIDX_MODIFY
] = d
->mod_time
;
259 advf
->mainfork
.fi
->timestamp
[DE_TIMESTAMPIDX_CREATE
] = d
->create_time
;
263 ectx
->fki_data
.pos
= d
->dfpos
;
264 advf
->mainfork
.fork_len
= d
->dflen
;
266 if(d
->extract_files
) {
267 do_prepare_one_fork(c
, d
, &advf
->mainfork
, &ectx
->fki_data
);
268 if(!ectx
->fki_data
.extract_error_flag
) {
269 advf
->mainfork
.fork_exists
= 1;
273 pos
+= de_pad_to_n(d
->dflen
, 128);
279 ectx
->fki_rsrc
.is_rsrc
= 1;
280 ectx
->fki_rsrc
.pos
= d
->rfpos
;
281 advf
->rsrcfork
.fork_len
= d
->rflen
;
283 if(d
->extract_files
) {
284 do_prepare_one_fork(c
, d
, &advf
->rsrcfork
, &ectx
->fki_rsrc
);
285 if(!ectx
->fki_rsrc
.extract_error_flag
) {
286 advf
->rsrcfork
.fork_exists
= 1;
291 if(d
->extract_files
) {
292 advf
->userdata
= (void*)ectx
;
293 advf
->writefork_cbfn
= my_advfile_cbfn
;
294 de_advfile_run(advf
);
297 de_advfile_destroy(advf
);
301 static void de_run_macbinary(deark
*c
, de_module_params
*mparams
)
305 d
= de_malloc(c
, sizeof(lctx
));
307 d
->extract_files
= 1;
308 if(de_havemodcode(c
, mparams
, 'D')) {
309 d
->extract_files
= 0;
312 run_macbinary_internal(c
, d
);
315 mparams
->out_params
.uint1
= (u32
)d
->dfpos
;
316 mparams
->out_params
.uint2
= (u32
)d
->dflen
;
317 mparams
->out_params
.uint3
= (u32
)d
->rfpos
;
318 mparams
->out_params
.uint4
= (u32
)d
->rflen
;
320 if(mparams
->out_params
.fi
) {
321 // If caller created out_params.fi for us, save the mod time to it.
322 mparams
->out_params
.fi
->timestamp
[DE_TIMESTAMPIDX_MODIFY
] = d
->mod_time
;
324 // If caller created .fi->name_other, copy the filename to it.
325 if(d
->filename_srd
&& d
->filename_srd
->str
->len
>0 && mparams
->out_params
.fi
->name_other
) {
326 ucstring_append_ucstring(mparams
->out_params
.fi
->name_other
, d
->filename_srd
->str
);
332 de_destroy_stringreaderdata(c
, d
->filename_srd
);
337 // Detecting MacBinary format is important, but also very difficult.
338 // Note: This must be coordinated with the macpaint detection routine.
339 // It should never set the confidence to 100, because macpaint and
340 // maybe other formats need to be able to have higher confidence.
341 static int de_identify_macbinary(deark
*c
)
346 int is_v23
= 0; // v2 or v3
347 int good_file_len
= 0;
352 i64 min_expected_len
;
353 u32 crc_reported
, crc_calc
;
356 // "old" version number is always 0.
357 b
[0] = de_getbyte(0);
358 if(b
[0]!=0) goto done
;
361 b
[1] = de_getbyte(1);
362 if(b
[1]<1 || b
[1]>63) goto done
;
364 de_read(&b
[2], 2, sizeof(b
)-2);
366 if(b
[2]==0) goto done
; // First filename byte
367 if(b
[74]!=0) goto done
;
368 if(b
[82]!=0) goto done
;
370 // Extended version number
371 if(b
[122]==129 && b
[123]==129) {
375 else if(b
[122]==130 && (b
[123]==129 || b
[123]==130)) {
380 // Some v1 files have garbage in the last 29 bytes of the file,
381 // so we can't assume the extended version number is 0.
383 // Ver.III signature, but possibly used in some files that have earlier
385 has_sig
= !de_memcmp(&b
[102], (const void*)"mBIN", 4);
391 // Check if filename characters are sensible
392 for(k
=0; k
<(int)b
[1]; k
++) {
393 if(b
[2+k
]>0 && b
[2+k
]<32) goto done
;
396 // File type code. Expect ASCII.
398 for(k
=65; k
<=68; k
++) {
399 if(b
[k
]<32 || b
[k
]>127) good_cc
= 0;
402 dflen
= de_getu32be_direct(&b
[83]);
403 rflen
= de_getu32be_direct(&b
[87]);
405 crc_reported
= (u32
)de_getu16be_direct(&b
[124]);
407 // Check the file size.
409 // Resource forks that go beyond the end of file are too common to
411 if(128 + dflen
> c
->infile
->len
) goto done
;
412 if(128 + rflen
+ dflen
> c
->infile
->len
+ 4096) goto done
;
415 min_expected_len
= 128 + de_pad_to_n(dflen
, 128) + rflen
;
418 min_expected_len
= 128 + dflen
;
421 // The file size really should be exactly min_expected_len, or that
422 // number padded to the next multiple of 128. But I'm not bold
423 // enough to require it.
424 if((c
->infile
->len
== min_expected_len
) ||
425 (c
->infile
->len
== de_pad_to_n(min_expected_len
, 128)))
431 // Most MacBinary II specific checks go here
433 if(!de_is_all_zeroes(&b
[102], 14)) {
434 if(!good_file_len
) goto done
;
437 // Secondary header length. We don't support this.
438 n
= de_getu16be_direct(&b
[120]);
442 // Most Original MacBinary format checks go here
444 // An empty file is not illegal, but we need as many checks as possible
445 // that won't be passed by all 0 bytes.
446 if(dflen
==0 && rflen
==0) goto done
;
448 // Unused fields in this version should be all 0, though we'll allow
449 // nonzero values in some cases.
450 if(!de_is_all_zeroes(&b
[99], 25)) {
451 if(!good_file_len
) goto done
;
455 if(crc_reported
!=0 || is_v23
) {
456 struct de_crcobj
*crco
;
458 crco
= de_crcobj_create(c
, DE_CRCOBJ_CRC16_XMODEM
);
459 de_crcobj_addbuf(crco
, b
, 124);
460 crc_calc
= de_crcobj_getval(crco
);
461 de_crcobj_destroy(crco
);
462 if(crc_calc
!=crc_reported
&& is_v23
&& crc_reported
!=0) {
467 if(is_v23
&& good_file_len
&& good_cc
) {
490 c
->detection_data
->is_macbinary
= 1;
495 void de_module_macbinary(deark
*c
, struct deark_module_info
*mi
)
497 mi
->id
= "macbinary";
498 mi
->desc
= "MacBinary";
499 mi
->run_fn
= de_run_macbinary
;
500 mi
->identify_fn
= de_identify_macbinary
;
501 mi
->flags
= DE_MODFLAG_SHAREDDETECTION
;