1 // This file is part of Deark.
2 // Copyright (C) 2019-2020 Jason Summers
3 // See the file COPYING for terms of use.
5 #define DE_NOT_IN_MODULE
6 #include "deark-config.h"
7 #include "deark-private.h"
8 #include "deark-fmtutil.h"
10 #define DE_MACFORMAT_RAW 0
11 #define DE_MACFORMAT_APPLESINGLE 1
12 #define DE_MACFORMAT_APPLEDOUBLE 2
13 #define DE_MACFORMAT_MACBINARY 3
15 // advfile is a uniform way to handle multi-fork files (e.g. classic Mac files
16 // with a resource fork), and files with platform-specific metadata that we
17 // might want to do something special with (e.g. Mac type/creator codes).
18 // It is essentially a wrapper around dbuf/finfo.
20 // de_advfile_create creates a new object.
21 // Then, before calling de_advfile_run, caller must:
22 // - Set advf->filename if possible, e.g. using ucstring_append_*().
23 // - Set advf->original_filename_flag, if appropriate. Note that this annotates the
24 // ->filename field, and is not related to de_advfile_set_orig_filename().
25 // - Set advf->snflags, if needed.
26 // - Set advf->createflags, if needed (unlikely to be).
27 // - Set advf->mainfork.fork_exists, if there is a main fork.
28 // - Set advf->mainfork.fork_len, if there is a main fork. advfile cannot be
29 // used if the fork lengths are not known in advance.
30 // - Set advf->rsrcfork.fork_exists, if there is an rsrc fork.
31 // - Set advf->rsrcfork.fork_len, if there is an rsrc fork.
32 // - Set advf->mainfork.fi->timestamp[*], if known, even if there is no main fork.
33 // Mac files do not use advf->rsrcfork.fi->timestamp[].
34 // - (Same for advf->mainfork.create_time, etc.)
35 // - If appropriate, set other fields potentially advf->mainfork.fi and/or
36 // advf->rsrcfork.fi, such as ->is_directory. But verify that they work
38 struct de_advfile
*de_advfile_create(deark
*c
)
40 struct de_advfile
*advf
= NULL
;
42 advf
= de_malloc(c
, sizeof(struct de_advfile
));
44 advf
->filename
= ucstring_create(c
);
45 advf
->mainfork
.fi
= de_finfo_create(c
);
46 advf
->rsrcfork
.fi
= de_finfo_create(c
);
50 void de_advfile_destroy(struct de_advfile
*advf
)
56 ucstring_destroy(advf
->filename
);
57 de_finfo_destroy(c
, advf
->mainfork
.fi
);
58 de_finfo_destroy(c
, advf
->rsrcfork
.fi
);
59 de_free(c
, advf
->orig_filename
);
63 // Set the original untranslated filename, as an array of bytes of indeterminate
64 // encoding (most likely MacRoman).
65 // We can't necessarily decode this filename correctly, but we can copy it
66 // unchanged to AppleSingle/AppleDouble's "Real Name" field.
67 void de_advfile_set_orig_filename(struct de_advfile
*advf
, const char *fn
, size_t fnlen
)
71 if(advf
->orig_filename
) {
72 de_free(c
, advf
->orig_filename
);
73 advf
->orig_filename
= NULL
;
77 advf
->orig_filename_len
= fnlen
;
78 if(advf
->orig_filename_len
>1024)
79 advf
->orig_filename_len
= 1024;
80 advf
->orig_filename
= de_malloc(c
, advf
->orig_filename_len
);
81 de_memcpy(advf
->orig_filename
, fn
, advf
->orig_filename_len
);
83 if(advf
->orig_filename
[0]<32) {
84 // This is to ensure that our applesd module won't incorrectly guess that
85 // this is a Pascal string.
86 advf
->orig_filename
[0] = '_';
90 static void setup_rsrc_finfo(struct de_advfile
*advf
)
94 de_ucstring
*fname_rsrc
= NULL
;
96 // Only the .mainfork timestamps are used.
97 for(i
=0; i
<DE_TIMESTAMPIDX_COUNT
; i
++) {
98 advf
->rsrcfork
.fi
->timestamp
[i
] = advf
->mainfork
.fi
->timestamp
[i
];
101 fname_rsrc
= ucstring_create(c
);
102 ucstring_append_ucstring(fname_rsrc
, advf
->filename
);
103 if(fname_rsrc
->len
<1) {
104 ucstring_append_sz(fname_rsrc
, "_", DE_ENCODING_LATIN1
);
106 ucstring_append_sz(fname_rsrc
, ".rsrc", DE_ENCODING_LATIN1
);
107 de_finfo_set_name_from_ucstring(c
, advf
->rsrcfork
.fi
, fname_rsrc
, advf
->snflags
);
108 advf
->rsrcfork
.fi
->original_filename_flag
= advf
->original_filename_flag
;
110 ucstring_destroy(fname_rsrc
);
113 // If is_appledouble is set, do not write the resource fork (it will be handled
114 // in another way), and *always* write a main fork (even if we have to write a
116 static void de_advfile_run_rawfiles(deark
*c
, struct de_advfile
*advf
, int is_appledouble
)
118 struct de_advfile_cbparams
*afp_main
= NULL
;
119 struct de_advfile_cbparams
*afp_rsrc
= NULL
;
121 if(advf
->mainfork
.fork_exists
|| is_appledouble
) {
122 if(!advf
->mainfork
.fork_exists
) {
123 advf
->mainfork
.fork_len
= 0;
125 afp_main
= de_malloc(c
, sizeof(struct de_advfile_cbparams
));
126 afp_main
->whattodo
= DE_ADVFILE_WRITEMAIN
;
127 de_finfo_set_name_from_ucstring(c
, advf
->mainfork
.fi
, advf
->filename
, advf
->snflags
);
128 advf
->mainfork
.fi
->original_filename_flag
= advf
->original_filename_flag
;
129 afp_main
->outf
= dbuf_create_output_file(c
, NULL
, advf
->mainfork
.fi
, advf
->createflags
);
130 dbuf_set_writelistener(afp_main
->outf
, advf
->mainfork
.writelistener_cb
,
131 advf
->mainfork
.userdata_for_writelistener
);
132 if(advf
->writefork_cbfn
&& advf
->mainfork
.fork_len
>0) {
133 advf
->writefork_cbfn(c
, advf
, afp_main
);
135 dbuf_close(afp_main
->outf
);
136 afp_main
->outf
= NULL
;
138 if(!is_appledouble
&& advf
->rsrcfork
.fork_exists
&& advf
->rsrcfork
.fork_len
>0) {
139 afp_rsrc
= de_malloc(c
, sizeof(struct de_advfile_cbparams
));
140 setup_rsrc_finfo(advf
);
141 afp_rsrc
->whattodo
= DE_ADVFILE_WRITERSRC
;
142 afp_rsrc
->outf
= dbuf_create_output_file(c
, NULL
, advf
->rsrcfork
.fi
, advf
->createflags
);
143 dbuf_set_writelistener(afp_rsrc
->outf
, advf
->rsrcfork
.writelistener_cb
,
144 advf
->rsrcfork
.userdata_for_writelistener
);
145 if(advf
->writefork_cbfn
) {
146 advf
->writefork_cbfn(c
, advf
, afp_rsrc
);
148 dbuf_close(afp_rsrc
->outf
);
149 afp_rsrc
->outf
= NULL
;
152 de_free(c
, afp_main
);
153 de_free(c
, afp_rsrc
);
156 struct applesd_entry
{
162 #define SDID_DATAFORK 1
163 #define SDID_RESOURCEFORK 2
164 #define SDID_REALNAME 3
165 #define SDID_COMMENT 4
166 #define SDID_FILEDATES 8
167 #define SDID_FINDERINFO 9
169 #define INVALID_APPLESD_DATE ((i64)(-0x80000000LL))
171 static i64
timestamp_to_applesd_date(deark
*c
, struct de_timestamp
*ts
)
175 if(!ts
->is_valid
) return INVALID_APPLESD_DATE
;
176 t
= de_timestamp_to_unix_time(ts
);
177 t
-= (365*30 + 7)*86400;
178 if(t
>0x7fffffffLL
|| t
<-0x7fffffffLL
) return INVALID_APPLESD_DATE
;
182 // If is_appledouble is set, do not write the data fork (it will be handled
184 static void de_advfile_run_applesd(deark
*c
, struct de_advfile
*advf
, int is_appledouble
)
186 de_ucstring
*fname
= NULL
;
187 struct de_advfile_cbparams
*afp_main
= NULL
;
188 struct de_advfile_cbparams
*afp_rsrc
= NULL
;
191 size_t num_entries
= 0;
194 size_t comment_strlen
;
195 struct applesd_entry entry_info
[16];
197 fname
= ucstring_create(c
);
198 ucstring_append_ucstring(fname
, advf
->filename
);
200 ucstring_append_sz(fname
, "_", DE_ENCODING_LATIN1
);
203 // TODO: Consider using "._" prefix when writing to ZIP/tar
204 ucstring_append_sz(fname
, ".adf", DE_ENCODING_LATIN1
);
207 ucstring_append_sz(fname
, ".as", DE_ENCODING_LATIN1
);
209 de_finfo_set_name_from_ucstring(c
, advf
->mainfork
.fi
, fname
, advf
->snflags
);
210 advf
->mainfork
.fi
->original_filename_flag
= advf
->original_filename_flag
;
211 outf
= dbuf_create_output_file(c
, NULL
, advf
->mainfork
.fi
, advf
->createflags
);
213 if(is_appledouble
) { // signature
214 dbuf_writeu32be(outf
, 0x00051607U
);
217 dbuf_writeu32be(outf
, 0x00051600U
);
219 dbuf_writeu32be(outf
, 0x00020000U
); // version
220 dbuf_write_zeroes(outf
, 16); // filler
222 // Decide what entries we will write, and in what order, and their data length.
224 de_snprintf(commentstr
, sizeof(commentstr
), "Apple%s container generated by Deark",
225 is_appledouble
?"Double":"Single");
226 comment_strlen
= de_strlen(commentstr
);
228 if(advf
->orig_filename
) {
229 entry_info
[num_entries
].id
= SDID_REALNAME
;
230 entry_info
[num_entries
].len
= (i64
)advf
->orig_filename_len
;
234 entry_info
[num_entries
].id
= SDID_COMMENT
;
235 entry_info
[num_entries
].len
= (i64
)comment_strlen
;
238 if(advf
->mainfork
.fi
->timestamp
[DE_TIMESTAMPIDX_MODIFY
].is_valid
) {
239 entry_info
[num_entries
].id
= SDID_FILEDATES
;
240 entry_info
[num_entries
].len
= 16;
243 if((advf
->has_typecode
|| advf
->has_creatorcode
|| advf
->has_finderflags
) &&
244 !advf
->mainfork
.fi
->is_directory
)
246 entry_info
[num_entries
].id
= SDID_FINDERINFO
;
247 entry_info
[num_entries
].len
= 32;
250 if(advf
->rsrcfork
.fork_exists
) {
251 entry_info
[num_entries
].id
= SDID_RESOURCEFORK
;
252 entry_info
[num_entries
].len
= advf
->rsrcfork
.fork_len
;
255 if(advf
->mainfork
.fork_exists
&& !is_appledouble
) {
256 entry_info
[num_entries
].id
= SDID_DATAFORK
;
257 entry_info
[num_entries
].len
= advf
->mainfork
.fork_len
;
261 dbuf_writeu16be(outf
, (i64
)num_entries
);
263 // Figure out where the each data element will be written.
264 cur_data_pos
= 26 + 12*(i64
)num_entries
;
265 for(k
=0; k
<num_entries
; k
++) {
266 entry_info
[k
].offset
= cur_data_pos
;
267 cur_data_pos
+= entry_info
[k
].len
;
270 // Write the element table
271 for(k
=0; k
<num_entries
; k
++) {
272 dbuf_writeu32be(outf
, (i64
)entry_info
[k
].id
);
273 if(entry_info
[k
].offset
>0xffffffffLL
|| entry_info
[k
].len
>0xffffffffLL
) {
274 de_err(c
, "File too large to write to AppleSingle/AppleDouble format");
277 dbuf_writeu32be(outf
, entry_info
[k
].offset
);
278 dbuf_writeu32be(outf
, entry_info
[k
].len
);
281 // Write the elements' data
282 for(k
=0; k
<num_entries
; k
++) {
283 switch(entry_info
[k
].id
) {
285 afp_main
= de_malloc(c
, sizeof(struct de_advfile_cbparams
));
286 afp_main
->whattodo
= DE_ADVFILE_WRITEMAIN
;
287 dbuf_set_writelistener(outf
, advf
->mainfork
.writelistener_cb
,
288 advf
->mainfork
.userdata_for_writelistener
);
289 afp_main
->outf
= outf
;
290 if(advf
->writefork_cbfn
&& advf
->mainfork
.fork_len
>0) {
291 advf
->writefork_cbfn(c
, advf
, afp_main
);
293 dbuf_set_writelistener(outf
, NULL
, NULL
);
296 case SDID_RESOURCEFORK
:
297 afp_rsrc
= de_malloc(c
, sizeof(struct de_advfile_cbparams
));
298 afp_rsrc
->whattodo
= DE_ADVFILE_WRITERSRC
;
299 dbuf_set_writelistener(outf
, advf
->rsrcfork
.writelistener_cb
,
300 advf
->rsrcfork
.userdata_for_writelistener
);
301 afp_rsrc
->outf
= outf
;
302 if(advf
->writefork_cbfn
&& advf
->rsrcfork
.fork_len
>0) {
303 advf
->writefork_cbfn(c
, advf
, afp_rsrc
);
305 dbuf_set_writelistener(outf
, NULL
, NULL
);
309 // If you think this code might be wrong, first review the comments
310 // in applesd.c regarding Pascal strings.
311 dbuf_write(outf
, advf
->orig_filename
, (i64
)advf
->orig_filename_len
);
315 dbuf_write(outf
, (const u8
*)commentstr
, (i64
)comment_strlen
);
319 // We could try to maintain dates other than the modification date, but
320 // Deark doesn't generally care about them.
321 dbuf_writei32be(outf
, timestamp_to_applesd_date(c
, &advf
->mainfork
.fi
->timestamp
[DE_TIMESTAMPIDX_CREATE
]));
322 dbuf_writei32be(outf
, timestamp_to_applesd_date(c
, &advf
->mainfork
.fi
->timestamp
[DE_TIMESTAMPIDX_MODIFY
]));
323 dbuf_writei32be(outf
, timestamp_to_applesd_date(c
, &advf
->mainfork
.fi
->timestamp
[DE_TIMESTAMPIDX_BACKUP
]));
324 dbuf_writei32be(outf
, timestamp_to_applesd_date(c
, &advf
->mainfork
.fi
->timestamp
[DE_TIMESTAMPIDX_ACCESS
]));
327 case SDID_FINDERINFO
:
328 if(advf
->has_typecode
)
329 dbuf_write(outf
, advf
->typecode
, 4);
331 dbuf_write_zeroes(outf
, 4);
332 if(advf
->has_creatorcode
)
333 dbuf_write(outf
, advf
->creatorcode
, 4);
335 dbuf_write_zeroes(outf
, 4);
336 dbuf_writeu16be(outf
, advf
->has_finderflags
?((i64
)advf
->finderflags
):0);
337 dbuf_write_zeroes(outf
, 6 + 16);
341 // In case something went wrong, try to make sure we're at the expected
343 // Note: This might not compensate for all failures, as dbuf_truncate
344 // might not be fully implemented for this output type.
345 dbuf_truncate(outf
, entry_info
[k
].offset
+ entry_info
[k
].len
);
350 de_free(c
, afp_main
);
351 de_free(c
, afp_rsrc
);
352 ucstring_destroy(fname
);
355 static i64
timestamp_to_mac_time(const struct de_timestamp
*ts
)
359 if(!ts
->is_valid
) return 0;
360 t
= de_timestamp_to_unix_time(ts
);
361 return t
+ 2082844800;
364 static void de_advfile_run_macbinary(deark
*c
, struct de_advfile
*advf
)
366 struct de_advfile_cbparams
*afp_main
= NULL
;
367 struct de_advfile_cbparams
*afp_rsrc
= NULL
;
369 de_ucstring
*fname
= NULL
;
370 i64 main_amt_padding
;
371 //i64 rsrc_amt_padding;
372 i64 main_fork_len
, rsrc_fork_len
;
374 struct de_crcobj
*crco
= NULL
;
377 if(advf
->mainfork
.fork_exists
) {
378 main_fork_len
= advf
->mainfork
.fork_len
;
383 if(advf
->rsrcfork
.fork_exists
) {
384 rsrc_fork_len
= advf
->rsrcfork
.fork_len
;
390 if(main_fork_len
>0xffffffffLL
|| rsrc_fork_len
>0xffffffffLL
) {
391 de_err(c
, "File too large to write to MacBinary format");
395 main_amt_padding
= advf
->mainfork
.fork_len
% 128;
396 if(main_amt_padding
> 0) main_amt_padding
= 128-main_amt_padding
;
397 //rsrc_amt_padding = advf->rsrcfork.fork_len % 128;
398 //if(rsrc_amt_padding > 0) rsrc_amt_padding = 128-rsrc_amt_padding;
400 fname
= ucstring_create(c
);
401 ucstring_append_ucstring(fname
, advf
->filename
);
403 ucstring_append_sz(fname
, "_", DE_ENCODING_LATIN1
);
405 ucstring_append_sz(fname
, ".bin", DE_ENCODING_LATIN1
);
406 de_finfo_set_name_from_ucstring(c
, advf
->mainfork
.fi
, fname
, advf
->snflags
);
407 advf
->mainfork
.fi
->original_filename_flag
= advf
->original_filename_flag
;
409 // Construct 128-byte header
410 hdr
= dbuf_create_membuf(c
, 128, 0);
411 dbuf_writebyte(hdr
, 0);
414 if(advf
->orig_filename
&& advf
->orig_filename_len
>0) {
415 i64 fnlen
= advf
->orig_filename_len
;
417 if(fnlen
>63) fnlen
=63;
418 dbuf_writebyte(hdr
, (u8
)fnlen
);
419 dbuf_write(hdr
, advf
->orig_filename
, fnlen
);
422 // TODO: Get the name from elsewhere?
423 dbuf_writebyte(hdr
, 7);
424 dbuf_puts(hdr
, "Unnamed");
426 dbuf_truncate(hdr
, 65);
429 if(advf
->has_typecode
) {
430 dbuf_write(hdr
, advf
->typecode
, 4);
432 dbuf_truncate(hdr
, 69);
433 if(advf
->has_creatorcode
) {
434 dbuf_write(hdr
, advf
->creatorcode
, 4);
437 dbuf_truncate(hdr
, 73); // high byte of finder flags
438 if(advf
->has_finderflags
) {
439 dbuf_writebyte(hdr
, (u8
)(advf
->finderflags
>> 8));
442 dbuf_truncate(hdr
, 83);
443 dbuf_writeu32be(hdr
, main_fork_len
);
444 dbuf_writeu32be(hdr
, rsrc_fork_len
);
446 dbuf_truncate(hdr
, 91);
447 dbuf_writeu32be(hdr
, timestamp_to_mac_time(&advf
->mainfork
.fi
->timestamp
[DE_TIMESTAMPIDX_CREATE
]));
448 dbuf_writeu32be(hdr
, timestamp_to_mac_time(&advf
->mainfork
.fi
->timestamp
[DE_TIMESTAMPIDX_MODIFY
]));
450 dbuf_truncate(hdr
, 101); // low byte of finder flags
451 if(advf
->has_finderflags
) {
452 dbuf_writebyte(hdr
, (u8
)(advf
->finderflags
& 0xff));
455 dbuf_truncate(hdr
, 102);
456 dbuf_write(hdr
, (const u8
*)"mBIN", 4);
458 dbuf_truncate(hdr
, 122);
459 dbuf_writebyte(hdr
, 130); // version (III)
460 dbuf_writebyte(hdr
, 129); // compatible version (II)
462 crco
= de_crcobj_create(c
, DE_CRCOBJ_CRC16_CCITT
);
463 de_crcobj_addslice(crco
, hdr
, 0, 124);
464 crc_calc
= de_crcobj_getval(crco
);
465 dbuf_writeu16be(hdr
, (i64
)crc_calc
);
467 dbuf_truncate(hdr
, 128);
468 outf
= dbuf_create_output_file(c
, NULL
, advf
->mainfork
.fi
, advf
->createflags
);
469 dbuf_copy(hdr
, 0, 128, outf
);
471 afp_main
= de_malloc(c
, sizeof(struct de_advfile_cbparams
));
472 afp_rsrc
= de_malloc(c
, sizeof(struct de_advfile_cbparams
));
473 afp_main
->whattodo
= DE_ADVFILE_WRITEMAIN
;
474 afp_rsrc
->whattodo
= DE_ADVFILE_WRITERSRC
;
476 dbuf_set_writelistener(outf
, advf
->mainfork
.writelistener_cb
,
477 advf
->mainfork
.userdata_for_writelistener
);
478 afp_main
->outf
= outf
;
479 if(advf
->writefork_cbfn
&& main_fork_len
>0) {
480 advf
->writefork_cbfn(c
, advf
, afp_main
);
482 dbuf_set_writelistener(outf
, NULL
, NULL
);
483 dbuf_write_zeroes(outf
, main_amt_padding
);
485 dbuf_set_writelistener(outf
, advf
->rsrcfork
.writelistener_cb
,
486 advf
->rsrcfork
.userdata_for_writelistener
);
487 afp_rsrc
->outf
= outf
;
488 if(advf
->writefork_cbfn
&& rsrc_fork_len
>0) {
489 advf
->writefork_cbfn(c
, advf
, afp_rsrc
);
491 dbuf_set_writelistener(outf
, NULL
, NULL
);
496 de_free(c
, afp_main
);
497 de_free(c
, afp_rsrc
);
498 ucstring_destroy(fname
);
499 de_crcobj_destroy(crco
);
502 void de_advfile_run(struct de_advfile
*advf
)
508 is_mac_file
= (advf
->rsrcfork
.fork_exists
&& advf
->rsrcfork
.fork_len
>0);
510 if(is_mac_file
&& !c
->macformat_known
) {
513 c
->macformat_known
= 1;
514 c
->macformat
= DE_MACFORMAT_APPLEDOUBLE
;
516 // [I know there is a module named "macrsrc", so this could lead to confusion,
517 // but I can't think of a better name.]
518 mfmt
= de_get_ext_option(c
, "macrsrc");
520 if(!de_strcmp(mfmt
, "raw")) {
521 c
->macformat
= DE_MACFORMAT_RAW
; // Raw resource file
523 else if(!de_strcmp(mfmt
, "as")) {
524 c
->macformat
= DE_MACFORMAT_APPLESINGLE
;
526 else if(!de_strcmp(mfmt
, "ad")) {
527 c
->macformat
= DE_MACFORMAT_APPLEDOUBLE
;
529 else if(!de_strcmp(mfmt
, "mbin")) {
530 c
->macformat
= DE_MACFORMAT_MACBINARY
;
535 fmt
= c
->macformat
; // Default to the default Mac format.
536 if(fmt
==DE_MACFORMAT_APPLESINGLE
&& advf
->no_applesingle
) fmt
= DE_MACFORMAT_APPLEDOUBLE
;
537 if(fmt
==DE_MACFORMAT_APPLEDOUBLE
&& advf
->no_appledouble
) fmt
= DE_MACFORMAT_RAW
;
539 if(is_mac_file
&& fmt
==DE_MACFORMAT_APPLESINGLE
) {
540 de_advfile_run_applesd(c
, advf
, 0);
542 else if(is_mac_file
&& fmt
==DE_MACFORMAT_APPLEDOUBLE
) {
543 int extract_dfork
= 0;
544 int extract_rfork
= 0;
546 if(advf
->mainfork
.fork_exists
&& advf
->mainfork
.fork_len
>0) {
549 if(advf
->rsrcfork
.fork_exists
&& advf
->rsrcfork
.fork_len
>0) {
552 if(!extract_dfork
&& !extract_rfork
) {
557 de_advfile_run_rawfiles(c
, advf
, 1); // For the data/main fork
560 de_advfile_run_applesd(c
, advf
, 1); // For the rsrc fork
563 else if(is_mac_file
&& fmt
==DE_MACFORMAT_MACBINARY
) {
564 de_advfile_run_macbinary(c
, advf
);
567 de_advfile_run_rawfiles(c
, advf
, 0);