exe: Support PAK v1.6 self-extracting archives
[deark.git] / modules / arj.c
blobf07e6c1d46258aca134375aeec5dce93c838c445
1 // This file is part of Deark.
2 // Copyright (C) 2020 Jason Summers
3 // See the file COPYING for terms of use.
5 // ARJ compressed archive
7 #include <deark-config.h>
8 #include <deark-private.h>
9 #include <deark-fmtutil.h>
10 DE_DECLARE_MODULE(de_module_arj);
12 #define ARJ_MIN_BASIC_HEADER_SIZE 30
13 #define ARJ_MAX_BASIC_HEADER_SIZE 2600 // From the ARJ TECHNOTE file
15 static const u8 *g_arj_hdr_id = (const u8*)"\x60\xea";
17 enum objtype_enum {
18 ARJ_OBJTYPE_MAINHDR = 100,
19 ARJ_OBJTYPE_MEMBERFILE, // Including directories, volume labels
20 ARJ_OBJTYPE_CHAPTERLABEL,
21 ARJ_OBJTYPE_UNKNOWN,
22 ARJ_OBJTYPE_EOA
25 #define ARJ_FILETYPE_BINARY 0
26 #define ARJ_FILETYPE_TEXT 1
27 #define ARJ_FILETYPE_MAINHDR 2
28 #define ARJ_FILETYPE_DIR 3
29 #define ARJ_FILETYPE_VOLUMELABEL 4
30 #define ARJ_FILETYPE_CHAPTERLABEL 5
32 #define ARJ_OS_DOS 0
33 #define ARJ_OS_UNIX 2
34 #define ARJ_OS_OS2 5
35 #define ARJ_OS_NEXT 8
36 #define ARJ_OS_WIN95 10
37 #define ARJ_OS_WIN32 11
39 struct member_data {
40 de_encoding input_encoding;
41 UI hdr_id;
42 enum objtype_enum objtype; // Artificial field; tells how to parse and process this item
43 u8 archiver_ver_num;
44 u8 min_ver_to_extract;
45 u8 os;
46 u8 unix_timestamp_format;
47 u8 flags;
48 u8 method;
49 u8 file_type; // ARJ_FILETYPE_*
51 u8 supports_ANSIPAGE_flag;
52 u8 supports_chapters;
53 u8 has_encrver_field;
54 u8 has_flags2_field;
55 u8 has_arch_mtime_field;
57 u8 is_dir;
58 u8 is_executable;
59 u8 is_nonexecutable;
60 UI file_mode;
61 u32 crc_reported;
62 i64 cmpr_len;
63 i64 orig_len;
64 i64 cmpr_pos;
65 struct de_timestamp tmstamp[DE_TIMESTAMPIDX_COUNT];
66 struct de_stringreaderdata *name_srd;
69 typedef struct localctx_struct {
70 de_encoding input_encoding; // if DE_ENCODING_UNKNOWN, autodetect for each member
71 u8 ansipage_flag;
72 u8 is_secured;
73 u8 is_old_secured;
74 u8 arjprot_flag;
75 u8 encryption_ver;
76 i64 archive_start;
77 i64 security_envelope_pos;
78 i64 security_envelope_len;
79 i64 arjprot_pos;
80 struct de_crcobj *crco;
81 } lctx;
83 static void read_arj_datetime(deark *c, lctx *d, struct member_data *md,
84 i64 pos, struct de_timestamp *ts1, const char *name)
86 i64 dosdt, dostm;
87 char timestamp_buf[64];
89 dostm = de_getu16le(pos);
90 dosdt = de_getu16le(pos+2);
91 if(dostm==0 && dosdt==0) {
92 de_snprintf(timestamp_buf, sizeof(timestamp_buf), "[not set]");
94 else if(md->unix_timestamp_format) {
95 i64 ut;
97 // Unix time is usually signed, but it seems that Open Source ARJ makes
98 // it unsigned, so the valid dates are from 1970-2106.
99 ut = (dosdt<<16) | dostm;
100 de_unix_time_to_timestamp(ut, ts1, 0x1);
101 de_timestamp_to_string(ts1, timestamp_buf, sizeof(timestamp_buf), 0);
103 else {
104 de_dos_datetime_to_timestamp(ts1, dosdt, dostm);
105 ts1->tzcode = DE_TZCODE_LOCAL;
106 de_timestamp_to_string(ts1, timestamp_buf, sizeof(timestamp_buf), 0);
108 de_dbg(c, "%s time: %s", name, timestamp_buf);
111 static void handle_comment(deark *c, lctx *d, struct member_data *md, i64 pos,
112 i64 nbytes_avail)
114 de_ucstring *s = NULL;
115 dbuf *outf = NULL;
117 if(nbytes_avail<2) goto done;
118 s = ucstring_create(c);
119 // The header containing the comment is limited to about 2.5KB, so we don't have
120 // check sizes here.
121 dbuf_read_to_ucstring(c->infile, pos, nbytes_avail, s, DE_CONVFLAG_STOP_AT_NUL,
122 DE_EXTENC_MAKE(md->input_encoding, DE_ENCSUBTYPE_HYBRID));
123 if(s->len<1) goto done;
124 de_dbg(c, "comment: \"%s\"", ucstring_getpsz_d(s));
126 if(c->extract_level>=2) {
127 const char *token;
129 if(md->objtype==ARJ_OBJTYPE_MAINHDR) token = "comment.txt";
130 else token = "fcomment.txt";
132 outf = dbuf_create_output_file(c, token, NULL, DE_CREATEFLAG_IS_AUX);
133 ucstring_write_as_utf8(c, s, outf, 1);
136 done:
137 dbuf_close(outf);
138 ucstring_destroy(s);
141 static const char *get_host_os_name(u8 n)
143 static const char *names[12] = { "MSDOS", "PRIMOS", "Unix", "Amiga", "MacOS",
144 "OS/2", "Apple GS", "Atari ST", "NeXT", "VMS", "Win95", "WIN32" };
146 if(n<=11) return names[(UI)n];
147 return "?";
150 static const char *get_file_type_name(struct member_data *md, u8 n)
152 const char *name = NULL;
154 switch(n) {
155 case 0: name = "binary"; break;
156 case 1: name = "text"; break;
157 case 2: name = "main header"; break;
158 case 3: name = "directory"; break;
159 case 4: name = "volume label"; break;
160 case 5: name = "chapter"; break;
163 return name?name:"?";
166 static void get_flags_descr(struct member_data *md, u8 n1, de_ucstring *s)
168 u8 n = n1;
170 if(n & 0x01) {
171 ucstring_append_flags_item(s, "GARBLED");
172 n -= 0x01;
175 if((n & 0x02) && (md->objtype==ARJ_OBJTYPE_MAINHDR)) {
176 if(md->supports_ANSIPAGE_flag) {
177 // ANSIPAGE introduced around ARJ v2.62.
178 ucstring_append_flags_item(s, "ANSIPAGE");
180 else {
181 // Suspect this is supported thru v2.39b, and was replaced with
182 // (new) SECURED in v2.39c, with no versions that support both.
183 ucstring_append_flags_item(s, "OLD_SECURED");
185 n -= 0x02;
188 if(n & 0x04) {
189 ucstring_append_flags_item(s, "VOLUME");
190 n -= 0x04;
193 if(n & 0x08) {
194 if(md->objtype==ARJ_OBJTYPE_MAINHDR) {
195 ucstring_append_flags_item(s, "ARJPROT");
197 else {
198 ucstring_append_flags_item(s, "EXTFILE");
200 n -= 0x08;
203 if(n & 0x10) {
204 ucstring_append_flags_item(s, "PATHSYM");
205 n -= 0x10;
208 if((n & 0x40) && (md->objtype==ARJ_OBJTYPE_MAINHDR)) {
209 ucstring_append_flags_item(s, "SECURED");
210 n -= 0x40;
213 if((n & 0x80) && (md->objtype==ARJ_OBJTYPE_MAINHDR)) {
214 ucstring_append_flags_item(s, "ALTNAME");
215 n -= 0x80;
218 if(n!=0) {
219 ucstring_append_flags_itemf(s, "0x%02x", (UI)n);
223 struct method4_ctx {
224 i64 nbytes_written;
225 int stop_flag;
226 u8 old_format;
227 struct de_dfilter_out_params *dcmpro;
228 struct de_bitreader bitrd;
231 static void method4_lz77buf_writebytecb(struct de_lz77buffer *rb, const u8 n)
233 struct method4_ctx *cctx = (struct method4_ctx*)rb->userdata;
235 if(cctx->stop_flag) return;
236 if(cctx->dcmpro->len_known) {
237 if(cctx->nbytes_written >= cctx->dcmpro->expected_len) {
238 cctx->stop_flag = 1;
239 return;
243 dbuf_writebyte(cctx->dcmpro->f, n);
244 cctx->nbytes_written++;
247 static UI method4_read_a_length_code(struct method4_ctx *cctx)
249 UI onescount = 0;
250 UI n;
252 // Read up to 7 bits, counting the number of 1 bits, stopping after the first 0.
253 while(1) {
254 n = (UI)de_bitreader_getbits(&cctx->bitrd, 1);
255 if(n==0) break;
256 onescount++;
257 if(onescount>=7) {
258 if(cctx->old_format) {
259 // A small hack for ARJ v0.13-0.14.
260 // Seems to work, but not extensively tested.
261 // This extra bit is presumed to be 0. I don't know what
262 // happens if it's 1.
263 (void)de_bitreader_getbits(&cctx->bitrd, 1);
265 break;
268 // However many ones there were, read that number of bits.
269 if(onescount==0) return 0;
270 n = (UI)de_bitreader_getbits(&cctx->bitrd, onescount);
271 return (1U<<onescount)-1 + n;
274 static UI method4_read_an_offset(struct method4_ctx *cctx)
276 UI onescount = 0;
277 UI n;
279 // Read up to 4 bits, counting the number of 1 bits, stopping after the first 0.
280 while(1) {
281 n = (UI)de_bitreader_getbits(&cctx->bitrd, 1);
282 if(n==0) break;
283 onescount++;
284 if(onescount>=4) {
285 if(cctx->old_format) {
286 (void)de_bitreader_getbits(&cctx->bitrd, 1);
288 break;
292 // Read {9 + the number of 1 bits} more bits.
293 n = (UI)de_bitreader_getbits(&cctx->bitrd, 9+onescount);
294 return (1U<<(9+onescount))-512 + n;
297 static void decompress_method_4(deark *c, lctx *d, struct member_data *md,
298 struct de_dfilter_in_params *dcmpri, struct de_dfilter_out_params *dcmpro,
299 struct de_dfilter_results *dres)
301 struct method4_ctx *cctx = NULL;
302 struct de_lz77buffer *ringbuf = NULL;
304 cctx = de_malloc(c, sizeof(struct method4_ctx));
305 cctx->dcmpro = dcmpro;
306 cctx->bitrd.f = dcmpri->f;
307 cctx->bitrd.curpos = dcmpri->pos;
308 cctx->bitrd.endpos = dcmpri->pos + dcmpri->len;
310 if(md->archiver_ver_num==1) {
311 cctx->old_format = 1;
314 // The maximum offset that can be encoded is 15871, so a 16K history is enough.
315 ringbuf = de_lz77buffer_create(c, 16384);
316 ringbuf->writebyte_cb = method4_lz77buf_writebytecb;
317 ringbuf->userdata = (void*)cctx;
319 while(1) {
320 UI len_code;
322 if(cctx->bitrd.eof_flag) goto done;
323 if(cctx->stop_flag) goto done;
324 if(cctx->dcmpro->len_known && (cctx->nbytes_written >= cctx->dcmpro->expected_len)) {
325 goto done;
328 len_code = method4_read_a_length_code(cctx);
329 if(len_code==0) {
330 u8 b;
332 b = (u8)de_bitreader_getbits(&cctx->bitrd, 8);
333 if(c->debug_level>=4) {
334 de_dbg(c, "lit %u", (UI)b);
336 de_lz77buffer_add_literal_byte(ringbuf, b);
338 else {
339 UI offs;
341 offs = method4_read_an_offset(cctx);
342 if(c->debug_level>=4) {
343 de_dbg(c, "match d=%u l=%u", (UI)(offs+1), (UI)(len_code+2));
345 de_lz77buffer_copy_from_hist(ringbuf, ringbuf->curpos-1-offs, len_code+2);
349 done:
350 dres->bytes_consumed_valid = 1;
351 dres->bytes_consumed = cctx->bitrd.curpos - dcmpri->pos;
352 de_lz77buffer_destroy(c, ringbuf);
353 de_free(c, cctx);
356 static void decompress_method_1(deark *c, lctx *d, struct member_data *md,
357 struct de_dfilter_in_params *dcmpri, struct de_dfilter_out_params *dcmpro,
358 struct de_dfilter_results *dres)
360 struct de_lh5x_params lzhparams;
362 de_zeromem(&lzhparams, sizeof(struct de_lh5x_params));
363 if(md->min_ver_to_extract==51) {
364 lzhparams.fmt = DE_LH5X_FMT_LH7; // For ARJZ
366 else {
367 lzhparams.fmt = DE_LH5X_FMT_LH6;
370 // ARJ does not appear to allow LZ77 offsets that point to data before
371 // the beginning of the file, so it doesn't matter what we initialize the
372 // history buffer to.
373 lzhparams.history_fill_val = 0x00;
375 lzhparams.zero_codes_block_behavior = DE_LH5X_ZCB_65536;
376 lzhparams.warn_about_zero_codes_block = 1;
377 fmtutil_decompress_lh5x(c, dcmpri, dcmpro, dres, &lzhparams);
379 de_dbg3(c, "max dist: %"I64_FMT, lzhparams.max_offset_used);
380 if(!dres->errcode) {
381 if(lzhparams.max_offset_used>26624 && md->min_ver_to_extract<=11) {
382 // Open-source ARJ can make files like this, with "-hdd32750" option.
383 de_warn(c, "%s: Non-portable compression detected (max dist=%"I64_FMT", "
384 "expected "DE_CHAR_LEQ"26624)",
385 ucstring_getpsz_d(md->name_srd->str), lzhparams.max_offset_used);
390 static void extract_member_file(deark *c, lctx *d, struct member_data *md)
392 de_finfo *fi = NULL;
393 dbuf *outf = NULL;
394 size_t k;
395 int need_to_decompress;
396 u32 crc_calc;
397 struct de_dfilter_in_params dcmpri;
398 struct de_dfilter_out_params dcmpro;
399 struct de_dfilter_results dres;
401 if(md->objtype!=ARJ_OBJTYPE_MEMBERFILE) goto done;
402 if(!md->name_srd) goto done;
404 if(md->is_dir || (md->orig_len==0))
405 need_to_decompress = 0;
406 else
407 need_to_decompress = 1;
409 if(md->file_type==9) { // Presumably ARJZ -t9
410 de_err(c, "%s: Unsupported file type",
411 ucstring_getpsz_d(md->name_srd->str));
412 goto done;
415 if((md->flags & 0x01) && need_to_decompress) {
416 de_err(c, "%s: %sed files are not supported",
417 ucstring_getpsz_d(md->name_srd->str),
418 (d->encryption_ver>=2 ? "Encrypt":"Garbl"));
419 goto done;
422 if(need_to_decompress && (md->method>4)) {
423 de_err(c, "%s: Compression method %u is not supported",
424 ucstring_getpsz_d(md->name_srd->str), (UI)md->method);
425 goto done;
428 if((md->flags & 0x0c)!=0) { // Test for VOLUME(0x4) and/or EXTFILE(0x08) flag
429 de_warn(c, "%s: Incomplete file; multi-volume archives are not supported",
430 ucstring_getpsz_d(md->name_srd->str));
433 fi = de_finfo_create(c);
435 de_finfo_set_name_from_ucstring(c, fi, md->name_srd->str, DE_SNFLAG_FULLPATH);
436 fi->original_filename_flag = 1;
438 fi->is_directory = md->is_dir;
439 fi->is_volume_label = (md->file_type==ARJ_FILETYPE_VOLUMELABEL);
440 if(!fi->is_directory && !fi->is_volume_label) {
441 if(md->is_executable) {
442 fi->mode_flags |= DE_MODEFLAG_EXE;
444 else if(md->is_nonexecutable) {
445 fi->mode_flags |= DE_MODEFLAG_NONEXE;
449 for(k=0; k<DE_TIMESTAMPIDX_COUNT; k++) {
450 fi->timestamp[k] = md->tmstamp[k];
453 outf = dbuf_create_output_file(c, NULL, fi, 0);
454 dbuf_enable_wbuffer(outf);
456 if(md->is_dir) goto done;
458 de_dfilter_init_objects(c, &dcmpri, &dcmpro, &dres);
459 dcmpri.f = c->infile;
460 dcmpri.pos = md->cmpr_pos;
461 dcmpri.len = md->cmpr_len;
462 dcmpro.f = outf;
463 dcmpro.len_known = 1;
464 dcmpro.expected_len = md->orig_len;
466 de_crcobj_reset(d->crco);
467 dbuf_set_writelistener(outf, de_writelistener_for_crc, (void*)d->crco);
469 if(md->orig_len==0) {
472 else if(md->method==0) {
473 fmtutil_decompress_uncompressed(c, &dcmpri, &dcmpro, &dres, 0);
475 else if(md->method>=1 && md->method<=3) {
476 decompress_method_1(c, d, md, &dcmpri, &dcmpro, &dres);
478 else if(md->method==4) {
479 decompress_method_4(c, d, md, &dcmpri, &dcmpro, &dres);
481 dbuf_flush(dcmpro.f);
483 if(dres.errcode) {
484 de_err(c, "%s: Decompression failed: %s", ucstring_getpsz_d(md->name_srd->str),
485 de_dfilter_get_errmsg(c, &dres));
486 goto done;
489 crc_calc = de_crcobj_getval(d->crco);
490 de_dbg(c, "crc (calculated): 0x%08x", (UI)crc_calc);
491 if(crc_calc != md->crc_reported) {
492 de_err(c, "%s: CRC check failed", ucstring_getpsz_d(md->name_srd->str));
493 goto done;
496 done:
497 dbuf_close(outf);
498 if(fi) de_finfo_destroy(c, fi);
501 static const char *get_objtype_name(enum objtype_enum t) {
502 const char *name = NULL;
504 switch(t) {
505 case ARJ_OBJTYPE_MAINHDR: name="archive header"; break;
506 case ARJ_OBJTYPE_MEMBERFILE: name="member file"; break;
507 case ARJ_OBJTYPE_CHAPTERLABEL: name="chapter label"; break;
508 case ARJ_OBJTYPE_EOA: name="end of archive"; break;
509 default: break;
511 return name?name:"?";
514 static void fixup_path(de_ucstring *s)
516 i64 i;
518 for(i=0; i<s->len; i++) {
519 if(s->str[i]=='\\') {
520 s->str[i] = '/';
525 static char *get_archiver_ver_name(u8 v, char *buf, size_t buflen)
527 static const char *anames[11] = {
528 "0.13-0.14", "0.15-1.00", "1.10-2.22", "2.30", "2.39a-b",
529 "2.39c-2.41a", "2.42a-2.50a", "2.55-2.61", "2.62-2.63", "2.63-2.76",
530 "2.81+" };
531 static const char *a32names[3] = { "3.00a-3.01a", "3.02-3.09", "3.10+" };
533 if(v>=1 && v<=11) {
534 // v=9 could also be ARJ32 3.00, but it's not worth listing.
535 de_snprintf(buf, buflen, "ARJ %s", anames[(UI)v-1]);
537 else if(v>=100 && v<=102) {
538 de_snprintf(buf, buflen, "ARJ32 %s", a32names[(UI)v-100]);
540 else if(v==51) {
541 de_strlcpy(buf, "ARJZ", buflen);
543 else {
544 de_strlcpy(buf, "?", buflen);
546 return buf;
549 static int do_extended_headers(deark *c, lctx *d, struct member_data *md,
550 i64 pos1, i64 *pbytes_consumed)
552 i64 pos = pos1;
553 int idx = 0;
554 u32 crc_reported;
555 u32 crc_calc;
556 int retval = 0;
557 int saved_indent_level;
559 de_dbg_indent_save(c, &saved_indent_level);
560 de_dbg(c, "ext hdrs at %"I64_FMT, pos1);
561 de_dbg_indent(c, 1);
563 while(1) {
564 i64 ext_hdr_size;
565 i64 ext_hdr_startpos = pos;
566 i64 dpos;
568 ext_hdr_size = de_getu16le_p(&pos);
570 if(ext_hdr_size==0) {
571 de_dbg(c, "end of ext hdrs at %"I64_FMT, pos);
572 retval = 1;
573 goto done;
575 de_dbg(c, "ext hdr #%d at %"I64_FMT", dlen=%"I64_FMT, idx, ext_hdr_startpos,
576 ext_hdr_size);
577 de_dbg_indent(c, 1);
579 if(pos+ext_hdr_size+4 > c->infile->len) goto done;
581 dpos = pos;
583 de_crcobj_reset(d->crco);
584 de_crcobj_addslice(d->crco, c->infile, dpos, ext_hdr_size);
585 crc_calc = de_crcobj_getval(d->crco);
587 pos = dpos + ext_hdr_size;
588 crc_reported = (u32)de_getu32le_p(&pos);
590 de_dbg(c, "ext hdr crc (reported): 0x%08x", (UI)crc_reported);
591 de_dbg(c, "ext hdr crc (calculated): 0x%08x", (UI)crc_calc);
592 if(crc_calc != crc_reported) goto done; // Assume we've gone off the rails
594 de_dbg_hexdump(c, c->infile, dpos, ext_hdr_size, 256, NULL, 0x1);
596 idx++;
597 de_dbg_indent(c, -1);
600 done:
601 *pbytes_consumed = pos - pos1;
602 de_dbg_indent_restore(c, saved_indent_level);
603 return retval;
606 // If successfully parsed, sets *pbytes_consumed.
607 // Returns 1 normally, 2 if this is the EOA marker, 0 on fatal error.
608 static int do_header_or_member(deark *c, lctx *d, i64 pos1, int expecting_archive_hdr,
609 i64 *pbytes_consumed)
611 i64 pos = pos1;
612 i64 basic_hdr_size;
613 i64 first_hdr_size;
614 i64 first_hdr_endpos;
615 i64 extra_data_len;
616 i64 nbytes_avail;
617 i64 n;
618 i64 basic_hdr_endpos;
619 i64 bytes_consumed;
620 u32 basic_hdr_crc_reported;
621 u32 basic_hdr_crc_calc;
622 struct member_data *md = NULL;
623 de_ucstring *flags_descr = NULL;
624 int retval = 0;
625 int saved_indent_level;
626 u8 b;
627 char namebuf[32];
628 char tmpbuf[64];
630 de_dbg_indent_save(c, &saved_indent_level);
631 md = de_malloc(c, sizeof(struct member_data));
633 md->hdr_id = (UI)de_getu16le_p(&pos);
634 if(md->hdr_id!=0xea60) {
635 de_err(c, "ARJ data not found at %"I64_FMT, pos1);
636 goto done;
639 de_dbg(c, "block at %"I64_FMT, pos1);
640 de_dbg_indent(c, 1);
642 basic_hdr_size = de_getu16le_p(&pos);
643 de_dbg(c, "basic header size: %"I64_FMT, basic_hdr_size);
644 if(basic_hdr_size==0) {
645 md->objtype = ARJ_OBJTYPE_EOA;
647 else {
648 // Skip ahead to read some fields that can affect fields that appear
649 // before them.
650 md->archiver_ver_num = de_getbyte(pos1+5);
651 md->file_type = de_getbyte(pos1+10);
653 if(md->file_type==ARJ_FILETYPE_MAINHDR) {
654 md->objtype = ARJ_OBJTYPE_MAINHDR;
656 else if(md->file_type==ARJ_FILETYPE_CHAPTERLABEL) {
657 md->objtype = ARJ_OBJTYPE_CHAPTERLABEL;
659 else if(md->file_type<=4) {
660 md->objtype = ARJ_OBJTYPE_MEMBERFILE;
662 else if(md->file_type==9 && md->archiver_ver_num==51) {
663 md->objtype = ARJ_OBJTYPE_MEMBERFILE; // ARJZ with -t9 option
665 else {
666 md->objtype = ARJ_OBJTYPE_UNKNOWN;
669 de_dbg(c, "block type: %s", get_objtype_name(md->objtype));
671 if(basic_hdr_size==0) {
672 *pbytes_consumed = 4;
673 retval = 2;
674 goto done;
677 if(basic_hdr_size>ARJ_MAX_BASIC_HEADER_SIZE) {
678 de_err(c, "Bad header size");
679 goto done;
682 de_dbg(c, "basic header at %"I64_FMT, pos);
683 de_dbg_indent(c, 1);
685 de_dbg(c, "first header at %"I64_FMT, pos);
686 de_dbg_indent(c, 1);
688 basic_hdr_endpos = pos1 + 4 + basic_hdr_size;
689 first_hdr_size = (i64)de_getbyte_p(&pos);
690 de_dbg(c, "first header size: %"I64_FMT, first_hdr_size);
691 first_hdr_endpos = pos1 + 4 + first_hdr_size;
692 pos++; // md->archiver_ver_num, already read
693 de_dbg(c, "archiver version: %u (%s)", (UI)md->archiver_ver_num,
694 get_archiver_ver_name(md->archiver_ver_num, namebuf, sizeof(namebuf)));
696 // Note:
697 // ver>=100 used by ARJ32 roughly corresponds to that number minus 91.
698 // ver=51 used by ARJZ probably corresponds to ver=6 or 7.
699 md->supports_ANSIPAGE_flag = (u8)((md->archiver_ver_num>=9 && md->archiver_ver_num<=49) ||
700 md->archiver_ver_num>=100);
701 md->supports_chapters = (u8)((md->archiver_ver_num>=8 && md->archiver_ver_num<=49) ||
702 md->archiver_ver_num>=100);
703 md->has_encrver_field = md->supports_chapters;
704 md->has_flags2_field = (u8)((md->archiver_ver_num>=11 && md->archiver_ver_num<=49) ||
705 md->archiver_ver_num>=102);
706 md->has_arch_mtime_field = (u8)(md->archiver_ver_num>=6);
708 md->min_ver_to_extract = de_getbyte_p(&pos);
709 de_dbg(c, "min ver to extract: %u", (UI)md->min_ver_to_extract);
711 md->os = de_getbyte_p(&pos);
712 de_dbg(c, "host OS: %u (%s)", (UI)md->os, get_host_os_name(md->os));
714 if((md->os==ARJ_OS_UNIX || md->os==ARJ_OS_NEXT) &&
715 (md->archiver_ver_num>=11 && md->archiver_ver_num<=49))
717 // Ref: Open Source ARJ, resource/en/readme.txt
718 md->unix_timestamp_format = 1;
721 md->flags = de_getbyte_p(&pos);
722 flags_descr = ucstring_create(c);
723 get_flags_descr(md, md->flags, flags_descr);
724 de_dbg(c, "flags: 0x%02x (%s)", (UI)md->flags, ucstring_getpsz_d(flags_descr));
725 if(md->objtype==ARJ_OBJTYPE_MAINHDR) {
726 if(md->supports_ANSIPAGE_flag && (md->flags & 0x02)) {
727 d->ansipage_flag = 1;
729 if(md->flags & 0x40) {
730 d->is_secured = 1;
732 else if((!md->supports_ANSIPAGE_flag) && (md->flags & 0x02)) {
733 d->is_old_secured = 1;
735 if(md->flags & 0x08) {
736 d->arjprot_flag = 1;
740 // Now we have enough information to choose a character encoding.
741 md->input_encoding = d->input_encoding;
742 if(md->input_encoding==DE_ENCODING_UNKNOWN) {
743 if(d->ansipage_flag) {
744 md->input_encoding = DE_ENCODING_WINDOWS1252;
746 else {
747 md->input_encoding = DE_ENCODING_CP437;
751 if(md->objtype==ARJ_OBJTYPE_MAINHDR) {
752 b = de_getbyte_p(&pos);
753 if(d->is_secured) {
754 de_dbg(c, "security version: %u", (UI)b);
757 else {
758 md->method = de_getbyte_p(&pos);
759 de_dbg(c, "cmpr method: %u", (UI)md->method);
762 pos++; // file_type already read
763 de_dbg(c, "file type: %u (%s)", (UI)md->file_type, get_file_type_name(md, md->file_type));
764 if(expecting_archive_hdr && md->file_type!=ARJ_FILETYPE_MAINHDR) {
765 de_err(c, "Invalid or missing archive header");
766 goto done;
768 if(md->objtype==ARJ_OBJTYPE_UNKNOWN) {
769 de_err(c, "Unknown file type: %u", (UI)md->file_type);
770 // (try to continue)
773 pos++; // reserved
775 if(md->objtype==ARJ_OBJTYPE_MAINHDR) {
776 read_arj_datetime(c, d, md, pos, &md->tmstamp[DE_TIMESTAMPIDX_CREATE], "archive creation");
777 pos += 4;
779 else if(md->objtype==ARJ_OBJTYPE_CHAPTERLABEL) {
780 read_arj_datetime(c, d, md, pos, &md->tmstamp[DE_TIMESTAMPIDX_CREATE], "creation");
781 pos += 4;
783 else {
784 read_arj_datetime(c, d, md, pos, &md->tmstamp[DE_TIMESTAMPIDX_MODIFY], "mod");
785 pos += 4;
788 if(md->objtype==ARJ_OBJTYPE_MAINHDR) {
789 md->cmpr_len = 0;
790 if(d->is_old_secured) {
791 n = de_getu32le_p(&pos);
792 de_dbg(c, "archive size: %"I64_FMT, n); // This is a guess
794 else if(md->has_arch_mtime_field) {
795 read_arj_datetime(c, d, md, pos, &md->tmstamp[DE_TIMESTAMPIDX_MODIFY], "archive mod");
796 pos += 4;
798 else {
799 pos += 4;
802 else {
803 // Assume this field always exists, except for the main header and EOA.
804 // We need it just to parse the file.
805 md->cmpr_len = de_getu32le_p(&pos);
806 de_dbg(c, "compressed size: %"I64_FMT, md->cmpr_len);
809 if(md->objtype==ARJ_OBJTYPE_MAINHDR && d->is_old_secured) {
810 // This is a guess
811 de_dbg(c, "security data: %s",
812 de_render_hexbytes_from_dbuf(c->infile, pos, 12, tmpbuf, sizeof(tmpbuf)));
813 pos += 12;
814 goto at_offset_32;
817 if(md->objtype==ARJ_OBJTYPE_MAINHDR) {
818 n = de_getu32le_p(&pos);
819 if(d->is_secured) {
820 de_dbg(c, "archive size: %"I64_FMT, n);
823 else if(md->objtype==ARJ_OBJTYPE_MEMBERFILE) {
824 md->orig_len = de_getu32le_p(&pos);
825 de_dbg(c, "original size: %"I64_FMT, md->orig_len);
827 else {
828 pos += 4;
831 if(md->objtype==ARJ_OBJTYPE_MAINHDR) {
832 n = de_getu32le_p(&pos);
833 if(d->is_secured) {
834 d->security_envelope_pos = n;
835 de_dbg(c, "security envelope pos: %"I64_FMT, d->security_envelope_pos);
837 else if(d->arjprot_flag) {
838 d->arjprot_pos = n; // This is a guess
839 de_dbg(c, "ARJPROT data pos: %"I64_FMT, d->arjprot_pos);
842 else {
843 md->crc_reported = (u32)de_getu32le_p(&pos);
844 de_dbg(c, "crc (reported): 0x%08x", (UI)md->crc_reported);
847 n = de_getu16le_p(&pos);
848 de_dbg(c, "filespec pos in filename: %d", (int)n);
850 if(md->objtype==ARJ_OBJTYPE_MAINHDR) {
851 n = de_getu16le_p(&pos);
852 if(d->is_secured) {
853 d->security_envelope_len = n;
854 de_dbg(c, "security envelope len: %"I64_FMT, d->security_envelope_len);
857 else {
858 de_ucstring *mode_descr;
860 md->file_mode = (UI)de_getu16le_p(&pos);
861 mode_descr = ucstring_create(c);
862 if(md->os==ARJ_OS_DOS || md->os==ARJ_OS_OS2 || md->os==ARJ_OS_WIN95 || md->os==ARJ_OS_WIN32) {
863 de_describe_dos_attribs(c, md->file_mode, mode_descr, 0);
865 else if(md->os==ARJ_OS_UNIX || md->os==ARJ_OS_NEXT) {
866 ucstring_printf(mode_descr, DE_ENCODING_LATIN1, "octal %03o", md->file_mode);
867 if((md->file_mode)&0111) {
868 md->is_executable = 1;
870 else {
871 md->is_nonexecutable = 1;
874 else {
875 ucstring_append_char(mode_descr, '?');
878 de_dbg(c, "access mode: 0x%02x (%s)", md->file_mode, ucstring_getpsz_d(mode_descr));
879 ucstring_destroy(mode_descr);
882 at_offset_32:
884 b = de_getbyte_p(&pos); // first chapter / encryption ver / host data (byte1) / unused
885 if(md->objtype==ARJ_OBJTYPE_MAINHDR) {
886 if(md->has_encrver_field) {
887 de_dbg(c, "encryption ver: %u", (UI)b);
888 // We expect the GARBLED flag to be set in the main header when this field
889 // is meaningful, but I don't think ARJ requires that.
890 d->encryption_ver = b;
893 else {
894 if(md->supports_chapters) {
895 de_dbg(c, "first chapter: %u", (UI)b);
899 b = de_getbyte_p(&pos); // last chapter / host data (byte2) / unused
900 if(md->supports_chapters) {
901 de_dbg(c, "last chapter: %u", (UI)b);
904 extra_data_len = first_hdr_endpos - pos;
905 de_dbg(c, "extra data at %"I64_FMT", len=%"I64_FMT, pos, extra_data_len);
906 if(extra_data_len>0) {
907 de_dbg_indent(c, 1);
909 if(md->objtype==ARJ_OBJTYPE_MAINHDR) {
910 if(extra_data_len>=1) {
911 b = de_getbyte_p(&pos);
912 if(md->flags & 0x08) {
913 de_dbg(c, "protection factor: %u", (UI)b);
916 if(extra_data_len>=2) {
917 b = de_getbyte_p(&pos);
918 if(md->has_flags2_field) {
919 de_dbg(c, "flags (2nd set): 0x%02x", (UI)b);
923 else if(md->objtype==ARJ_OBJTYPE_MEMBERFILE) {
924 if(extra_data_len>=4) {
925 n = de_getu32le_p(&pos);
926 de_dbg(c, "ext. file pos: %"I64_FMT, n);
928 if(extra_data_len>=12) {
929 read_arj_datetime(c, d, md, pos, &md->tmstamp[DE_TIMESTAMPIDX_ACCESS], "access");
930 pos += 4;
931 read_arj_datetime(c, d, md, pos, &md->tmstamp[DE_TIMESTAMPIDX_CREATE], "create");
932 pos += 4;
934 if(extra_data_len>=16) {
935 n = de_getu32le_p(&pos);
936 de_dbg(c, "ext. orig size: %"I64_FMT, n);
940 de_dbg_indent(c, -1);
943 de_dbg_indent(c, -1);
944 pos = first_hdr_endpos; // Now at the offset of the filename field
945 nbytes_avail = basic_hdr_endpos - pos;
946 de_dbg(c, "filename/comment area at %"I64_FMT", len=%"I64_FMT, pos, nbytes_avail);
947 de_dbg_indent(c, 1);
948 md->name_srd = dbuf_read_string(c->infile, pos, nbytes_avail, 256, DE_CONVFLAG_STOP_AT_NUL,
949 md->input_encoding);
950 if(!(md->flags & 0x10)) {
951 // "PATHSYM" flag missing, need to convert '\' to '/'
952 fixup_path(md->name_srd->str);
954 de_dbg(c, "filename: \"%s\"", ucstring_getpsz_d(md->name_srd->str));
956 if(md->name_srd->found_nul) {
957 pos += md->name_srd->bytes_consumed;
958 nbytes_avail = basic_hdr_endpos - pos;
959 handle_comment(c, d, md, pos, nbytes_avail);
961 de_dbg_indent(c, -1);
963 de_dbg_indent(c, -1);
964 pos = basic_hdr_endpos; // Now at the offset just after the 'comment' field
965 basic_hdr_crc_reported = (u32)de_getu32le_p(&pos);
966 de_dbg(c, "basic hdr crc (reported): 0x%08x", (UI)basic_hdr_crc_reported);
968 de_crcobj_reset(d->crco);
969 de_crcobj_addslice(d->crco, c->infile, pos1+4, basic_hdr_size);
970 basic_hdr_crc_calc = de_crcobj_getval(d->crco);
971 de_dbg(c, "basic hdr crc (calculated): 0x%08x", (UI)basic_hdr_crc_calc);
972 if(basic_hdr_crc_calc != basic_hdr_crc_reported) {
973 de_warn(c, "Header CRC check failed");
976 if(!do_extended_headers(c, d, md, pos, &bytes_consumed)) {
977 de_err(c, "Bad extended headers - can't continue");
978 goto done;
980 pos += bytes_consumed;
982 md->is_dir = (md->file_type==ARJ_FILETYPE_DIR);
984 md->cmpr_pos = pos;
985 if(md->objtype==ARJ_OBJTYPE_MEMBERFILE) {
986 de_dbg(c, "compressed data at %"I64_FMT, md->cmpr_pos);
987 de_dbg_indent(c, 1);
988 extract_member_file(c, d, md);
989 de_dbg_indent(c, -1);
991 pos += md->cmpr_len;
993 *pbytes_consumed = pos - pos1;
994 retval = 1;
996 done:
997 ucstring_destroy(flags_descr);
998 if(md) {
999 de_destroy_stringreaderdata(c, md->name_srd);
1000 de_free(c, md);
1002 de_dbg_indent_restore(c, saved_indent_level);
1003 return retval;
1006 static void report_extra_bytes(deark *c, lctx *d, i64 main_endpos)
1008 i64 num_extra_bytes;
1009 i64 max_pos;
1011 if(d->arjprot_flag) {
1012 return; // This check is not implemented in this situation.
1015 max_pos = main_endpos;
1016 if(d->security_envelope_len>0) {
1017 max_pos = de_max_int(max_pos, d->security_envelope_pos+d->security_envelope_len);
1020 num_extra_bytes = c->infile->len - max_pos;
1021 if(num_extra_bytes>1) {
1022 de_dbg(c, "[%"I64_FMT" extra bytes at EOF, starting at %"I64_FMT"]", num_extra_bytes, max_pos);
1026 static void do_member_sequence(deark *c, lctx *d, i64 pos1)
1028 i64 pos = pos1;
1030 while(1) {
1031 int ret;
1032 i64 bytes_consumed = 0;
1034 if(pos+2 > c->infile->len) goto done;
1036 ret = do_header_or_member(c, d, pos, 0, &bytes_consumed);
1037 if(ret==0 || bytes_consumed<2) goto done;
1038 pos += bytes_consumed;
1039 if(ret==2) { // End of archive
1040 break;
1044 report_extra_bytes(c, d, pos);
1046 done:
1050 static void do_security_envelope(deark *c, lctx *d)
1052 if(d->security_envelope_len==0) return;
1053 de_dbg(c, "security envelope at %"I64_FMT", len=%"I64_FMT, d->security_envelope_pos,
1054 d->security_envelope_len);
1055 de_dbg_indent(c, 1);
1056 de_dbg_hexdump(c, c->infile, d->security_envelope_pos, d->security_envelope_len,
1057 256, NULL, 0x0);
1058 de_dbg_indent(c, -1);
1061 // Low-level options, needed by the relocator utility
1062 struct options_struct {
1063 de_module_params *mparams;
1064 const char *reloc_opt;
1065 i64 archive_start;
1068 // Tries to figure out if an ARJ archive starts at the given offset.
1069 // It must start with the archive header.
1070 // Note that this is not the algorithm specified in the ARJ TECHNOTE file.
1071 // The function is used when we want to tolerate a bad header CRC.
1072 static int is_arj_data_at(deark *c, i64 pos1)
1074 i64 pos;
1075 i64 basic_hdr_size, first_ext_hdr_size;
1076 UI hdr_id;
1078 pos = pos1+2;
1080 basic_hdr_size = de_getu16le_p(&pos);
1081 if(basic_hdr_size>ARJ_MAX_BASIC_HEADER_SIZE || basic_hdr_size<ARJ_MIN_BASIC_HEADER_SIZE) return 0;
1082 pos += basic_hdr_size + 4;
1083 first_ext_hdr_size = de_getu16le_p(&pos);
1084 if(first_ext_hdr_size!=0) pos += first_ext_hdr_size+4;
1086 // Should now be at the start of the 1st member file (after the archive header)
1087 hdr_id = (UI)de_getu16le(pos);
1088 if(hdr_id==0xea60) {
1089 return 1;
1091 return 0;
1094 static u8 is_exe_format(deark *c)
1096 u8 sig[2];
1098 de_read(sig, 0, 2);
1099 if((sig[0]=='M' && sig[1]=='Z') || (sig[0]=='Z' && sig[1]=='M')) {
1100 return 1;
1102 return 0;
1105 static i64 get_exe_overlay_pos(deark *c)
1107 i64 overlay_pos;
1108 struct fmtutil_exe_info *ei;
1110 ei = de_malloc(c, sizeof(struct fmtutil_exe_info));
1111 // TODO: Should collect_exe_info do more validation of the format?
1112 fmtutil_collect_exe_info(c, c->infile, ei);
1113 overlay_pos = ei->end_of_dos_code;
1114 de_free(c, ei);
1115 return overlay_pos;
1118 static void do_run_arj_relocator(deark *c, struct options_struct *arj_opts);
1120 static void de_run_arj(deark *c, de_module_params *mparams)
1122 lctx *d = NULL;
1123 i64 pos;
1124 i64 bytes_consumed = 0;
1125 u8 archive_start_req_flag = 0;
1126 u8 used_scan = 0;
1127 i64 archive_start_req = 0;
1128 i64 exact_archive_start = 0;
1129 const char *s;
1130 u8 scan_opt; // 0 or 1, 0xff for unset
1131 int modcode_R = 0;
1132 struct options_struct *arj_opts = NULL;
1134 arj_opts = de_malloc(c, sizeof(struct options_struct));
1135 arj_opts->mparams = mparams;
1137 if(c->module_disposition==DE_MODDISP_INTERNAL) {
1138 modcode_R = de_havemodcode(c, mparams, 'R');
1141 if(c->module_disposition!=DE_MODDISP_INTERNAL) {
1142 scan_opt = (u8)de_get_ext_option_bool(c, "arj:scan", 0xff);
1144 else {
1145 scan_opt = 0xff;
1148 // Starting point for the scan for the archive header
1149 // (or the exact header pos if scan=0).
1150 if(c->module_disposition!=DE_MODDISP_INTERNAL) {
1151 s = de_get_ext_option(c, "arj:entrypoint");
1152 if(s) {
1153 archive_start_req_flag = 1;
1154 archive_start_req = de_atoi64(s);
1158 // Try various things to find the start of the ARJ archive.
1159 // TODO?: This is unpleasantly complicated.
1161 if(archive_start_req_flag && scan_opt==0) {
1162 exact_archive_start = archive_start_req;
1163 goto after_archive_start_known;
1166 if(modcode_R && mparams && mparams->in_params.obj1) {
1167 struct fmtutil_specialexe_detection_data *edd;
1169 edd = (struct fmtutil_specialexe_detection_data*)mparams->in_params.obj1;
1170 if(edd->payload_valid) {
1171 exact_archive_start = edd->payload_pos;
1172 goto after_archive_start_known;
1176 if(c->module_disposition==DE_MODDISP_AUTODETECT) {
1177 exact_archive_start = 0; // The "identify" routine only looks here
1178 goto after_archive_start_known;
1181 if(scan_opt!=0) {
1182 int ret;
1183 i64 scan_startpos;
1184 u8 allow_bad_crc;
1186 if(archive_start_req_flag) {
1187 scan_startpos = archive_start_req;
1188 allow_bad_crc = 1;
1190 else if(is_exe_format(c)) {
1191 scan_startpos = get_exe_overlay_pos(c);
1192 allow_bad_crc = 0;
1194 else {
1195 scan_startpos = 0;
1196 allow_bad_crc = 1;
1199 if(allow_bad_crc) {
1200 if(is_arj_data_at(c, scan_startpos)) {
1201 exact_archive_start = scan_startpos;
1202 goto after_archive_start_known;
1206 ret = fmtutil_scan_for_arj_data(c->infile, scan_startpos, c->infile->len-scan_startpos, 0,
1207 &exact_archive_start);
1208 if(ret) {
1209 if(exact_archive_start != archive_start_req) {
1210 used_scan = 1;
1212 goto after_archive_start_known;
1216 exact_archive_start = 0; // Give up
1218 after_archive_start_known:
1219 arj_opts->archive_start = exact_archive_start;
1221 if(dbuf_memcmp(c->infile, arj_opts->archive_start, g_arj_hdr_id, 2)) {
1222 if(!modcode_R) {
1223 de_err(c, "Not an ARJ file, or could not find ARJ data");
1225 goto done;
1228 if(used_scan) {
1229 de_dbg(c, "ARJ data found at %"I64_FMT, arj_opts->archive_start);
1232 if(c->module_disposition!=DE_MODDISP_INTERNAL) {
1233 const char *s;
1235 s = de_get_ext_option(c, "arj:reloc");
1236 if(s) {
1237 arj_opts->reloc_opt = s;
1238 do_run_arj_relocator(c, arj_opts);
1239 goto done;
1243 if(modcode_R) {
1244 do_run_arj_relocator(c, arj_opts);
1245 goto done;
1248 d = de_malloc(c, sizeof(lctx));
1250 d->archive_start = arj_opts->archive_start;
1252 de_declare_fmt(c, "ARJ");
1253 d->input_encoding = de_get_input_encoding(c, NULL, DE_ENCODING_UNKNOWN);
1255 d->crco = de_crcobj_create(c, DE_CRCOBJ_CRC32_IEEE);
1256 pos = d->archive_start;
1257 if(do_header_or_member(c, d, pos, 1, &bytes_consumed) != 1) goto done;
1258 pos += bytes_consumed;
1259 if(d->is_secured) {
1260 do_security_envelope(c, d);
1263 do_member_sequence(c, d, pos);
1265 done:
1266 if(d) {
1267 de_crcobj_destroy(d->crco);
1268 de_free(c, d);
1270 de_free(c, arj_opts);
1273 static int de_identify_arj(deark *c)
1275 i64 basic_hdr_size;
1277 if(dbuf_memcmp(c->infile, 0, g_arj_hdr_id, 2)) return 0;
1278 basic_hdr_size = de_getu16le(2);
1279 if(basic_hdr_size>ARJ_MAX_BASIC_HEADER_SIZE || basic_hdr_size<ARJ_MIN_BASIC_HEADER_SIZE) return 0;
1280 if(de_input_file_has_ext(c, "arj")) return 100;
1281 return 75;
1284 static void de_help_arj(deark *c)
1286 de_msg(c, "-opt arj:entrypoint=<n> : Offset of archive header");
1287 de_msg(c, "-opt arj:scan=0 : Disable scanning for the ARJ data");
1288 de_msg(c, "-opt arj:reloc[=<n>] : Move the ARJ data");
1291 void de_module_arj(deark *c, struct deark_module_info *mi)
1293 mi->id = "arj";
1294 mi->desc = "ARJ";
1295 mi->run_fn = de_run_arj;
1296 mi->identify_fn = de_identify_arj;
1297 mi->help_fn = de_help_arj;
1300 /////////////////////// ARJ relocator utility
1301 // This routine converts an ARJ file to one in which the ARJ data starts at
1302 // beginning of the file, or optionally after some padding.
1303 // This is useful for "extracting" a pure ARJ file from a self-extracting
1304 // archive.
1305 // It also disables any v2 "security envelope". If we didn't do that, the ARJ
1306 // software would reject the modified file.
1308 struct arjreloc_ctx {
1309 i64 src_startpos;
1310 dbuf *ahdr;
1313 // Edit the archive header to disable any v2 security envelope.
1314 // Writes to d->ahdr as many bytes as should be replaced in the ARJ file.
1315 static int reloc_process_archive_hdr(deark *c, struct arjreloc_ctx *d)
1317 struct de_crcobj *crco = NULL;
1318 int retval = 0;
1319 i64 basic_hdr_size;
1320 UI hdr_id;
1321 i32 newcrc;
1322 u8 flags;
1323 u8 file_type;
1325 hdr_id = (UI)de_getu16le(d->src_startpos);
1326 if(hdr_id!=0xea60) goto done;
1327 basic_hdr_size = de_getu16le(d->src_startpos+2);
1328 if(basic_hdr_size>ARJ_MAX_BASIC_HEADER_SIZE || basic_hdr_size<ARJ_MIN_BASIC_HEADER_SIZE) goto done;
1330 flags = de_getbyte(d->src_startpos+8);
1331 file_type = de_getbyte(d->src_startpos+10);
1332 if(file_type!=ARJ_FILETYPE_MAINHDR) goto done;
1334 if(flags & 0x40) {
1335 de_info(c, "Note: Disabling ARJ security envelope");
1336 flags -= 0x40;
1338 else if(flags & 0x08) {
1339 de_info(c, "Note: Disabling ARJ-PROTECT");
1340 flags -= 0x08;
1342 else {
1343 // Not secured, or not a problematic type of security. Just copy everything.
1344 retval = 1;
1345 goto done;
1348 // Copy, with changes as needed
1349 dbuf_copy(c->infile, d->src_startpos, 8, d->ahdr);
1351 // Alter some of the bytes
1352 dbuf_writebyte(d->ahdr, flags);
1353 dbuf_writebyte(d->ahdr, 0); // security version
1354 dbuf_copy(c->infile, d->src_startpos+10, 10, d->ahdr); // filetype...modtime
1355 dbuf_writeu32le(d->ahdr, 0); // archive size
1356 dbuf_writeu32le(d->ahdr, 0); // security envelope or ARJPROT pos
1357 dbuf_copy(c->infile, d->src_startpos+28, 2, d->ahdr); // filespec pos
1358 dbuf_writeu16le(d->ahdr, 0); // security envelope len
1359 // (now at file offset 32) Copy the rest of the basic header
1360 dbuf_copy(c->infile, d->src_startpos+32, (4+basic_hdr_size)-32, d->ahdr);
1362 crco = de_crcobj_create(c, DE_CRCOBJ_CRC32_IEEE);
1363 de_crcobj_addslice(crco, d->ahdr, 4, basic_hdr_size);
1364 newcrc = de_crcobj_getval(crco);
1365 dbuf_writeu32le(d->ahdr, newcrc); // basic hdr crc
1367 // Note - This is not the end of the archive header, but anything after
1368 // this can remain unchanged.
1369 retval = 1;
1370 done:
1371 de_crcobj_destroy(crco);
1372 return retval;
1375 static void do_run_arj_relocator(deark *c, struct options_struct *arj_opts)
1377 dbuf *outf = NULL;
1378 i64 dst_startpos = 0;
1379 i64 archive_size;
1380 struct arjreloc_ctx *d = NULL;
1381 int ok = 0;
1383 de_dbg(c, "ARJ relocation mode");
1384 d = de_malloc(c, sizeof(struct arjreloc_ctx));
1386 if(arj_opts->reloc_opt) {
1387 dst_startpos = de_atoi64(arj_opts->reloc_opt);
1389 if(dst_startpos<0) dst_startpos = 0;
1391 d->ahdr = dbuf_create_membuf(c, 0, 0);
1393 d->src_startpos = arj_opts->archive_start;
1394 archive_size = c->infile->len - d->src_startpos;
1395 if(archive_size<1) goto done;
1397 if(!reloc_process_archive_hdr(c, d)) goto done;
1399 de_dbg(c, "reloc from %"I64_FMT" to %"I64_FMT, d->src_startpos, dst_startpos);
1400 outf = dbuf_create_output_file(c, "arj", NULL, 0);
1401 dbuf_write_zeroes(outf, dst_startpos);
1403 // Copy the potentially-changed part of the file
1404 dbuf_copy(d->ahdr, 0, d->ahdr->len, outf);
1405 // Copy the definitely-unchanged part of the file
1406 dbuf_copy(c->infile, d->src_startpos+d->ahdr->len, archive_size-d->ahdr->len, outf);
1407 ok = 1;
1409 done:
1410 if(ok) {
1411 if(arj_opts->mparams) {
1412 // Inform the caller of success
1413 arj_opts->mparams->out_params.flags |= 0x1;
1416 else {
1417 de_err(c, "Cannot relocate/extract this ARJ file");
1419 dbuf_close(outf);
1420 if(d) {
1421 dbuf_close(d->ahdr);
1422 de_free(c, d);