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";
18 ARJ_OBJTYPE_MAINHDR
= 100,
19 ARJ_OBJTYPE_MEMBERFILE
, // Including directories, volume labels
20 ARJ_OBJTYPE_CHAPTERLABEL
,
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
36 #define ARJ_OS_WIN95 10
37 #define ARJ_OS_WIN32 11
40 de_encoding input_encoding
;
42 enum objtype_enum objtype
; // Artificial field; tells how to parse and process this item
44 u8 min_ver_to_extract
;
46 u8 unix_timestamp_format
;
49 u8 file_type
; // ARJ_FILETYPE_*
51 u8 supports_ANSIPAGE_flag
;
55 u8 has_arch_mtime_field
;
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
77 i64 security_envelope_pos
;
78 i64 security_envelope_len
;
80 struct de_crcobj
*crco
;
83 static void read_arj_datetime(deark
*c
, lctx
*d
, struct member_data
*md
,
84 i64 pos
, struct de_timestamp
*ts1
, const char *name
)
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
) {
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);
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
,
114 de_ucstring
*s
= 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
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) {
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);
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
];
150 static const char *get_file_type_name(struct member_data
*md
, u8 n
)
152 const char *name
= NULL
;
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
)
171 ucstring_append_flags_item(s
, "GARBLED");
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");
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");
189 ucstring_append_flags_item(s
, "VOLUME");
194 if(md
->objtype
==ARJ_OBJTYPE_MAINHDR
) {
195 ucstring_append_flags_item(s
, "ARJPROT");
198 ucstring_append_flags_item(s
, "EXTFILE");
204 ucstring_append_flags_item(s
, "PATHSYM");
208 if((n
& 0x40) && (md
->objtype
==ARJ_OBJTYPE_MAINHDR
)) {
209 ucstring_append_flags_item(s
, "SECURED");
213 if((n
& 0x80) && (md
->objtype
==ARJ_OBJTYPE_MAINHDR
)) {
214 ucstring_append_flags_item(s
, "ALTNAME");
219 ucstring_append_flags_itemf(s
, "0x%02x", (UI
)n
);
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
) {
243 dbuf_writebyte(cctx
->dcmpro
->f
, n
);
244 cctx
->nbytes_written
++;
247 static UI
method4_read_a_length_code(struct method4_ctx
*cctx
)
252 // Read up to 7 bits, counting the number of 1 bits, stopping after the first 0.
254 n
= (UI
)de_bitreader_getbits(&cctx
->bitrd
, 1);
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);
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
)
279 // Read up to 4 bits, counting the number of 1 bits, stopping after the first 0.
281 n
= (UI
)de_bitreader_getbits(&cctx
->bitrd
, 1);
285 if(cctx
->old_format
) {
286 (void)de_bitreader_getbits(&cctx
->bitrd
, 1);
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
;
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
)) {
328 len_code
= method4_read_a_length_code(cctx
);
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
);
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);
350 dres
->bytes_consumed_valid
= 1;
351 dres
->bytes_consumed
= cctx
->bitrd
.curpos
- dcmpri
->pos
;
352 de_lz77buffer_destroy(c
, ringbuf
);
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
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
);
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
)
395 int need_to_decompress
;
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;
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
));
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"));
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
);
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
;
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
);
484 de_err(c
, "%s: Decompression failed: %s", ucstring_getpsz_d(md
->name_srd
->str
),
485 de_dfilter_get_errmsg(c
, &dres
));
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
));
498 if(fi
) de_finfo_destroy(c
, fi
);
501 static const char *get_objtype_name(enum objtype_enum t
) {
502 const char *name
= NULL
;
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;
511 return name
?name
:"?";
514 static void fixup_path(de_ucstring
*s
)
518 for(i
=0; i
<s
->len
; i
++) {
519 if(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",
531 static const char *a32names
[3] = { "3.00a-3.01a", "3.02-3.09", "3.10+" };
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]);
541 de_strlcpy(buf
, "ARJZ", buflen
);
544 de_strlcpy(buf
, "?", buflen
);
549 static int do_extended_headers(deark
*c
, lctx
*d
, struct member_data
*md
,
550 i64 pos1
, i64
*pbytes_consumed
)
557 int saved_indent_level
;
559 de_dbg_indent_save(c
, &saved_indent_level
);
560 de_dbg(c
, "ext hdrs at %"I64_FMT
, pos1
);
565 i64 ext_hdr_startpos
= pos
;
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
);
575 de_dbg(c
, "ext hdr #%d at %"I64_FMT
", dlen=%"I64_FMT
, idx
, ext_hdr_startpos
,
579 if(pos
+ext_hdr_size
+4 > c
->infile
->len
) goto done
;
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);
597 de_dbg_indent(c
, -1);
601 *pbytes_consumed
= pos
- pos1
;
602 de_dbg_indent_restore(c
, saved_indent_level
);
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
)
614 i64 first_hdr_endpos
;
618 i64 basic_hdr_endpos
;
620 u32 basic_hdr_crc_reported
;
621 u32 basic_hdr_crc_calc
;
622 struct member_data
*md
= NULL
;
623 de_ucstring
*flags_descr
= NULL
;
625 int saved_indent_level
;
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
);
639 de_dbg(c
, "block at %"I64_FMT
, pos1
);
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
;
648 // Skip ahead to read some fields that can affect fields that appear
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
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;
677 if(basic_hdr_size
>ARJ_MAX_BASIC_HEADER_SIZE
) {
678 de_err(c
, "Bad header size");
682 de_dbg(c
, "basic header at %"I64_FMT
, pos
);
685 de_dbg(c
, "first header at %"I64_FMT
, pos
);
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
)));
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) {
732 else if((!md
->supports_ANSIPAGE_flag
) && (md
->flags
& 0x02)) {
733 d
->is_old_secured
= 1;
735 if(md
->flags
& 0x08) {
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
;
747 md
->input_encoding
= DE_ENCODING_CP437
;
751 if(md
->objtype
==ARJ_OBJTYPE_MAINHDR
) {
752 b
= de_getbyte_p(&pos
);
754 de_dbg(c
, "security version: %u", (UI
)b
);
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");
768 if(md
->objtype
==ARJ_OBJTYPE_UNKNOWN
) {
769 de_err(c
, "Unknown file type: %u", (UI
)md
->file_type
);
775 if(md
->objtype
==ARJ_OBJTYPE_MAINHDR
) {
776 read_arj_datetime(c
, d
, md
, pos
, &md
->tmstamp
[DE_TIMESTAMPIDX_CREATE
], "archive creation");
779 else if(md
->objtype
==ARJ_OBJTYPE_CHAPTERLABEL
) {
780 read_arj_datetime(c
, d
, md
, pos
, &md
->tmstamp
[DE_TIMESTAMPIDX_CREATE
], "creation");
784 read_arj_datetime(c
, d
, md
, pos
, &md
->tmstamp
[DE_TIMESTAMPIDX_MODIFY
], "mod");
788 if(md
->objtype
==ARJ_OBJTYPE_MAINHDR
) {
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");
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
) {
811 de_dbg(c
, "security data: %s",
812 de_render_hexbytes_from_dbuf(c
->infile
, pos
, 12, tmpbuf
, sizeof(tmpbuf
)));
817 if(md
->objtype
==ARJ_OBJTYPE_MAINHDR
) {
818 n
= de_getu32le_p(&pos
);
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
);
831 if(md
->objtype
==ARJ_OBJTYPE_MAINHDR
) {
832 n
= de_getu32le_p(&pos
);
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
);
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
);
853 d
->security_envelope_len
= n
;
854 de_dbg(c
, "security envelope len: %"I64_FMT
, d
->security_envelope_len
);
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;
871 md
->is_nonexecutable
= 1;
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
);
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
;
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) {
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");
931 read_arj_datetime(c
, d
, md
, pos
, &md
->tmstamp
[DE_TIMESTAMPIDX_CREATE
], "create");
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
);
948 md
->name_srd
= dbuf_read_string(c
->infile
, pos
, nbytes_avail
, 256, DE_CONVFLAG_STOP_AT_NUL
,
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");
980 pos
+= bytes_consumed
;
982 md
->is_dir
= (md
->file_type
==ARJ_FILETYPE_DIR
);
985 if(md
->objtype
==ARJ_OBJTYPE_MEMBERFILE
) {
986 de_dbg(c
, "compressed data at %"I64_FMT
, md
->cmpr_pos
);
988 extract_member_file(c
, d
, md
);
989 de_dbg_indent(c
, -1);
993 *pbytes_consumed
= pos
- pos1
;
997 ucstring_destroy(flags_descr
);
999 de_destroy_stringreaderdata(c
, md
->name_srd
);
1002 de_dbg_indent_restore(c
, saved_indent_level
);
1006 static void report_extra_bytes(deark
*c
, lctx
*d
, i64 main_endpos
)
1008 i64 num_extra_bytes
;
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
)
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
1044 report_extra_bytes(c
, d
, pos
);
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
,
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
;
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
)
1075 i64 basic_hdr_size
, first_ext_hdr_size
;
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) {
1094 static u8
is_exe_format(deark
*c
)
1099 if((sig
[0]=='M' && sig
[1]=='Z') || (sig
[0]=='Z' && sig
[1]=='M')) {
1105 static i64
get_exe_overlay_pos(deark
*c
)
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
;
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
)
1124 i64 bytes_consumed
= 0;
1125 u8 archive_start_req_flag
= 0;
1127 i64 archive_start_req
= 0;
1128 i64 exact_archive_start
= 0;
1130 u8 scan_opt
; // 0 or 1, 0xff for unset
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);
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");
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
;
1186 if(archive_start_req_flag
) {
1187 scan_startpos
= archive_start_req
;
1190 else if(is_exe_format(c
)) {
1191 scan_startpos
= get_exe_overlay_pos(c
);
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
);
1209 if(exact_archive_start
!= archive_start_req
) {
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)) {
1223 de_err(c
, "Not an ARJ file, or could not find ARJ data");
1229 de_dbg(c
, "ARJ data found at %"I64_FMT
, arj_opts
->archive_start
);
1232 if(c
->module_disposition
!=DE_MODDISP_INTERNAL
) {
1235 s
= de_get_ext_option(c
, "arj:reloc");
1237 arj_opts
->reloc_opt
= s
;
1238 do_run_arj_relocator(c
, arj_opts
);
1244 do_run_arj_relocator(c
, arj_opts
);
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
;
1260 do_security_envelope(c
, d
);
1263 do_member_sequence(c
, d
, pos
);
1267 de_crcobj_destroy(d
->crco
);
1270 de_free(c
, arj_opts
);
1273 static int de_identify_arj(deark
*c
)
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;
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
)
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
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
{
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
;
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
;
1335 de_info(c
, "Note: Disabling ARJ security envelope");
1338 else if(flags
& 0x08) {
1339 de_info(c
, "Note: Disabling ARJ-PROTECT");
1343 // Not secured, or not a problematic type of security. Just copy everything.
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.
1371 de_crcobj_destroy(crco
);
1375 static void do_run_arj_relocator(deark
*c
, struct options_struct
*arj_opts
)
1378 i64 dst_startpos
= 0;
1380 struct arjreloc_ctx
*d
= NULL
;
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
);
1411 if(arj_opts
->mparams
) {
1412 // Inform the caller of success
1413 arj_opts
->mparams
->out_params
.flags
|= 0x1;
1417 de_err(c
, "Cannot relocate/extract this ARJ file");
1421 dbuf_close(d
->ahdr
);