Cleaned up some Huffman-related debug messages
[deark.git] / src / fmtutil-advfile.c
blob122c88c05aee253c6c5ef280f3cd1566839edbf0
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
37 // as expected.
38 struct de_advfile *de_advfile_create(deark *c)
40 struct de_advfile *advf = NULL;
42 advf = de_malloc(c, sizeof(struct de_advfile));
43 advf->c = c;
44 advf->filename = ucstring_create(c);
45 advf->mainfork.fi = de_finfo_create(c);
46 advf->rsrcfork.fi = de_finfo_create(c);
47 return advf;
50 void de_advfile_destroy(struct de_advfile *advf)
52 deark *c;
54 if(!advf) return;
55 c = advf->c;
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);
60 de_free(c, advf);
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)
69 deark *c = advf->c;
71 if(advf->orig_filename) {
72 de_free(c, advf->orig_filename);
73 advf->orig_filename = NULL;
76 if(fnlen<1) return;
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)
92 i64 i;
93 deark *c = advf->c;
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
115 // 0-length file).
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 {
157 unsigned int id;
158 i64 offset;
159 i64 len;
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)
173 i64 t;
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;
179 return t;
182 // If is_appledouble is set, do not write the data fork (it will be handled
183 // in another way).
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;
189 dbuf *outf = NULL;
190 i64 cur_data_pos;
191 size_t num_entries = 0;
192 size_t k;
193 char commentstr[80];
194 size_t comment_strlen;
195 struct applesd_entry entry_info[16];
197 fname = ucstring_create(c);
198 ucstring_append_ucstring(fname, advf->filename);
199 if(fname->len<1) {
200 ucstring_append_sz(fname, "_", DE_ENCODING_LATIN1);
202 if(is_appledouble) {
203 // TODO: Consider using "._" prefix when writing to ZIP/tar
204 ucstring_append_sz(fname, ".adf", DE_ENCODING_LATIN1);
206 else {
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);
216 else {
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;
231 num_entries++;
234 entry_info[num_entries].id = SDID_COMMENT;
235 entry_info[num_entries].len = (i64)comment_strlen;
236 num_entries++;
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;
241 num_entries++;
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;
248 num_entries++;
250 if(advf->rsrcfork.fork_exists) {
251 entry_info[num_entries].id = SDID_RESOURCEFORK;
252 entry_info[num_entries].len = advf->rsrcfork.fork_len;
253 num_entries++;
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;
258 num_entries++;
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");
275 goto done;
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) {
284 case SDID_DATAFORK:
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);
294 break;
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);
306 break;
308 case SDID_REALNAME:
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);
312 break;
314 case SDID_COMMENT:
315 dbuf_write(outf, (const u8*)commentstr, (i64)comment_strlen);
316 break;
318 case SDID_FILEDATES:
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]));
325 break;
327 case SDID_FINDERINFO:
328 if(advf->has_typecode)
329 dbuf_write(outf, advf->typecode, 4);
330 else
331 dbuf_write_zeroes(outf, 4);
332 if(advf->has_creatorcode)
333 dbuf_write(outf, advf->creatorcode, 4);
334 else
335 dbuf_write_zeroes(outf, 4);
336 dbuf_writeu16be(outf, advf->has_finderflags?((i64)advf->finderflags):0);
337 dbuf_write_zeroes(outf, 6 + 16);
338 break;
341 // In case something went wrong, try to make sure we're at the expected
342 // file position.
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);
348 done:
349 dbuf_close(outf);
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)
357 i64 t;
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;
368 dbuf *outf = NULL;
369 de_ucstring *fname = NULL;
370 i64 main_amt_padding;
371 //i64 rsrc_amt_padding;
372 i64 main_fork_len, rsrc_fork_len;
373 dbuf *hdr = NULL;
374 struct de_crcobj *crco = NULL;
375 u32 crc_calc;
377 if(advf->mainfork.fork_exists) {
378 main_fork_len = advf->mainfork.fork_len;
380 else {
381 main_fork_len = 0;
383 if(advf->rsrcfork.fork_exists) {
384 rsrc_fork_len = advf->rsrcfork.fork_len;
386 else {
387 rsrc_fork_len = 0;
390 if(main_fork_len>0xffffffffLL || rsrc_fork_len>0xffffffffLL) {
391 de_err(c, "File too large to write to MacBinary format");
392 goto done;
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);
402 if(fname->len<1) {
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);
413 // Filename
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);
421 else {
422 // TODO: Get the name from elsewhere?
423 dbuf_writebyte(hdr, 7);
424 dbuf_puts(hdr, "Unnamed");
426 dbuf_truncate(hdr, 65);
428 // type/creator
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);
493 done:
494 dbuf_close(hdr);
495 dbuf_close(outf);
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)
504 deark *c = advf->c;
505 int is_mac_file;
506 int fmt;
508 is_mac_file = (advf->rsrcfork.fork_exists && advf->rsrcfork.fork_len>0);
510 if(is_mac_file && !c->macformat_known) {
511 const char *mfmt;
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");
519 if(mfmt) {
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) {
547 extract_dfork = 1;
549 if(advf->rsrcfork.fork_exists && advf->rsrcfork.fork_len>0) {
550 extract_rfork = 1;
552 if(!extract_dfork && !extract_rfork) {
553 extract_dfork = 1;
556 if(extract_dfork) {
557 de_advfile_run_rawfiles(c, advf, 1); // For the data/main fork
559 if(extract_rfork) {
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);
566 else {
567 de_advfile_run_rawfiles(c, advf, 0);