1 // This file is part of Deark.
2 // Copyright (C) 2021 Jason Summers
3 // See the file COPYING for terms of use.
5 // This file is for miscellaneous small archive-format modules.
7 #include <deark-private.h>
8 #include <deark-fmtutil.h>
9 #include <deark-fmtutil-arch.h>
10 DE_DECLARE_MODULE(de_module_cpshrink
);
11 DE_DECLARE_MODULE(de_module_dwc
);
12 DE_DECLARE_MODULE(de_module_edi_pack
);
13 DE_DECLARE_MODULE(de_module_qip
);
14 DE_DECLARE_MODULE(de_module_pcxlib
);
15 DE_DECLARE_MODULE(de_module_gxlib
);
16 DE_DECLARE_MODULE(de_module_mdcd
);
17 DE_DECLARE_MODULE(de_module_cazip
);
18 DE_DECLARE_MODULE(de_module_cmz
);
19 DE_DECLARE_MODULE(de_module_pcshrink
);
20 DE_DECLARE_MODULE(de_module_arcv
);
21 DE_DECLARE_MODULE(de_module_red
);
22 DE_DECLARE_MODULE(de_module_lif_kdc
);
23 DE_DECLARE_MODULE(de_module_ain
);
24 DE_DECLARE_MODULE(de_module_hta
);
25 DE_DECLARE_MODULE(de_module_hit
);
26 DE_DECLARE_MODULE(de_module_binary_ii
);
27 DE_DECLARE_MODULE(de_module_tc_trs80
);
29 static int dclimplode_header_at(deark
*c
, i64 pos
)
35 b
= de_getbyte(pos
+1);
36 if(b
<4 || b
>6) return 0;
40 static void dclimplode_decompressor_fn(struct de_arch_member_data
*md
)
42 fmtutil_dclimplode_codectype1(md
->c
, md
->dcmpri
, md
->dcmpro
, md
->dres
, NULL
);
45 static void dbg_timestamp(deark
*c
, struct de_timestamp
*ts
, const char *name
)
47 char timestamp_buf
[64];
49 de_timestamp_to_string(ts
, timestamp_buf
, sizeof(timestamp_buf
), 0);
50 de_dbg(c
, "%s: %s", name
, timestamp_buf
);
53 // **************************************************************************
55 // **************************************************************************
57 static void cpshrink_decompressor_fn(struct de_arch_member_data
*md
)
61 switch(md
->cmpr_meth
) {
64 fmtutil_dclimplode_codectype1(c
, md
->dcmpri
, md
->dcmpro
, md
->dres
, NULL
);
67 fmtutil_decompress_uncompressed(c
, md
->dcmpri
, md
->dcmpro
, md
->dres
, 0);
70 de_dfilter_set_generic_error(c
, md
->dres
, NULL
);
74 // Caller creates/destroys md, and sets a few fields.
75 static void cpshrink_do_member(deark
*c
, de_arch_lctx
*d
, struct de_arch_member_data
*md
)
77 i64 pos
= md
->member_hdr_pos
;
78 UI cdata_crc_reported
;
81 int saved_indent_level
;
83 de_dbg_indent_save(c
, &saved_indent_level
);
84 md
->cmpr_pos
= d
->cmpr_data_curpos
;
86 de_dbg(c
, "member #%u: hdr at %"I64_FMT
", cmpr data at %"I64_FMT
,
87 (UI
)md
->member_idx
, md
->member_hdr_pos
, md
->cmpr_pos
);
90 cdata_crc_reported
= (u32
)de_getu32le_p(&pos
);
91 de_dbg(c
, "CRC of cmpr. data (reported): 0x%08x", (UI
)cdata_crc_reported
);
93 dbuf_read_to_ucstring(c
->infile
, pos
, 15, md
->filename
, DE_CONVFLAG_STOP_AT_NUL
,
96 de_dbg(c
, "filename: \"%s\"", ucstring_getpsz_d(md
->filename
));
98 md
->cmpr_meth
= (UI
)de_getbyte_p(&pos
);
99 de_dbg(c
, "cmpr. method: %u", md
->cmpr_meth
);
101 de_arch_read_field_orig_len_p(md
, &pos
);
102 de_arch_read_field_cmpr_len_p(md
, &pos
);
103 d
->cmpr_data_curpos
+= md
->cmpr_len
;
105 de_arch_read_field_dttm_p(d
, &md
->fi
->timestamp
[DE_TIMESTAMPIDX_MODIFY
], "mod",
106 DE_ARCH_TSTYPE_DOS_DT
, &pos
);
108 if(!de_arch_good_cmpr_data_pos(md
)) {
113 de_crcobj_reset(d
->crco
);
114 de_crcobj_addslice(d
->crco
, c
->infile
, md
->cmpr_pos
, md
->cmpr_len
);
115 cdata_crc_calc
= de_crcobj_getval(d
->crco
);
116 de_dbg(c
, "CRC of cmpr. data (calculated): 0x%08x", (UI
)cdata_crc_calc
);
117 if(cdata_crc_calc
!=cdata_crc_reported
) {
118 de_err(c
, "File data CRC check failed (expected 0x%08x, got 0x%08x). "
119 "CPZ file may be corrupted.", (UI
)cdata_crc_reported
,
123 md
->dfn
= cpshrink_decompressor_fn
;
124 de_arch_extract_member_file(md
);
127 de_dbg_indent_restore(c
, saved_indent_level
);
130 static void de_run_cpshrink(deark
*c
, de_module_params
*mparams
)
132 de_arch_lctx
*d
= NULL
;
136 u32 member_hdrs_crc_reported
;
137 u32 member_hdrs_crc_calc
;
139 int saved_indent_level
;
141 de_dbg_indent_save(c
, &saved_indent_level
);
142 d
= de_arch_create_lctx(c
);
144 d
->input_encoding
= de_get_input_encoding(c
, NULL
, DE_ENCODING_CP437
);
147 de_dbg(c
, "archive header at %d", (int)pos
);
149 // Not sure if this is a 16-bit, or 32-bit, field, but CP Shrink doesn't
150 // work right if the 2 bytes at offset 2 are not 0.
151 d
->num_members
= de_getu32le_p(&pos
);
152 de_dbg(c
, "number of members: %"I64_FMT
, d
->num_members
);
153 if(d
->num_members
<1 || d
->num_members
>0xffff) {
154 de_err(c
, "Bad member file count");
157 member_hdrs_crc_reported
= (u32
)de_getu32le_p(&pos
);
158 de_dbg(c
, "member hdrs crc (reported): 0x%08x", (UI
)member_hdrs_crc_reported
);
159 de_dbg_indent(c
, -1);
161 member_hdrs_pos
= pos
;
162 member_hdrs_len
= d
->num_members
* 32;
163 d
->cmpr_data_curpos
= member_hdrs_pos
+member_hdrs_len
;
165 de_dbg(c
, "member headers at %"I64_FMT
, member_hdrs_pos
);
167 d
->crco
= de_crcobj_create(c
, DE_CRCOBJ_CRC32_IEEE
);
168 de_crcobj_addslice(d
->crco
, c
->infile
, member_hdrs_pos
, member_hdrs_len
);
169 member_hdrs_crc_calc
= de_crcobj_getval(d
->crco
);
170 de_dbg(c
, "member hdrs crc (calculated): 0x%08x", (UI
)member_hdrs_crc_calc
);
171 if(member_hdrs_crc_calc
!=member_hdrs_crc_reported
) {
172 de_err(c
, "Header CRC check failed (expected 0x%08x, got 0x%08x). "
173 "This is not a valid CP Shrink file", (UI
)member_hdrs_crc_reported
,
174 (UI
)member_hdrs_crc_calc
);
176 de_dbg_indent(c
, -1);
178 de_dbg(c
, "cmpr data starts at %"I64_FMT
, d
->cmpr_data_curpos
);
180 for(i
=0; i
<d
->num_members
; i
++) {
181 struct de_arch_member_data
*md
;
183 md
= de_arch_create_md(c
, d
);
185 md
->member_hdr_pos
= pos
;
188 cpshrink_do_member(c
, d
, md
);
189 de_arch_destroy_md(c
, md
);
190 if(d
->fatalerrflag
) goto done
;
194 de_arch_destroy_lctx(c
, d
);
195 de_dbg_indent_restore(c
, saved_indent_level
);
198 static int de_identify_cpshrink(deark
*c
)
202 if(!de_input_file_has_ext(c
, "cpz")) return 0;
204 if(n
<1 || n
>0xffff) return 0;
205 if(de_getbyte(27)>2) return 0; // cmpr meth of 1st file
209 void de_module_cpshrink(deark
*c
, struct deark_module_info
*mi
)
212 mi
->desc
= "CP Shrink .CPZ";
213 mi
->run_fn
= de_run_cpshrink
;
214 mi
->identify_fn
= de_identify_cpshrink
;
217 // **************************************************************************
219 // **************************************************************************
221 static void dwc_decompressor_fn(struct de_arch_member_data
*md
)
225 if(md
->cmpr_meth
==1) {
226 struct de_lzw_params delzwp
;
228 de_zeromem(&delzwp
, sizeof(struct de_lzw_params
));
229 delzwp
.fmt
= DE_LZWFMT_DWC
;
230 fmtutil_decompress_lzw(c
, md
->dcmpri
, md
->dcmpro
, md
->dres
, &delzwp
);
232 else if(md
->cmpr_meth
==2) {
233 fmtutil_decompress_uncompressed(c
, md
->dcmpri
, md
->dcmpro
, md
->dres
, 0);
236 de_dfilter_set_generic_error(c
, md
->dres
, NULL
);
240 static void squash_slashes(de_ucstring
*s
)
244 for(i
=0; i
<s
->len
; i
++) {
251 // Set md->filename to the full-path filename, using tmpfn_path + tmpfn_base.
252 static void dwc_process_filename(deark
*c
, de_arch_lctx
*d
, struct de_arch_member_data
*md
)
254 ucstring_empty(md
->filename
);
255 squash_slashes(md
->tmpfn_base
);
256 if(ucstring_isempty(md
->tmpfn_path
)) {
257 ucstring_append_ucstring(md
->filename
, md
->tmpfn_base
);
261 md
->set_name_flags
|= DE_SNFLAG_FULLPATH
;
262 ucstring_append_ucstring(md
->filename
, md
->tmpfn_path
);
263 de_arch_fixup_path(md
->filename
, 0x1);
264 if(ucstring_isempty(md
->tmpfn_base
)) {
265 ucstring_append_char(md
->filename
, '_');
268 ucstring_append_ucstring(md
->filename
, md
->tmpfn_base
);
272 static void do_dwc_member(deark
*c
, de_arch_lctx
*d
, i64 pos1
, i64 fhsize
)
275 struct de_arch_member_data
*md
= NULL
;
278 UI cdata_crc_reported
= 0;
280 u8 have_cdata_crc
= 0;
282 de_ucstring
*comment
= NULL
;
284 md
= de_arch_create_md(c
, d
);
286 de_dbg(c
, "member header at %"I64_FMT
, pos1
);
288 md
->tmpfn_base
= ucstring_create(c
);
289 dbuf_read_to_ucstring(c
->infile
, pos
, 12, md
->tmpfn_base
, DE_CONVFLAG_STOP_AT_NUL
,
291 de_dbg(c
, "filename: \"%s\"", ucstring_getpsz_d(md
->tmpfn_base
));
292 // tentative md->filename (could be used by error messages)
293 ucstring_append_ucstring(md
->filename
, md
->tmpfn_base
);
296 de_arch_read_field_orig_len_p(md
, &pos
);
297 de_arch_read_field_dttm_p(d
, &md
->fi
->timestamp
[DE_TIMESTAMPIDX_MODIFY
], "mod",
298 DE_ARCH_TSTYPE_UNIX
, &pos
);
299 de_arch_read_field_cmpr_len_p(md
, &pos
);
300 md
->cmpr_pos
= de_getu32le_p(&pos
);
301 de_dbg(c
, "cmpr. data pos: %"I64_FMT
, md
->cmpr_pos
);
303 b
= de_getbyte_p(&pos
);
304 md
->cmpr_meth
= ((UI
)b
) & 0x0f;
305 de_dbg(c
, "cmpr. method: %u", md
->cmpr_meth
);
306 md
->file_flags
= ((UI
)b
) >> 4;
307 de_dbg(c
, "flags: 0x%x", md
->file_flags
);
308 if(md
->file_flags
& 0x4) {
309 md
->is_encrypted
= 1;
313 cmt_len
= (i64
)de_getbyte_p(&pos
);
314 de_dbg(c
, "comment len: %d", (int)cmt_len
);
317 path_len
= (i64
)de_getbyte_p(&pos
);
318 de_dbg(c
, "path len: %d", (int)path_len
);
321 cdata_crc_reported
= (u32
)de_getu16le_p(&pos
);
322 de_dbg(c
, "CRC of cmpr. data (reported): 0x%04x", (UI
)cdata_crc_reported
);
326 if(!de_arch_good_cmpr_data_pos(md
)) {
331 md
->tmpfn_path
= ucstring_create(c
);
332 dbuf_read_to_ucstring(c
->infile
, md
->cmpr_pos
+md
->cmpr_len
,
334 md
->tmpfn_path
, DE_CONVFLAG_STOP_AT_NUL
, d
->input_encoding
);
335 de_dbg(c
, "path: \"%s\"", ucstring_getpsz_d(md
->tmpfn_path
));
338 comment
= ucstring_create(c
);
339 dbuf_read_to_ucstring(c
->infile
, md
->cmpr_pos
+md
->cmpr_len
+path_len
,
340 cmt_len
-1, comment
, DE_CONVFLAG_STOP_AT_NUL
, d
->input_encoding
);
341 de_dbg(c
, "comment: \"%s\"", ucstring_getpsz_d(comment
));
344 dwc_process_filename(c
, d
, md
);
348 d
->crco
= de_crcobj_create(c
, DE_CRCOBJ_CRC16_ARC
);
350 de_crcobj_reset(d
->crco
);
351 de_crcobj_addslice(d
->crco
, c
->infile
, md
->cmpr_pos
, md
->cmpr_len
);
352 cdata_crc_calc
= de_crcobj_getval(d
->crco
);
353 de_dbg(c
, "CRC of cmpr. data (calculated): 0x%04x", (UI
)cdata_crc_calc
);
354 if(cdata_crc_calc
!=cdata_crc_reported
) {
355 de_err(c
, "File data CRC check failed (expected 0x%04x, got 0x%04x). "
356 "DWC file may be corrupted.", (UI
)cdata_crc_reported
,
362 md
->dfn
= dwc_decompressor_fn
;
363 de_arch_extract_member_file(md
);
367 de_dbg_indent(c
, -1);
368 de_arch_destroy_md(c
, md
);
369 ucstring_destroy(comment
);
372 static int has_dwc_sig(deark
*c
)
374 return !dbuf_memcmp(c
->infile
, c
->infile
->len
-3, (const u8
*)"DWC", 3);
377 static void de_run_dwc(deark
*c
, de_module_params
*mparams
)
379 de_arch_lctx
*d
= NULL
;
383 i64 fhsize
; // size of each file header
386 struct de_timestamp tmpts
;
388 int saved_indent_level
;
390 de_dbg_indent_save(c
, &saved_indent_level
);
392 d
= de_arch_create_lctx(c
);
394 d
->input_encoding
= de_get_input_encoding(c
, NULL
, DE_ENCODING_CP437
);
395 d
->private1
= de_get_ext_option_bool(c
, "dwc:extract", 0);
397 if(!has_dwc_sig(c
)) {
398 de_err(c
, "Not a DWC file");
401 de_declare_fmt(c
, "DWC archive");
404 de_info(c
, "Note: Use \"-opt dwc:extract\" to attempt decompression "
405 "(works for most small files).");
408 de_dbg(c
, "trailer");
411 pos
= c
->infile
->len
- 27; // Position of the "trailer size" field
412 trailer_len
= de_getu16le_p(&pos
); // Usually 27
413 trailer_pos
= c
->infile
->len
- trailer_len
;
414 de_dbg(c
, "size: %"I64_FMT
" (starts at %"I64_FMT
")", trailer_len
, trailer_pos
);
415 if(trailer_len
<27 || trailer_pos
<0) {
420 fhsize
= (i64
)de_getbyte_p(&pos
);
421 de_dbg(c
, "file header entry size: %d", (int)fhsize
);
427 pos
+= 13; // TODO?: name of header file ("h" command)
428 de_arch_read_field_dttm_p(d
, &tmpts
, "archive last-modified", DE_ARCH_TSTYPE_UNIX
, &pos
);
430 nmembers
= de_getu16le_p(&pos
);
431 de_dbg(c
, "number of member files: %d", (int)nmembers
);
432 de_dbg_indent(c
, -1);
434 pos
= trailer_pos
- fhsize
*nmembers
;
439 for(i
=0; i
<nmembers
; i
++) {
440 do_dwc_member(c
, d
, pos
, fhsize
);
441 if(d
->fatalerrflag
) goto done
;
447 de_err(c
, "Bad DWC file");
449 de_arch_destroy_lctx(c
, d
);
450 de_dbg_indent_restore(c
, saved_indent_level
);
453 static int de_identify_dwc(deark
*c
)
459 if(!has_dwc_sig(c
)) return 0;
460 tsize
= de_getu16le(c
->infile
->len
-27);
461 if(tsize
<27 || tsize
>c
->infile
->len
) return 0;
462 dsize
= de_getbyte(c
->infile
->len
-25);
463 if(dsize
<30) return 0;
464 has_ext
= de_input_file_has_ext(c
, "dwc");
465 if(tsize
==27 && dsize
==34) {
466 if(has_ext
) return 100;
469 if(has_ext
) return 10;
473 static void de_help_dwc(deark
*c
)
475 de_msg(c
, "-opt dwc:extract : Try to decompress");
478 void de_module_dwc(deark
*c
, struct deark_module_info
*mi
)
481 mi
->desc
= "DWC compressed archive";
482 mi
->run_fn
= de_run_dwc
;
483 mi
->identify_fn
= de_identify_dwc
;
484 mi
->help_fn
= de_help_dwc
;
485 mi
->flags
|= DE_MODFLAG_WARNPARSEONLY
;
488 // **************************************************************************
489 // EDI Install [Pro] packed file / EDI Pack / EDI LZSS / EDI LZSSLib
490 // **************************************************************************
492 static const u8
*g_edilzss_sig
= (const u8
*)"EDILZSS";
494 static void edi_pack_decompressor_fn(struct de_arch_member_data
*md
)
496 fmtutil_decompress_lzss1(md
->c
, md
->dcmpri
, md
->dcmpro
, md
->dres
, 0x0);
499 // This basically checks for a valid DOS filename.
500 // EDI Pack is primarily a Windows 3.x format -- I'm not sure what filenames are
502 static int edi_is_filename_at(deark
*c
, de_arch_lctx
*d
, i64 pos
)
511 if(pos
+13 > c
->infile
->len
) return 0;
512 de_read(buf
, pos
, 13);
514 for(i
=0; i
<13; i
++) {
523 if(found_dot
) return 0;
526 else if(b
<33 || b
=='"' || b
=='*' || b
=='+' || b
==',' || b
=='/' ||
527 b
==':' || b
==';' || b
=='<' || b
=='=' || b
=='>' || b
=='?' ||
528 b
=='[' || b
=='\\' || b
==']' || b
=='|' || b
==127)
533 // TODO: Are capital letters allowed in this format? If not, that
534 // would be a good thing to check for.
535 if(found_dot
) ext_len
++;
540 if(!found_nul
|| base_len
<1 || base_len
>8 || ext_len
>3) return 0;
544 // Sets d->fmtver to:
545 // 0 = Not a known format
546 // 1 = EDI Pack "EDILZSS1"
547 // 2 = EDI Pack "EDILZSS2"
548 // 10 = EDI LZSSLib EDILZSSA.DLL
549 // Other formats might exist, but are unlikely to ever be supported:
550 // * EDI LZSSLib EDILZSSB.DLL
551 // * EDI LZSSLib EDILZSSC.DLL
552 static void edi_detect_fmt(deark
*c
, de_arch_lctx
*d
)
557 if(dbuf_memcmp(c
->infile
, pos
, g_edilzss_sig
, 7)) {
563 ver
= de_getbyte_p(&pos
);
565 // There's no easy way to distinguish some LZSS1 formats. This will not
567 if(edi_is_filename_at(c
, d
, pos
)) {
582 static void de_run_edi_pack(deark
*c
, de_module_params
*mparams
)
584 de_arch_lctx
*d
= NULL
;
585 struct de_arch_member_data
*md
= NULL
;
588 d
= de_arch_create_lctx(c
);
590 d
->input_encoding
= de_get_input_encoding(c
, NULL
, DE_ENCODING_WINDOWS1252
);
592 edi_detect_fmt(c
, d
);
593 if(d
->fmtver
==0) goto done
;
594 else if(d
->fmtver
==10) {
595 de_declare_fmt(c
, "EDI LZSSLib");
598 de_declare_fmtf(c
, "EDI Pack LZSS%d", d
->fmtver
);
602 md
= de_arch_create_md(c
, d
);
603 if(d
->fmtver
==1 || d
->fmtver
==2) {
604 dbuf_read_to_ucstring(c
->infile
, pos
, 12, md
->filename
, DE_CONVFLAG_STOP_AT_NUL
,
606 de_dbg(c
, "filename: \"%s\"", ucstring_getpsz_d(md
->filename
));
611 de_arch_read_field_orig_len_p(md
, &pos
);
614 if(pos
> c
->infile
->len
) {
620 md
->cmpr_len
= c
->infile
->len
- md
->cmpr_pos
;
621 md
->dfn
= edi_pack_decompressor_fn
;
622 de_arch_extract_member_file(md
);
625 de_arch_destroy_md(c
, md
);
627 de_err(c
, "Bad or unsupported EDI Pack format");
629 de_arch_destroy_lctx(c
, d
);
632 static int de_identify_edi_pack(deark
*c
)
634 if(!dbuf_memcmp(c
->infile
, 0, g_edilzss_sig
, 7)) {
638 if(v
=='1' || v
=='2') return 100;
644 void de_module_edi_pack(deark
*c
, struct deark_module_info
*mi
)
647 mi
->desc
= "EDI Install packed file";
648 mi
->run_fn
= de_run_edi_pack
;
649 mi
->identify_fn
= de_identify_edi_pack
;
652 // **************************************************************************
654 // **************************************************************************
656 // Returns 0 if no member was found at md->member_hdr_pos.
657 static int do_qip_member(deark
*c
, de_arch_lctx
*d
, struct de_arch_member_data
*md
)
659 int saved_indent_level
;
664 de_dbg_indent_save(c
, &saved_indent_level
);
665 de_dbg(c
, "member at %"I64_FMT
, md
->member_hdr_pos
);
667 pos
= md
->member_hdr_pos
;
668 if(dbuf_memcmp(c
->infile
, pos
, "QD", 2)) goto done
;
672 de_arch_read_field_cmpr_len_p(md
, &pos
);
673 index
= (UI
)de_getu16le_p(&pos
); // ?
674 de_dbg(c
, "index: %u", index
);
677 md
->crc_reported
= (u32
)de_getu32le_p(&pos
);
678 de_dbg(c
, "crc (reported): 0x%08x", (UI
)md
->crc_reported
);
681 de_arch_read_field_dos_attr_p(md
, &pos
); // ?
683 de_arch_read_field_dttm_p(d
, &md
->fi
->timestamp
[DE_TIMESTAMPIDX_MODIFY
], "mod",
684 DE_ARCH_TSTYPE_DOS_TD
, &pos
);
685 de_arch_read_field_orig_len_p(md
, &pos
);
686 dbuf_read_to_ucstring(c
->infile
, pos
, 12, md
->filename
, DE_CONVFLAG_STOP_AT_NUL
,
688 de_dbg(c
, "filename: \"%s\"", ucstring_getpsz_d(md
->filename
));
690 pos
+= 1; // Maybe to allow the name to always be NUL terminated?
693 de_dbg(c
, "cmpr data at %"I64_FMT
, md
->cmpr_pos
);
694 md
->dfn
= dclimplode_decompressor_fn
;
696 md
->validate_crc
= 1;
699 de_arch_extract_member_file(md
);
702 de_dbg_indent_restore(c
, saved_indent_level
);
706 static void qip_do_v1(deark
*c
, de_arch_lctx
*d
)
709 struct de_arch_member_data
*md
= NULL
;
711 // This version doesn't have an index, but we sort of pretend it does,
712 // so that v1 and v2 can be handled pretty much the same.
717 if(pos
+32 >= c
->infile
->len
) goto done
;
720 de_arch_destroy_md(c
, md
);
723 md
= de_arch_create_md(c
, d
);
725 md
->member_hdr_pos
= pos
;
726 cmpr_len
= de_getu32le(pos
+4);
727 if(!do_qip_member(c
, d
, md
)) {
730 pos
+= 32 + cmpr_len
;
735 de_arch_destroy_md(c
, md
);
739 static void qip_do_v2(deark
*c
, de_arch_lctx
*d
)
746 struct de_arch_member_data
*md
= NULL
;
749 d
->num_members
= de_getu16le_p(&pos
);
750 de_dbg(c
, "number of members: %"I64_FMT
, d
->num_members
);
751 index_len
= de_getu32le_p(&pos
);
752 de_dbg(c
, "index size: %"I64_FMT
, index_len
); // ??
753 d
->crco
= de_crcobj_create(c
, DE_CRCOBJ_CRC32_IEEE
);
756 de_dbg(c
, "index at %"I64_FMT
, index_pos
);
757 index_endpos
= index_pos
+index_len
;
758 if(index_endpos
> c
->infile
->len
) goto done
;
761 for(i
=0; i
<d
->num_members
; i
++) {
762 if(pos
+16 > index_endpos
) goto done
;
765 de_arch_destroy_md(c
, md
);
768 md
= de_arch_create_md(c
, d
);
770 md
->member_hdr_pos
= de_getu32le_p(&pos
);
771 (void)do_qip_member(c
, d
, md
);
777 de_arch_destroy_md(c
, md
);
781 static void de_run_qip(deark
*c
, de_module_params
*mparams
)
783 de_arch_lctx
*d
= NULL
;
787 d
= de_arch_create_lctx(c
);
789 d
->input_encoding
= de_get_input_encoding(c
, NULL
, DE_ENCODING_CP437
);
804 if(de_getbyte(8)!=0x02) {
819 de_err(c
, "Not a supported QIP format");
821 de_arch_destroy_lctx(c
, d
);
824 static int de_identify_qip(deark
*c
)
829 if(de_getbyte(0)!='Q') return 0;
832 if(de_getbyte(8)!=0x02) return 0;
834 if(n
>c
->infile
->len
) return 0;
835 if(!dbuf_memcmp(c
->infile
, n
, "QD", 2)) return 100;
838 if(de_getu16le(2)==0 &&
847 void de_module_qip(deark
*c
, struct deark_module_info
*mi
)
850 mi
->desc
= "QIP (Quarterdeck)";
851 mi
->run_fn
= de_run_qip
;
852 mi
->identify_fn
= de_identify_qip
;
855 // **************************************************************************
856 // PCX Library (by Genus Microprogramming)
857 // **************************************************************************
862 static void noncompressed_decompressor_fn(struct de_arch_member_data
*md
)
864 fmtutil_decompress_uncompressed(md
->c
, md
->dcmpri
, md
->dcmpro
, md
->dres
, 0);
867 static void read_pcxgxlib_filename(deark
*c
, de_arch_lctx
*d
, struct de_arch_member_data
*md
,
870 de_ucstring
*tmps
= NULL
;
872 tmps
= ucstring_create(c
);
873 dbuf_read_to_ucstring(c
->infile
, pos
, 8, tmps
, DE_CONVFLAG_STOP_AT_NUL
,
875 ucstring_strip_trailing_spaces(tmps
);
876 ucstring_append_ucstring(md
->filename
, tmps
);
877 ucstring_empty(tmps
);
878 dbuf_read_to_ucstring(c
->infile
, pos
+8, 4, tmps
, DE_CONVFLAG_STOP_AT_NUL
,
880 ucstring_strip_trailing_spaces(tmps
);
882 // The extension part includes the dot. If len==1, there is no extension.
883 ucstring_append_ucstring(md
->filename
, tmps
);
885 ucstring_destroy(tmps
);
888 static void do_pcxgxlib_member(deark
*c
, de_arch_lctx
*d
, struct de_arch_member_data
*md
)
890 int saved_indent_level
;
891 i64 pos
= md
->member_hdr_pos
;
893 de_dbg_indent_save(c
, &saved_indent_level
);
895 de_dbg(c
, "member file at %"I64_FMT
, md
->member_hdr_pos
);
898 if(d
->fmtcode
==FMT_PCXLIB
) {
899 pos
++; // already read
901 if(d
->fmtcode
==FMT_GXLIB
) {
902 md
->cmpr_meth
= de_getbyte_p(&pos
);
905 read_pcxgxlib_filename(c
, d
, md
, pos
);
906 de_dbg(c
, "filename: \"%s\"", ucstring_getpsz_d(md
->filename
));
909 if(d
->fmtcode
==FMT_GXLIB
) {
910 md
->cmpr_pos
= de_getu32le_p(&pos
);
911 de_dbg(c
, "cmpr. data pos: %"I64_FMT
, md
->cmpr_pos
);
914 de_arch_read_field_cmpr_len_p(md
, &pos
);
916 de_arch_read_field_dttm_p(d
, &md
->fi
->timestamp
[DE_TIMESTAMPIDX_MODIFY
], "mod",
917 DE_ARCH_TSTYPE_DOS_DT
, &pos
);
919 if(d
->fmtcode
==FMT_PCXLIB
) {
920 md
->cmpr_meth
= (UI
)de_getu16le_p(&pos
);
923 de_dbg(c
, "packing type: %u", (UI
)md
->cmpr_meth
);
924 if(md
->cmpr_meth
==0) {
925 md
->orig_len
= md
->cmpr_len
;
926 md
->orig_len_known
= 1;
929 de_err(c
, "Unsupported compression: %u", (UI
)md
->cmpr_meth
);
933 if(d
->fmtcode
==FMT_PCXLIB
) {
938 if(d
->fmtcode
==FMT_PCXLIB
) {
942 if(!de_arch_good_cmpr_data_pos(md
)) {
947 if(d
->fmtcode
==FMT_GXLIB
) {
948 md
->member_total_size
= pos
- md
->member_hdr_pos
;
951 md
->member_total_size
= md
->cmpr_pos
+ md
->cmpr_len
- md
->member_hdr_pos
;
954 md
->dfn
= noncompressed_decompressor_fn
;
955 de_arch_extract_member_file(md
);
958 de_dbg_indent_restore(c
, saved_indent_level
);
961 static void do_pcxgxlib_main(deark
*c
, de_module_params
*mparams
, UI fmtcode
)
964 i64 member_count
= 0;
965 struct de_arch_member_data
*md
= NULL
;
966 de_arch_lctx
*d
= NULL
;
968 d
= de_arch_create_lctx(c
);
969 d
->fmtcode
= fmtcode
;
971 d
->input_encoding
= de_get_input_encoding(c
, NULL
, DE_ENCODING_CP437
);
973 if(d
->fmtcode
==FMT_GXLIB
) {
975 pos
+= 50; // copyright message
976 d
->fmtver
= (int)de_getu16le_p(&pos
);
977 de_dbg(c
, "gxLib ver: %d", d
->fmtver
);
979 d
->num_members
= de_getu16le_p(&pos
);
980 de_dbg(c
, "number of members: %"I64_FMT
, d
->num_members
);
986 d
->fmtver
= (int)de_getu16le_p(&pos
);
987 de_dbg(c
, "pcxLib ver: %d", d
->fmtver
);
988 pos
+= 40; // TODO: volume label
993 if(pos
>= c
->infile
->len
) goto done
;
995 if(d
->fmtcode
==FMT_GXLIB
) {
996 if(member_count
>= d
->num_members
) goto done
;
999 if(d
->fmtcode
==FMT_PCXLIB
) {
1002 b
= de_getbyte(pos
);
1003 if(b
!= 0x01) goto done
;
1007 de_arch_destroy_md(c
, md
);
1010 md
= de_arch_create_md(c
, d
);
1011 md
->member_hdr_pos
= pos
;
1012 do_pcxgxlib_member(c
, d
, md
);
1013 if(d
->fatalerrflag
) goto done
;
1014 if(md
->member_total_size
<1) goto done
;
1015 pos
+= md
->member_total_size
;
1021 de_arch_destroy_md(c
, md
);
1023 de_arch_destroy_lctx(c
, d
);
1026 static void de_run_pcxlib(deark
*c
, de_module_params
*mparams
)
1028 do_pcxgxlib_main(c
, mparams
, FMT_PCXLIB
);
1031 static int de_identify_pcxlib(deark
*c
)
1033 if(!dbuf_memcmp(c
->infile
, 0, "pcxLib\0", 7)) return 100;
1037 void de_module_pcxlib(deark
*c
, struct deark_module_info
*mi
)
1040 mi
->desc
= "PCX Library";
1041 mi
->run_fn
= de_run_pcxlib
;
1042 mi
->identify_fn
= de_identify_pcxlib
;
1045 // **************************************************************************
1046 // GX Library / Genus Graphics Library
1047 // **************************************************************************
1049 static void de_run_gxlib(deark
*c
, de_module_params
*mparams
)
1051 do_pcxgxlib_main(c
, mparams
, FMT_GXLIB
);
1054 static int de_identify_gxlib(deark
*c
)
1057 u8 has_copyr
, has_ver
;
1059 n
= (UI
)de_getu16be(0);
1060 if(n
!=0x01ca) return 0;
1061 has_copyr
= !dbuf_memcmp(c
->infile
, 2, "Copyri", 6);
1062 n
= (UI
)de_getu16le(52);
1064 if(has_copyr
&& has_ver
) return 100;
1065 if(has_copyr
|| has_ver
) return 25;
1069 void de_module_gxlib(deark
*c
, struct deark_module_info
*mi
)
1072 mi
->desc
= "GX Library";
1073 mi
->run_fn
= de_run_gxlib
;
1074 mi
->identify_fn
= de_identify_gxlib
;
1077 // **************************************************************************
1079 // **************************************************************************
1081 #define MDCD_MINHEADERLEN 54
1083 static int mdcd_sig_at(deark
*c
, i64 pos
)
1085 return !dbuf_memcmp(c
->infile
, pos
, (const void*)"MDmd", 4);
1088 static void mdcd_decompressor_fn(struct de_arch_member_data
*md
)
1092 if(md
->cmpr_meth
==1) {
1093 struct de_lzw_params delzwp
;
1095 de_zeromem(&delzwp
, sizeof(struct de_lzw_params
));
1096 delzwp
.fmt
= DE_LZWFMT_ZOOLZD
;
1097 fmtutil_decompress_lzw(c
, md
->dcmpri
, md
->dcmpro
, md
->dres
, &delzwp
);
1099 else if(md
->cmpr_meth
==0) {
1100 fmtutil_decompress_uncompressed(c
, md
->dcmpri
, md
->dcmpro
, md
->dres
, 0);
1103 de_dfilter_set_generic_error(c
, md
->dres
, NULL
);
1107 // Returns 0 if no member was found at md->member_hdr_pos.
1108 static int do_mdcd_member(deark
*c
, de_arch_lctx
*d
, struct de_arch_member_data
*md
)
1114 int saved_indent_level
;
1119 de_dbg_indent_save(c
, &saved_indent_level
);
1121 // Note: For info about the MDCD header format, see MDCD.PAS, near the
1122 // "FileHeader = Record" line.
1124 pos
= md
->member_hdr_pos
;
1125 if(!mdcd_sig_at(c
, pos
)) {
1126 if(md
->member_hdr_pos
==0) {
1127 de_err(c
, "Not an MDCD file");
1130 de_dbg(c
, "[member not found at %"I64_FMT
"]", pos
);
1136 de_dbg(c
, "member at %"I64_FMT
, md
->member_hdr_pos
);
1137 de_dbg_indent(c
, 1);
1138 pos
++; // software version?
1139 hdrtype
= de_getbyte_p(&pos
);
1140 de_dbg(c
, "header type: %u", (UI
)hdrtype
);
1145 hdrlen
= de_getu16le_p(&pos
);
1146 de_dbg(c
, "header len: %"I64_FMT
, hdrlen
);
1147 if(hdrlen
<MDCD_MINHEADERLEN
) {
1152 pos
+= 16; // various
1153 md
->cmpr_meth
= (UI
)de_getbyte_p(&pos
);
1154 de_dbg(c
, "cmpr. method: %u", md
->cmpr_meth
);
1155 de_arch_read_field_orig_len_p(md
, &pos
);
1156 de_arch_read_field_cmpr_len_p(md
, &pos
);
1158 md
->cmpr_pos
= md
->member_hdr_pos
+ hdrlen
;
1159 // TODO: If we can set the filename first, we should.
1160 if(!de_arch_good_cmpr_data_pos(md
)) {
1163 md
->member_total_size
= hdrlen
+ md
->cmpr_len
;
1166 attr
= (UI
)de_getu16le_p(&pos
);
1167 de_arch_handle_field_dos_attr(md
, attr
);
1169 de_arch_read_field_dttm_p(d
, &md
->fi
->timestamp
[DE_TIMESTAMPIDX_MODIFY
], "mod",
1170 DE_ARCH_TSTYPE_DOS_TD
, &pos
);
1172 md
->crc_reported
= (u32
)de_getu16le_p(&pos
);
1173 de_dbg(c
, "crc (reported): 0x%04x", (UI
)md
->crc_reported
);
1175 s_len
= de_getbyte_p(&pos
);
1180 md
->tmpfn_base
= ucstring_create(c
);
1181 dbuf_read_to_ucstring(c
->infile
, pos
, s_len
, md
->tmpfn_base
, 0, d
->input_encoding
);
1182 de_dbg(c
, "filename: \"%s\"", ucstring_getpsz_d(md
->tmpfn_base
));
1186 s_len
= de_getbyte_p(&pos
);
1191 md
->tmpfn_path
= ucstring_create(c
);
1192 dbuf_read_to_ucstring(c
->infile
, pos
, s_len
, md
->tmpfn_path
, 0, d
->input_encoding
);
1193 de_dbg(c
, "path: \"%s\"", ucstring_getpsz_d(md
->tmpfn_path
));
1195 // Ignore paths that look like absolute paths. Not sure what to do with them.
1196 have_path
= ucstring_isnonempty(md
->tmpfn_path
);
1198 if(have_path
&& md
->tmpfn_path
->len
>= 1 && (md
->tmpfn_path
->str
[0]=='\\' ||
1199 md
->tmpfn_path
->str
[0]=='/'))
1203 if(have_path
&& md
->tmpfn_path
->len
>= 2 && md
->tmpfn_path
->str
[1]==':') {
1208 de_arch_fixup_path(md
->tmpfn_path
, 0x1);
1209 ucstring_append_ucstring(md
->filename
, md
->tmpfn_path
);
1210 md
->set_name_flags
|= DE_SNFLAG_FULLPATH
;
1212 ucstring_append_ucstring(md
->filename
, md
->tmpfn_base
);
1214 de_dbg(c
, "compressed data at %"I64_FMT
", len=%"I64_FMT
, md
->cmpr_pos
, md
->cmpr_len
);
1216 if(md
->cmpr_meth
>1) {
1217 de_err(c
, "Unsupported compression: %u", (UI
)md
->cmpr_meth
);
1221 md
->dfn
= mdcd_decompressor_fn
;
1223 md
->validate_crc
= 1;
1224 // When extracting, MDCD (1.0) does not validate the CRC of files that were
1225 // stored uncompressed. Some files seem to exploit this, and set the CRC to
1226 // 0, so we tolerate it with a warning.
1227 if(md
->crc_reported
==0 && md
->cmpr_meth
==0) {
1228 md
->behavior_on_wrong_crc
= 1;
1231 de_arch_extract_member_file(md
);
1234 de_dbg_indent_restore(c
, saved_indent_level
);
1238 static void de_run_mdcd(deark
*c
, de_module_params
*mparams
)
1240 de_arch_lctx
*d
= NULL
;
1242 struct de_arch_member_data
*md
= NULL
;
1244 d
= de_arch_create_lctx(c
);
1246 d
->input_encoding
= de_get_input_encoding(c
, NULL
, DE_ENCODING_CP437
);
1247 d
->crco
= de_crcobj_create(c
, DE_CRCOBJ_CRC16_XMODEM
);
1250 if(pos
+MDCD_MINHEADERLEN
>= c
->infile
->len
) goto done
;
1253 de_arch_destroy_md(c
, md
);
1256 md
= de_arch_create_md(c
, d
);
1257 md
->member_hdr_pos
= pos
;
1258 if(!do_mdcd_member(c
, d
, md
)) goto done
;
1259 if(md
->member_total_size
<=0) goto done
;
1260 pos
+= md
->member_total_size
;
1265 de_arch_destroy_md(c
, md
);
1268 if(d
->need_errmsg
) {
1269 de_err(c
, "Bad or unsupported MDCD file");
1271 de_arch_destroy_lctx(c
, d
);
1275 static int de_identify_mdcd(deark
*c
)
1277 if(mdcd_sig_at(c
, 0)) {
1283 void de_module_mdcd(deark
*c
, struct deark_module_info
*mi
)
1286 mi
->desc
= "MDCD archive";
1287 mi
->run_fn
= de_run_mdcd
;
1288 mi
->identify_fn
= de_identify_mdcd
;
1291 // **************************************************************************
1293 // **************************************************************************
1295 static void de_run_cazip(deark
*c
, de_module_params
*mparams
)
1297 de_arch_lctx
*d
= NULL
;
1298 struct de_arch_member_data
*md
= NULL
;
1300 UI verfield
, field10
, field12
, field18
;
1302 d
= de_arch_create_lctx(c
);
1304 d
->crco
= de_crcobj_create(c
, DE_CRCOBJ_CRC32_IEEE
);
1306 md
= de_arch_create_md(c
, d
);
1307 //d->input_encoding = de_get_input_encoding(c, NULL, DE_ENCODING_CP437);
1310 verfield
= (UI
)de_getu16be_p(&pos
);
1311 field10
= (UI
)de_getu16le_p(&pos
);
1312 field12
= (UI
)de_getu16le_p(&pos
);
1314 md
->crc_reported
= (u32
)de_getu32le_p(&pos
);
1315 de_dbg(c
, "crc (reported): 0x%08x", (UI
)md
->crc_reported
);
1317 field18
= (UI
)de_getu16le_p(&pos
);
1319 if(verfield
!=0x3333 || field10
!=1 || field12
!=1 || field18
!=0) {
1320 de_warn(c
, "This version of CAZIP file might not be handled correctly");
1324 md
->cmpr_len
= c
->infile
->len
- md
->cmpr_pos
;
1325 if(!de_arch_good_cmpr_data_pos(md
)) {
1329 md
->dfn
= dclimplode_decompressor_fn
;
1330 md
->validate_crc
= 1;
1331 de_arch_extract_member_file(md
);
1335 de_arch_destroy_md(c
, md
);
1339 de_arch_destroy_lctx(c
, d
);
1344 static int de_identify_cazip(deark
*c
)
1346 if(dbuf_memcmp(c
->infile
, 0, (const void*)"\x0d\x0a\x1a" "CAZIP", 8)) {
1353 void de_module_cazip(deark
*c
, struct deark_module_info
*mi
)
1356 mi
->desc
= "CAZIP compressed file";
1357 mi
->run_fn
= de_run_cazip
;
1358 mi
->identify_fn
= de_identify_cazip
;
1361 // **************************************************************************
1362 // CMZ (Ami Pro installer archive)
1363 // **************************************************************************
1365 #define CMZ_MINHEADERLEN 21
1367 static int cmz_sig_at(deark
*c
, i64 pos
)
1369 return !dbuf_memcmp(c
->infile
, pos
, (const void*)"Clay", 4);
1372 // Returns 0 if no member was found at md->member_hdr_pos.
1373 static int do_cmz_member(deark
*c
, de_arch_lctx
*d
, struct de_arch_member_data
*md
)
1377 int saved_indent_level
;
1380 de_dbg_indent_save(c
, &saved_indent_level
);
1382 pos
= md
->member_hdr_pos
;
1383 if(!cmz_sig_at(c
, pos
)) {
1384 if(md
->member_hdr_pos
==0) {
1385 de_err(c
, "Not a CMZ file");
1388 de_err(c
, "Bad data found at %"I64_FMT
, pos
);
1394 de_dbg(c
, "member at %"I64_FMT
, md
->member_hdr_pos
);
1395 de_dbg_indent(c
, 1);
1397 de_arch_read_field_cmpr_len_p(md
, &pos
);
1398 de_arch_read_field_orig_len_p(md
, &pos
);
1399 de_arch_read_field_dttm_p(d
, &md
->fi
->timestamp
[DE_TIMESTAMPIDX_MODIFY
], "mod",
1400 DE_ARCH_TSTYPE_DOS_DT
, &pos
);
1402 namelen
= de_getu16le_p(&pos
);
1407 pos
+= 2; // Unknown field (flags?)
1409 dbuf_read_to_ucstring(c
->infile
, pos
, namelen
, md
->filename
, 0, d
->input_encoding
);
1410 de_dbg(c
, "filename: \"%s\"", ucstring_getpsz_d(md
->filename
));
1414 de_dbg(c
, "compressed data at %"I64_FMT
", len=%"I64_FMT
, md
->cmpr_pos
, md
->cmpr_len
);
1415 if(!de_arch_good_cmpr_data_pos(md
)) {
1419 md
->member_total_size
= md
->cmpr_pos
+ md
->cmpr_len
- md
->member_hdr_pos
;
1422 md
->dfn
= dclimplode_decompressor_fn
;
1423 de_arch_extract_member_file(md
);
1426 de_dbg_indent_restore(c
, saved_indent_level
);
1430 static void de_run_cmz(deark
*c
, de_module_params
*mparams
)
1432 de_arch_lctx
*d
= NULL
;
1434 struct de_arch_member_data
*md
= NULL
;
1436 d
= de_arch_create_lctx(c
);
1438 d
->input_encoding
= de_get_input_encoding(c
, NULL
, DE_ENCODING_CP437
);
1441 if(pos
+CMZ_MINHEADERLEN
>= c
->infile
->len
) goto done
;
1444 de_arch_destroy_md(c
, md
);
1447 md
= de_arch_create_md(c
, d
);
1448 md
->member_hdr_pos
= pos
;
1449 if(!do_cmz_member(c
, d
, md
)) goto done
;
1450 if(md
->member_total_size
<=0) goto done
;
1451 pos
+= md
->member_total_size
;
1456 de_arch_destroy_md(c
, md
);
1460 if(d
->need_errmsg
) {
1461 de_err(c
, "Bad or unsupported CMZ file");
1463 de_arch_destroy_lctx(c
, d
);
1467 static int de_identify_cmz(deark
*c
)
1472 if(!cmz_sig_at(c
, 0)) {
1476 cmpr_len
= de_getu32le(4);
1477 name_len
= de_getu16le(16);
1478 if(cmpr_len
==0 && name_len
>0) {
1481 if(dclimplode_header_at(c
, 20+name_len
)) {
1488 void de_module_cmz(deark
*c
, struct deark_module_info
*mi
)
1491 mi
->desc
= "CMZ installer archive";
1492 mi
->run_fn
= de_run_cmz
;
1493 mi
->identify_fn
= de_identify_cmz
;
1496 // **************************************************************************
1497 // SHR - PC-Install "PC-Shrink" format
1498 // **************************************************************************
1500 #define PCSHRINK_MINHEADERLEN 48
1506 static int detect_pcshrink_internal(deark
*c
, u8
*pmultipart_flag
)
1512 *pmultipart_flag
= 0;
1514 if(b
==0x74) { // maybe old format
1515 if(de_getbyte(0)!=0) return 0;
1516 if(de_getbyte(16)!=0x74) return 0;
1517 if(de_getbyte(56)==0) return 0; // 1st byte of filename
1518 cmpr_len
= de_getu32le(76);
1520 if(!dclimplode_header_at(c
, 104)) return 0;
1524 else if(b
==0) { // maybe new format
1525 if(de_getu16le(14)!=0x74) return 0;
1526 n
= (UI
)de_getu16le(18);
1528 *pmultipart_flag
= 1;
1534 if(de_getbyte(58)==0) return 0; // 1st byte of filename
1535 cmpr_len
= de_getu32le(194);
1537 if(!dclimplode_header_at(c
, 226)) return 0;
1540 if(de_getbyte(0)!=0) {
1541 *pmultipart_flag
= 1;
1548 static int do_pcshrink_member(deark
*c
, de_arch_lctx
*d
, struct de_arch_member_data
*md
)
1551 int saved_indent_level
;
1555 de_dbg_indent_save(c
, &saved_indent_level
);
1556 pos
= md
->member_hdr_pos
;
1557 de_dbg(c
, "member at %"I64_FMT
, md
->member_hdr_pos
);
1558 de_dbg_indent(c
, 1);
1566 dbuf_read_to_ucstring(c
->infile
, pos
, fnfieldlen
, md
->filename
,
1567 DE_CONVFLAG_STOP_AT_NUL
, d
->input_encoding
);
1568 de_dbg(c
, "filename: \"%s\"", ucstring_getpsz_d(md
->filename
));
1571 de_arch_fixup_path(md
->filename
, 0);
1572 md
->set_name_flags
|= DE_SNFLAG_FULLPATH
;
1576 pos
++; // attributes?
1585 de_arch_read_field_cmpr_len_p(md
, &pos
);
1588 de_arch_read_field_dttm_p(d
, &md
->fi
->timestamp
[DE_TIMESTAMPIDX_MODIFY
], "mod",
1589 DE_ARCH_TSTYPE_DOS_DXT
, &pos
);
1592 de_arch_read_field_dttm_p(d
, &md
->fi
->timestamp
[DE_TIMESTAMPIDX_MODIFY
], "mod",
1593 DE_ARCH_TSTYPE_DOS_DT
, &pos
);
1597 md
->cmpr_pos
= md
->member_hdr_pos
+ 168;
1600 md
->cmpr_pos
= md
->member_hdr_pos
+ 48;
1603 de_dbg(c
, "compressed data at %"I64_FMT
", len=%"I64_FMT
, md
->cmpr_pos
, md
->cmpr_len
);
1604 if(!de_arch_good_cmpr_data_pos(md
)) {
1608 md
->member_total_size
= md
->cmpr_pos
+ md
->cmpr_len
- md
->member_hdr_pos
;
1611 md
->dfn
= dclimplode_decompressor_fn
;
1612 de_arch_extract_member_file(md
);
1615 de_dbg_indent_restore(c
, saved_indent_level
);
1619 static void de_run_pcshrink(deark
*c
, de_module_params
*mparams
)
1621 de_arch_lctx
*d
= NULL
;
1625 struct de_arch_member_data
*md
= NULL
;
1627 d
= de_arch_create_lctx(c
);
1631 d
->fmtver
= detect_pcshrink_internal(c
, &multipart_flag
);
1632 if(d
->fmtver
!=1 && d
->fmtver
!=2) {
1633 de_err(c
, "Not a PC-Shrink file");
1636 de_dbg(c
, "format version: %d", d
->fmtver
);
1638 // Read archive header
1640 d
->num_members
= de_getu16le(16);
1643 d
->num_members
= de_getu16le(14);
1645 de_dbg(c
, "number of members: %d", (int)d
->num_members
);
1647 // TODO: Can we tell Windows files from DOS files?
1648 d
->input_encoding
= de_get_input_encoding(c
, NULL
, DE_ENCODING_CP437
);
1657 if(multipart_flag
) {
1658 de_err(c
, "Multi-part PC-Shrink files are not supported");
1662 for(idx
=0; idx
<d
->num_members
; idx
++) {
1663 if(pos
+PCSHRINK_MINHEADERLEN
>= c
->infile
->len
) {
1664 de_err(c
, "Unexpected end of file");
1669 de_arch_destroy_md(c
, md
);
1672 md
= de_arch_create_md(c
, d
);
1673 md
->member_hdr_pos
= pos
;
1674 if(!do_pcshrink_member(c
, d
, md
)) goto done
;
1675 if(md
->member_total_size
<=0) goto done
;
1676 pos
+= md
->member_total_size
;
1681 de_arch_destroy_md(c
, md
);
1685 de_arch_destroy_lctx(c
, d
);
1689 static int de_identify_pcshrink(deark
*c
)
1694 ver
= detect_pcshrink_internal(c
, &multipart_flag
);
1699 if(multipart_flag
) return 40;
1706 void de_module_pcshrink(deark
*c
, struct deark_module_info
*mi
)
1708 mi
->id
= "pcshrink";
1709 mi
->desc
= "PC-Install compressed archive";
1710 mi
->run_fn
= de_run_pcshrink
;
1711 mi
->identify_fn
= de_identify_pcshrink
;
1714 // **************************************************************************
1715 // ARCV - Eschalon Setup / EDI Install
1716 // **************************************************************************
1718 // Warning: This ARCV code is not based on any specification. It may be wrong
1721 // This format was popular enough that I wanted to at least parse it, but I'm
1722 // not optimistic about figuring out how to decompress it.
1723 // Initial testing suggests LZ77 with a 4k window, possibly with adaptive
1724 // Huffman coding or arithmetic coding. But it's not LZHUF or LZARI.
1726 #define CODE_ARCV 0x41524356U
1727 #define CODE_BLCK 0x424c434bU
1728 #define CODE_CHNK 0x43484e4bU
1730 static void do_arcv_common_fields(deark
*c
, de_arch_lctx
*d
,
1731 struct de_arch_member_data
*md
, i64 pos1
, i64
*nbytes_consumed
)
1734 UI fver_maj1
, fver_min1
;
1735 UI fver_maj2
, fver_min2
;
1739 fnlen
= (i64
)de_getbyte_p(&pos
);
1740 dbuf_read_to_ucstring(c
->infile
, pos
, fnlen
, md
->filename
, 0,
1742 de_dbg(c
, "filename: \"%s\"", ucstring_getpsz_d(md
->filename
));
1745 de_arch_read_field_orig_len_p(md
, &pos
);
1746 de_arch_read_field_cmpr_len_p(md
, &pos
);
1748 attr
= (UI
)de_getu32le_p(&pos
); // TODO: How big is this field
1749 de_arch_handle_field_dos_attr(md
, attr
);
1751 de_arch_read_field_dttm_p(d
, &md
->fi
->timestamp
[DE_TIMESTAMPIDX_MODIFY
], "mod",
1752 DE_ARCH_TSTYPE_DOS_TD
, &pos
);
1754 fver_min1
= (UI
)de_getu16le_p(&pos
);
1755 fver_maj1
= (UI
)de_getu16le_p(&pos
);
1756 de_dbg(c
, "file ver (1): %u.%u", fver_maj1
, fver_min1
);
1757 fver_min2
= (UI
)de_getu16le_p(&pos
);
1758 fver_maj2
= (UI
)de_getu16le_p(&pos
);
1759 de_dbg(c
, "file ver (2): %u.%u", fver_maj2
, fver_min2
);
1761 // Algorithm seems to be "CRC-32/JAMCRC".
1762 // Same as the usual CRC-32, except it doesn't invert the bits as a final step.
1763 md
->crc_reported
= (u32
)de_getu32le_p(&pos
);
1764 de_dbg(c
, "crc (reported): 0x%08x", (UI
)md
->crc_reported
);
1766 *nbytes_consumed
= pos
- pos1
;
1769 static void do_arcv_v1(deark
*c
, de_arch_lctx
*d
)
1771 struct de_arch_member_data
*md
= NULL
;
1777 i64 nbytes_consumed
= 0;
1779 int saved_indent_level
;
1781 de_dbg_indent_save(c
, &saved_indent_level
);
1782 md
= de_arch_create_md(c
, d
);
1785 arcv_hdr_len
= de_getu16le_p(&pos
);
1786 de_dbg(c
, "arcv hdr len: %"I64_FMT
, arcv_hdr_len
);
1787 d
->archive_flags
= (UI
)de_getu32le_p(&pos
);
1788 de_dbg(c
, "flags: 0x%08x", (UI
)d
->archive_flags
);
1790 do_arcv_common_fields(c
, d
, md
, pos
, &nbytes_consumed
);
1792 pos
= arcv_hdr_len
; // Seek to the CHNK segment
1794 id
= (u32
)de_getu32be_p(&pos
);
1795 if(id
!= CODE_CHNK
) {
1799 pos
+= 2; // segment version number?
1800 chnk_hdr_len
= de_getu16le_p(&pos
);
1801 de_dbg(c
, "chnk header len: %"I64_FMT
, chnk_hdr_len
);
1803 chnk_dlen
= de_getu32le_p(&pos
);
1804 md
->cmpr_pos
= chnk_pos
+ chnk_hdr_len
;
1805 de_dbg(c
, "file data at %"I64_FMT
", len=%"I64_FMT
, md
->cmpr_pos
, chnk_dlen
);
1808 de_dbg_indent_restore(c
, saved_indent_level
);
1809 de_arch_destroy_md(c
, md
);
1812 static void do_arcv_v2(deark
*c
, de_arch_lctx
*d
)
1819 struct de_arch_member_data
*md
= NULL
;
1820 int saved_indent_level
;
1822 de_dbg_indent_save(c
, &saved_indent_level
);
1824 arcv_hdr_len
= de_getu16le(6);
1825 de_dbg(c
, "arcv hdr len: %"I64_FMT
, arcv_hdr_len
);
1829 i64 nbytes_consumed
= 0;
1831 if(pos
>= c
->infile
->len
) {
1834 de_dbg(c
, "member at %"I64_FMT
, pos
);
1837 de_arch_destroy_md(c
, md
);
1840 md
= de_arch_create_md(c
, d
);
1841 md
->member_hdr_pos
= pos
;
1843 de_dbg_indent(c
, 1);
1844 id
= (u32
)de_getu32be_p(&pos
);
1846 de_dbg(c
, "can't find item at %"I64_FMT
, md
->member_hdr_pos
);
1849 pos
+= 2; // format version?
1850 blck_hdr_len
= de_getu16le_p(&pos
);
1851 de_dbg(c
, "block hdr len: %"I64_FMT
, blck_hdr_len
);
1854 blck_dlen
= de_getu32le_p(&pos
);
1855 de_dbg(c
, "block dlen: %"I64_FMT
, blck_dlen
);
1857 pos
= md
->member_hdr_pos
+16;
1859 do_arcv_common_fields(c
, d
, md
, pos
, &nbytes_consumed
);
1860 pos
+= nbytes_consumed
;
1863 de_dbg(c
, "file data at %"I64_FMT
", len=%"I64_FMT
, md
->cmpr_pos
, md
->cmpr_len
);
1865 pos
= md
->member_hdr_pos
+ blck_hdr_len
+ blck_dlen
;
1866 de_dbg_indent(c
, -1);
1870 de_dbg_indent_restore(c
, saved_indent_level
);
1872 de_arch_destroy_md(c
, md
);
1876 static void de_run_arcv(deark
*c
, de_module_params
*mparams
)
1878 de_arch_lctx
*d
= NULL
;
1881 d
= de_arch_create_lctx(c
);
1883 d
->input_encoding
= de_get_input_encoding(c
, NULL
, DE_ENCODING_WINDOWS1252
);
1885 d
->fmtver
= (int)de_getu16le(4);
1886 ver_maj
= (UI
)d
->fmtver
>> 8;
1887 de_dbg(c
, "format ver: 0x%04x", (UI
)d
->fmtver
);
1891 else if(ver_maj
==2) {
1901 if(d
->need_errmsg
) {
1902 de_err(c
, "Bad or unsupported ARCV file");
1904 de_arch_destroy_lctx(c
, d
);
1908 static int de_identify_arcv(deark
*c
)
1912 if((UI
)de_getu32be(0) != CODE_ARCV
) return 0;
1913 ver
= de_getbyte(5);
1914 if(ver
==1 || ver
==2) {
1920 void de_module_arcv(deark
*c
, struct deark_module_info
*mi
)
1923 mi
->desc
= "ARCV installer archive";
1924 mi
->run_fn
= de_run_arcv
;
1925 mi
->identify_fn
= de_identify_arcv
;
1926 mi
->flags
|= DE_MODFLAG_WARNPARSEONLY
;
1929 // **************************************************************************
1930 // Knowledge Dynamics .RED (including newer .LIF files)
1931 // **************************************************************************
1933 static void de_run_red(deark
*c
, de_module_params
*mparams
)
1935 de_arch_lctx
*d
= NULL
;
1938 struct de_arch_member_data
*md
= NULL
;
1939 int saved_indent_level
;
1941 de_dbg_indent_save(c
, &saved_indent_level
);
1942 d
= de_arch_create_lctx(c
);
1944 d
->input_encoding
= de_get_input_encoding(c
, NULL
, DE_ENCODING_CP437
);
1949 if(pos
>= c
->infile
->len
) goto done
;
1951 de_arch_destroy_md(c
, md
);
1954 md
= de_arch_create_md(c
, d
);
1955 md
->member_hdr_pos
= pos
;
1957 id
= (UI
)de_getu16be_p(&pos
);
1958 b
= de_getbyte_p(&pos
); // Format version? Always 1.
1959 md
->member_hdr_size
= (i64
)de_getbyte_p(&pos
); // Always 41?
1960 if(id
!=0x5252U
|| b
!=0x01 || md
->member_hdr_size
<39) {
1961 de_err(c
, "Member not found at %"I64_FMT
, md
->member_hdr_pos
);
1965 de_dbg(c
, "member at %"I64_FMT
, md
->member_hdr_pos
);
1966 de_dbg_indent(c
, 1);
1968 de_arch_read_field_dttm_p(d
, &md
->fi
->timestamp
[DE_TIMESTAMPIDX_MODIFY
], "mod",
1969 DE_ARCH_TSTYPE_DOS_TD
, &pos
);
1970 de_arch_read_field_cmpr_len_p(md
, &pos
);
1971 de_arch_read_field_orig_len_p(md
, &pos
);
1973 pos
= md
->member_hdr_pos
+ 26;
1974 dbuf_read_to_ucstring(c
->infile
, pos
, 12, md
->filename
, DE_CONVFLAG_STOP_AT_NUL
,
1976 de_dbg(c
, "filename: \"%s\"", ucstring_getpsz_d(md
->filename
));
1977 // Filename field is 13 bytes.
1978 // Then a 2-byte field unidentified field.
1980 md
->cmpr_pos
= md
->member_hdr_pos
+ md
->member_hdr_size
;
1981 de_dbg(c
, "compressed data at %"I64_FMT
", len=%"I64_FMT
, md
->cmpr_pos
, md
->cmpr_len
);
1983 de_dbg_indent(c
, -1);
1984 pos
= md
->cmpr_pos
+ md
->cmpr_len
;
1988 de_dbg_indent_restore(c
, saved_indent_level
);
1990 de_arch_destroy_md(c
, md
);
1994 if(d
->need_errmsg
) {
1995 de_err(c
, "Bad or unsupported RED file");
1997 de_arch_destroy_lctx(c
, d
);
2001 static int de_identify_red(deark
*c
)
2003 if((UI
)de_getu32be(0) != 0x52520129U
) return 0;
2007 void de_module_red(deark
*c
, struct deark_module_info
*mi
)
2010 mi
->desc
= "RED installer archive (Knowledge Dynamics Corp)";
2011 mi
->run_fn
= de_run_red
;
2012 mi
->identify_fn
= de_identify_red
;
2013 mi
->flags
|= DE_MODFLAG_WARNPARSEONLY
;
2016 // **************************************************************************
2017 // Knowledge Dynamics .LIF (old format)
2018 // **************************************************************************
2020 // It's ugly to have two different ways of reading these ASCII-encoded-hex-
2021 // digits fields. But the needs of the 'identify' phase, and the 'run' phase,
2022 // are different enough that it's how I've chosen to do it.
2024 static i64
lif_read_field(dbuf
*f
, i64 pos1
, i64 len
, int *perrflag
)
2030 for(i
=0; i
<len
; i
++) {
2034 b
= dbuf_getbyte_p(f
, &pos
);
2035 if(b
>='0' && b
<='9') {
2038 else if(b
>='a' && b
<='f') {
2046 val
= (val
<<4) | nv
;
2051 static int lif_kdc_convert_hdr(deark
*c
, i64 pos1
, dbuf
*f2
)
2057 for(i
=0; i
<17; i
++) {
2061 b0
= de_getbyte_p(&pos
);
2062 b1
= de_getbyte_p(&pos
);
2063 x0
= de_decode_hex_digit(b0
, &errorflag
);
2064 if(errorflag
) return 0;
2065 x1
= de_decode_hex_digit(b1
, &errorflag
);
2066 if(errorflag
) return 0;
2067 dbuf_writebyte(f2
, (u8
)((x0
<<4)|x1
));
2072 static void lif_method2_decompressor_fn(struct de_arch_member_data
*md
)
2075 struct de_lzw_params delzwp
;
2077 de_zeromem(&delzwp
, sizeof(struct de_lzw_params
));
2078 delzwp
.fmt
= DE_LZWFMT_ZOOLZD
;
2079 fmtutil_decompress_lzw(c
, md
->dcmpri
, md
->dcmpro
, md
->dres
, &delzwp
);
2082 static void de_run_lif_kdc(deark
*c
, de_module_params
*mparams
)
2084 de_arch_lctx
*d
= NULL
;
2086 struct de_arch_member_data
*md
= NULL
;
2088 int saved_indent_level
;
2090 de_dbg_indent_save(c
, &saved_indent_level
);
2091 d
= de_arch_create_lctx(c
);
2093 d
->input_encoding
= de_get_input_encoding(c
, NULL
, DE_ENCODING_CP437
);
2094 d
->crco
= de_crcobj_create(c
, DE_CRCOBJ_CRC16_IBM3740
);
2095 f2
= dbuf_create_membuf(c
, 17, 0);
2101 if(pos
>= c
->infile
->len
) goto done
;
2103 de_arch_destroy_md(c
, md
);
2108 // Decode the hex-encoded part of the header, so that we can read it
2110 if(!lif_kdc_convert_hdr(c
, pos
, f2
)) {
2115 md
= de_arch_create_md(c
, d
);
2116 md
->member_hdr_pos
= pos
;
2117 md
->member_hdr_size
= 54;
2119 de_dbg(c
, "member at %"I64_FMT
, md
->member_hdr_pos
);
2120 de_dbg_indent(c
, 1);
2125 de_arch_read_field_dttm_p(d
, &md
->fi
->timestamp
[DE_TIMESTAMPIDX_MODIFY
], "mod",
2126 DE_ARCH_TSTYPE_DOS_DT
, &f2_pos
);
2127 de_arch_read_field_cmpr_len_p(md
, &f2_pos
);
2128 de_arch_read_field_orig_len_p(md
, &f2_pos
);
2130 crc1_reported
= (u32
)dbuf_getu16be_p(f2
, &f2_pos
);
2131 de_dbg(c
, "crc of cmpr. data (reported): 0x%04x", (UI
)crc1_reported
);
2132 md
->crc_reported
= (u32
)dbuf_getu16be_p(f2
, &f2_pos
);
2133 de_dbg(c
, "crc of orig. data (reported): 0x%04x", (UI
)md
->crc_reported
);
2135 md
->cmpr_meth
= (UI
)dbuf_getbyte_p(f2
, &f2_pos
);
2136 de_dbg(c
, "cmpr. method: %u", md
->cmpr_meth
);
2139 pos
= md
->member_hdr_pos
+ 34;
2140 // TODO: How long is the filename field?
2141 dbuf_read_to_ucstring(c
->infile
, pos
, 12, md
->filename
, DE_CONVFLAG_STOP_AT_NUL
,
2143 de_dbg(c
, "filename: \"%s\"", ucstring_getpsz_d(md
->filename
));
2145 md
->cmpr_pos
= md
->member_hdr_pos
+ md
->member_hdr_size
;
2146 de_dbg(c
, "compressed data at %"I64_FMT
", len=%"I64_FMT
, md
->cmpr_pos
, md
->cmpr_len
);
2148 md
->validate_crc
= 1;
2149 if(md
->cmpr_meth
==1) {
2150 md
->dfn
= noncompressed_decompressor_fn
;
2151 de_arch_extract_member_file(md
);
2153 else if(md
->cmpr_meth
==2) {
2154 md
->dfn
= lif_method2_decompressor_fn
;
2155 de_arch_extract_member_file(md
);
2158 de_err(c
, "Unsupported compression: %u", (UI
)md
->cmpr_meth
);
2161 de_dbg_indent(c
, -1);
2162 pos
= md
->cmpr_pos
+ md
->cmpr_len
;
2166 de_dbg_indent_restore(c
, saved_indent_level
);
2168 de_arch_destroy_md(c
, md
);
2172 if(d
->need_errmsg
) {
2173 de_err(c
, "Bad or unsupported LIF file");
2175 de_arch_destroy_lctx(c
, d
);
2180 static int de_identify_lif_kdc(deark
*c
)
2189 cmprmeth
= lif_read_field(c
->infile
, 32, 2, &errflag
);
2190 if(errflag
) return 0;
2191 if(cmprmeth
<1 || cmprmeth
>3) return 0;
2193 b
= de_getbyte(34); // 1st char of filename
2195 b
= de_getbyte(53); // last char of NUL-padded filename field??
2198 for(i
=0; i
<4; i
++) {
2199 n
[i
] = lif_read_field(c
->infile
, 8*i
, 8, &errflag
);
2200 if(errflag
) return 0;
2202 if(54+n
[1] > c
->infile
->len
) return 0; // File too short
2204 has_ext
= de_input_file_has_ext(c
, "lif");
2205 return has_ext
? 45 : 15;
2208 void de_module_lif_kdc(deark
*c
, struct deark_module_info
*mi
)
2211 mi
->desc
= "LIF installer archive (Knowledge Dynamics Corp)";
2212 mi
->run_fn
= de_run_lif_kdc
;
2213 mi
->identify_fn
= de_identify_lif_kdc
;
2216 // **************************************************************************
2217 // AIN archive (Transas Marine Ltd)
2218 // **************************************************************************
2220 // This module doesn't do much. It parses the archive header, and computes
2223 static void ain_calc_hdr_checksum(deark
*c
, UI
*pchksum
)
2225 *pchksum
= (UI
)de_calccrc_oneshot(c
->infile
, 0, 22, DE_CRCOBJ_SUM_BYTES
);
2226 // No need to mod 2^16 here, since the max possible sum is much less than 2^16.
2230 static void do_ain_main(deark
*c
, de_arch_lctx
*d
)
2232 struct de_timestamp archive_timestamp
;
2233 UI hdr_checksum_reported
;
2234 UI hdr_checksum_calc
;
2235 i64 member_hdrs_pos
;
2236 i64 member_hdrs_len
;
2240 UI member_hdrs_checksum_reported
;
2241 UI member_hdrs_checksum_calc
= 0;
2245 de_declare_fmt(c
, "AIN archive");
2246 if(c
->module_disposition
==DE_MODDISP_AUTODETECT
) {
2247 de_info(c
, "Note: AIN support is limited to decoding the header");
2251 b
= de_getbyte_p(&pos
);
2253 de_dbg(c
, "update speed: /u%u", upd_speed
);
2254 cmpr_meth
= b
& 0x0f;
2255 de_dbg(c
, "cmpr. method: /m%u", cmpr_meth
);
2258 b
= de_getbyte_p(&pos
);
2259 de_dbg(c
, "flags: 0x%02x", b
);
2260 pos
+= 2; // password-related
2261 volume
= (UI
)de_getu16le_p(&pos
);
2262 de_dbg(c
, "volume: %u", volume
);
2265 d
->num_members
= de_getu16le_p(&pos
);
2266 de_dbg(c
, "number of members: %"I64_FMT
, d
->num_members
);
2268 de_arch_read_field_dttm_p(d
, &archive_timestamp
, "archive",
2269 DE_ARCH_TSTYPE_DOS_TD
, &pos
);
2271 member_hdrs_pos
= de_getu32le_p(&pos
);
2272 de_dbg(c
, "member hdrs pos: %"I64_FMT
, member_hdrs_pos
);
2273 member_hdrs_len
= c
->infile
->len
- member_hdrs_pos
;
2275 member_hdrs_checksum_reported
= (UI
)de_getu16le_p(&pos
);
2276 de_dbg(c
, "member hdrs checksum (reported): 0x%04x", member_hdrs_checksum_reported
);
2277 member_hdrs_checksum_calc
= (UI
)de_calccrc_oneshot(c
->infile
, member_hdrs_pos
,
2278 member_hdrs_len
, DE_CRCOBJ_SUM_BYTES
);
2279 member_hdrs_checksum_calc
&= 0xffff;
2280 de_dbg(c
, "member hdrs checksum (calculated): 0x%04x", member_hdrs_checksum_calc
);
2284 hdr_checksum_reported
= (UI
)de_getu16le_p(&pos
);
2285 de_dbg(c
, "archive hdr checksum (reported): 0x%04x", hdr_checksum_reported
);
2287 ain_calc_hdr_checksum(c
, &hdr_checksum_calc
);
2288 de_dbg(c
, "archive hdr checksum (calculated): 0x%04x", hdr_checksum_calc
);
2290 de_dbg(c
, "file data at %"I64_FMT
", len=%"I64_FMT
, pos
, member_hdrs_pos
-pos
);
2291 de_dbg(c
, "member hdrs at %"I64_FMT
", len=%"I64_FMT
, member_hdrs_pos
, member_hdrs_len
);
2294 static void de_run_ain(deark
*c
, de_module_params
*mparams
)
2296 de_arch_lctx
*d
= NULL
;
2298 d
= de_arch_create_lctx(c
);
2301 de_arch_destroy_lctx(c
, d
);
2304 static int de_identify_ain(deark
*c
)
2306 UI hdr_checksum_reported
;
2307 UI hdr_checksum_calc
;
2309 i64 member_hdrs_pos
;
2311 if(de_getbyte(0)!=0x21) return 0;
2312 if(de_getbyte(2)!=0x00) return 0;
2313 member_hdrs_pos
= de_getu32le(14);
2314 if(member_hdrs_pos
<24 || member_hdrs_pos
>=c
->infile
->len
) return 0;
2315 hdr_checksum_reported
= (UI
)de_getu16le(22);
2316 ain_calc_hdr_checksum(c
, &hdr_checksum_calc
);
2317 if(hdr_checksum_calc
!= hdr_checksum_reported
) return 0;
2318 has_ext
= de_input_file_has_ext(c
, "ain");
2319 return has_ext
?100:50;
2322 void de_module_ain(deark
*c
, struct deark_module_info
*mi
)
2325 mi
->desc
= "AIN archive";
2326 mi
->run_fn
= de_run_ain
;
2327 mi
->flags
|= DE_MODFLAG_HIDDEN
;
2328 mi
->identify_fn
= de_identify_ain
;
2331 // **************************************************************************
2332 // Hemera thumbnails file (.hta)
2333 // **************************************************************************
2335 static const char *hta_get_ext(struct de_arch_member_data
*md
)
2338 const char *ext
= "bin";
2340 if(md
->orig_len
<8) goto done
;
2341 dbuf_read(md
->d
->inf
, sig
, md
->cmpr_pos
, sizeof(sig
));
2342 if(sig
[0]==0x89 && sig
[1]==0x50) ext
="png";
2343 else if(sig
[0]=='G' && sig
[1]=='I') ext
="gif";
2344 else if(sig
[0]==0xff && sig
[1]==0xd8) ext
="jpg"; // Not observed, but just in case
2349 static void de_run_hta(deark
*c
, de_module_params
*mparams
)
2351 struct de_arch_member_data
*md
= NULL
;
2356 de_arch_lctx
*d
= NULL
;
2357 int saved_indent_level
;
2358 i64 tracking_dpos
= 0;
2360 de_dbg_indent_save(c
, &saved_indent_level
);
2362 d
= de_arch_create_lctx(c
);
2366 fmtver
= (UI
)de_getu32le_p(&pos
);
2367 de_dbg(c
, "format version: %u", fmtver
);
2368 if(fmtver
!=100) { d
->need_errmsg
= 1; goto done
; }
2369 num_members
= de_getu32le_p(&pos
);
2370 de_dbg(c
, "number of members: %"I64_FMT
, num_members
);
2371 if(pos
+num_members
*8 > c
->infile
->len
) { d
->need_errmsg
= 1; goto done
; }
2373 for(idx
=0; idx
<num_members
; idx
++) {
2378 de_arch_destroy_md(c
, md
);
2381 md
= de_arch_create_md(c
, d
);
2382 de_dbg(c
, "member[%"I64_FMT
"]", idx
);
2383 de_dbg_indent(c
, 1);
2385 md
->cmpr_pos
= de_getu32le_p(&pos
);
2386 de_dbg(c
, "data pos: %"I64_FMT
, md
->cmpr_pos
);
2387 de_arch_read_field_orig_len_p(md
, &pos
);
2388 md
->cmpr_len
= md
->orig_len
;
2390 if(md
->cmpr_pos
< tracking_dpos
) { d
->need_errmsg
= 1; goto done
; }
2391 endpos
= md
->cmpr_pos
+ md
->cmpr_len
;
2392 if(endpos
> c
->infile
->len
) { d
->need_errmsg
= 1; goto done
; }
2393 tracking_dpos
= endpos
;
2395 ext
= hta_get_ext(md
);
2396 de_finfo_set_name_from_sz(c
, md
->fi
, ext
, 0, DE_ENCODING_LATIN1
);
2397 md
->dfn
= noncompressed_decompressor_fn
;
2398 de_arch_extract_member_file(md
);
2400 de_dbg_indent(c
, -1);
2404 de_dbg_indent_restore(c
, saved_indent_level
);
2406 de_arch_destroy_md(c
, md
);
2410 if(d
->need_errmsg
) {
2411 de_err(c
, "Bad or unsupported HTA file");
2413 de_arch_destroy_lctx(c
, d
);
2417 static int de_identify_hta(deark
*c
)
2419 if(!dbuf_memcmp(c
->infile
, 0, "\x89\x48\x54\x41\x0d\x0a\x1a\x0a", 8)) return 100;
2423 void de_module_hta(deark
*c
, struct deark_module_info
*mi
)
2426 mi
->desc
= "Hemera thumbnails";
2427 mi
->run_fn
= de_run_hta
;
2428 mi
->identify_fn
= de_identify_hta
;
2431 // **************************************************************************
2432 // HIT (Bogdan Ureche)
2433 // **************************************************************************
2435 #define HIT_MINHEADERLEN 20
2437 static UI
hit_calc_hdr_checksum(struct de_arch_member_data
*md
)
2439 struct de_crcobj
*crco
= NULL
;
2442 crco
= de_crcobj_create(md
->c
, DE_CRCOBJ_SUM_BYTES
);
2443 de_crcobj_addslice(crco
, md
->d
->inf
, md
->member_hdr_pos
, 2);
2444 de_crcobj_addslice(crco
, md
->d
->inf
, md
->member_hdr_pos
+3,
2445 md
->member_hdr_size
-3);
2446 x
= de_crcobj_getval(crco
);
2448 de_crcobj_destroy(crco
);
2452 static int do_BUhit_member(deark
*c
, de_arch_lctx
*d
, struct de_arch_member_data
*md
)
2454 i64 pos
= md
->member_hdr_pos
;
2456 UI hdr_checksum_reported
;
2457 UI hdr_checksum_calc
;
2458 UI flags_and_cmpr_meth
;
2459 int saved_indent_level
;
2462 de_dbg_indent_save(c
, &saved_indent_level
);
2463 de_dbg(c
, "member at %"I64_FMT
, md
->member_hdr_pos
);
2464 de_dbg_indent(c
, 1);
2466 md
->member_hdr_size
= de_getu16le_p(&pos
);
2467 de_dbg(c
, "header len: %"I64_FMT
, md
->member_hdr_size
);
2468 if(md
->member_hdr_size
< HIT_MINHEADERLEN
) goto done
;
2470 hdr_checksum_reported
= (UI
)de_getbyte_p(&pos
);
2471 de_dbg(c
, "header checksum (reported): 0x%02x", hdr_checksum_reported
);
2473 hdr_checksum_calc
= hit_calc_hdr_checksum(md
);
2474 de_dbg(c
, "header checksum (calculated): 0x%02x", hdr_checksum_calc
);
2476 // bit 0x80 = garbled (reserved)
2477 // bit 0x40 = Maybe a version flag? Affects the CRC field.
2478 // bit 0x20 = unknown (reserved?)
2479 // low 5 bits = compression method
2480 flags_and_cmpr_meth
= (UI
)de_getbyte_p(&pos
);
2481 md
->cmpr_meth
= flags_and_cmpr_meth
& 0x1f;
2482 de_dbg(c
, "cmpr. method: %u", md
->cmpr_meth
);
2486 de_arch_read_field_cmpr_len_p(md
, &pos
);
2487 de_arch_read_field_orig_len_p(md
, &pos
);
2489 pos
= md
->member_hdr_pos
+13;
2490 de_arch_read_field_dttm_p(d
, &md
->fi
->timestamp
[DE_TIMESTAMPIDX_MODIFY
], "mod",
2491 DE_ARCH_TSTYPE_DOS_TD
, &pos
);
2492 de_arch_read_field_dos_attr_p(md
, &pos
);
2496 md
->crc_reported
= (u32
)de_getu32le_p(&pos
);
2497 de_dbg(c
, "crc (reported): 0x%08x", (UI
)md
->crc_reported
);
2499 fnlen
= (i64
)de_getbyte_p(&pos
);
2500 dbuf_read_to_ucstring(c
->infile
, pos
, fnlen
, md
->filename
, 0,
2502 de_dbg(c
, "filename: \"%s\"", ucstring_getpsz_d(md
->filename
));
2505 md
->cmpr_pos
= md
->member_hdr_pos
+ md
->member_hdr_size
;
2506 de_dbg(c
, "cmpr data pos: %"I64_FMT
, md
->cmpr_pos
);
2507 md
->member_total_size
= md
->member_hdr_size
+ md
->cmpr_len
;
2509 de_dbg_indent(c
, -1);
2512 de_dbg_indent_restore(c
, saved_indent_level
);
2516 static void de_run_hit(deark
*c
, de_module_params
*mparams
)
2518 de_arch_lctx
*d
= NULL
;
2519 struct de_arch_member_data
*md
= NULL
;
2522 d
= de_arch_create_lctx(c
);
2524 d
->input_encoding
= de_get_input_encoding(c
, NULL
, DE_ENCODING_CP437
);
2529 if(pos
+HIT_MINHEADERLEN
> c
->infile
->len
) goto done
;
2532 de_arch_destroy_md(c
, md
);
2535 md
= de_arch_create_md(c
, d
);
2536 md
->member_hdr_pos
= pos
;
2538 if(!do_BUhit_member(c
, d
, md
)) goto done
;
2539 if(md
->member_total_size
<=0) goto done
;
2540 pos
+= md
->member_total_size
;
2545 de_arch_destroy_md(c
, md
);
2547 de_arch_destroy_lctx(c
, d
);
2550 static int de_identify_hit(deark
*c
)
2552 if(!de_input_file_has_ext(c
, "hit")) return 0;
2553 if((UI
)de_getu16be(0) != 0x5542) return 0; // "UB"
2557 void de_module_hit(deark
*c
, struct deark_module_info
*mi
)
2560 mi
->desc
= "HIT archive";
2561 mi
->run_fn
= de_run_hit
;
2562 mi
->flags
|= DE_MODFLAG_WARNPARSEONLY
;
2563 mi
->identify_fn
= de_identify_hit
;
2566 // **************************************************************************
2567 // Binary II (Apple II format)
2568 // **************************************************************************
2570 // This struct is assumed to contain no pointers
2571 struct binary_ii_extra_md
{
2572 i64 filesize
; // in 512-byte blocks
2573 int num_members_remaining
;
2576 static void do_binary_ii_member(deark
*c
,
2577 de_arch_lctx
*d
, struct de_arch_member_data
*md
, struct binary_ii_extra_md
*b2_md
)
2582 i64 space_reqd_in_blocks
;
2584 UI auxtype
, auxtype_hi
;
2585 UI accesscode
, accesscode_hi
;
2586 UI filetype
, filetype_hi
;
2587 UI storagetype
, storagetype_hi
;
2596 int saved_indent_level
;
2598 de_dbg_indent_save(c
, &saved_indent_level
);
2599 pos1
= md
->member_hdr_pos
;
2600 de_dbg(c
, "member at %"I64_FMT
, pos1
);
2601 de_dbg_indent(c
, 1);
2603 // Skip ahead and read some "high bytes" fields first.
2605 auxtype_hi
= (UI
)de_getu16le_p(&pos
);
2606 accesscode_hi
= (UI
)de_getbyte_p(&pos
);
2607 filetype_hi
= (UI
)de_getbyte_p(&pos
);
2608 storagetype_hi
= (UI
)de_getbyte_p(&pos
);
2609 filesize_hi
= (UI
)de_getu16le_p(&pos
);
2610 eof_hi
= (UI
)de_getbyte_p(&pos
);
2613 accesscode
= (UI
)de_getbyte_p(&pos
);
2614 accesscode
= accesscode
| (accesscode_hi
<<8);
2615 de_dbg(c
, "access: 0x%04x", accesscode
);
2617 filetype
= (UI
)de_getbyte_p(&pos
);
2618 filetype
= filetype
| (filetype_hi
<<8);
2619 de_dbg(c
, "file type: 0x%04x", filetype
);
2621 auxtype
= (UI
)de_getu16le_p(&pos
);
2622 auxtype
= auxtype
| (auxtype_hi
<<16);
2623 de_dbg(c
, "aux type: 0x%08x",auxtype
);
2625 storagetype
= (UI
)de_getbyte_p(&pos
);
2626 storagetype
= storagetype
| (storagetype_hi
<<8);
2627 de_dbg(c
, "storage type: 0x%04x", storagetype
);
2629 b2_md
->filesize
= de_getu16le_p(&pos
);
2630 b2_md
->filesize
|= ((i64
)filesize_hi
<<16);
2631 de_dbg(c
, "\"file size\": %"I64_FMT
" (in 512-byte blocks)", b2_md
->filesize
);
2633 moddate_raw
= (UI
)de_getu16le_p(&pos
);
2634 modtime_raw
= (UI
)de_getu16le_p(&pos
);
2635 de_prodos_datetime_to_timestamp(&md
->fi
->timestamp
[DE_TIMESTAMPIDX_MODIFY
],
2636 moddate_raw
, modtime_raw
);
2637 dbg_timestamp(c
, &md
->fi
->timestamp
[DE_TIMESTAMPIDX_MODIFY
], "mod time");
2639 crdate_raw
= (UI
)de_getu16le_p(&pos
);
2640 crtime_raw
= (UI
)de_getu16le_p(&pos
);
2641 de_prodos_datetime_to_timestamp(&md
->fi
->timestamp
[DE_TIMESTAMPIDX_CREATE
],
2642 crdate_raw
, crtime_raw
);
2643 dbg_timestamp(c
, &md
->fi
->timestamp
[DE_TIMESTAMPIDX_CREATE
], "create time");
2646 md
->orig_len
= (UI
)dbuf_getint_ext(c
->infile
, pos
, 3, 1, 0);
2647 md
->orig_len
|= ((i64
)eof_hi
<<24);
2648 de_dbg(md
->c
, "original size: %"I64_FMT
, md
->orig_len
);
2649 md
->orig_len_known
= 1;
2650 md
->cmpr_len
= md
->orig_len
;
2653 fnlen
= de_getbyte_p(&pos
);
2658 dbuf_read_to_ucstring(c
->infile
, pos
, fnlen
, md
->filename
, DE_CONVFLAG_STOP_AT_NUL
,
2660 de_dbg(c
, "filename: \"%s\"", ucstring_getpsz_d(md
->filename
));
2663 space_reqd_in_blocks
= de_getu32le_p(&pos
);
2664 de_dbg(c
, "disk space req'd: %"I64_FMT
" (in 512-byte blocks)", space_reqd_in_blocks
);
2666 ostype
= (UI
)de_getbyte_p(&pos
);
2667 de_dbg(c
, "OS type: 0x%02x", ostype
);
2669 pos
+= 2; // [122] native file type
2670 pos
+= 1; // [124] phantom file flag
2672 data_flags
= (UI
)de_getbyte_p(&pos
);
2673 de_dbg(c
, "data flags: 0x%02x", data_flags
);
2675 fmt_ver
= de_getbyte_p(&pos
);
2676 de_dbg(c
, "fmt ver: 0x%02x", (UI
)fmt_ver
);
2678 b2_md
->num_members_remaining
= (int)de_getbyte_p(&pos
);
2679 de_dbg(c
, "num members remaining: %d", b2_md
->num_members_remaining
);
2682 md
->dfn
= noncompressed_decompressor_fn
;
2683 de_arch_extract_member_file(md
);
2686 de_dbg_indent_restore(c
, saved_indent_level
);
2689 static int binary_ii_is_member_at(dbuf
*f
, i64 pos
, u8 check_nr
, int nr_expected
)
2691 if(dbuf_memcmp(f
, pos
, (const void*)"\x0a\x47\x4c", 3)) return 0;
2692 if(dbuf_getbyte(f
, pos
+18) != 0x02) return 0;
2696 nr
= (int)dbuf_getbyte(f
, pos
+127);
2697 if(nr
!= nr_expected
) return 0;
2702 static void de_run_binary_ii(deark
*c
, de_module_params
*mparams
)
2704 de_arch_lctx
*d
= NULL
;
2705 struct de_arch_member_data
*md
= NULL
;
2706 struct binary_ii_extra_md
*b2_md
= NULL
;
2709 b2_md
= de_malloc(c
, sizeof(struct binary_ii_extra_md
));
2711 d
= de_arch_create_lctx(c
);
2713 d
->input_encoding
= de_get_input_encoding(c
, NULL
, DE_ENCODING_ASCII
);
2715 if(!binary_ii_is_member_at(c
->infile
, 0, 0, 0)) {
2725 de_arch_destroy_md(c
, md
);
2728 md
= de_arch_create_md(c
, d
);
2729 de_zeromem(b2_md
, sizeof(struct binary_ii_extra_md
));
2731 md
->member_hdr_pos
= pos
;
2732 do_binary_ii_member(c
, d
, md
, b2_md
);
2733 if(d
->fatalerrflag
) goto done
;
2734 if(b2_md
->num_members_remaining
<1) goto done
;
2736 // I'm not sure how we're supposed to find the next archive member.
2737 // We'll check two places.
2739 // This is where "ibmnulib" seems to look for the next member:
2740 npos1
= de_pad_to_n(md
->cmpr_pos
+ md
->cmpr_len
, 128);
2741 // This is where the Raymond Clay document says to look:
2742 npos2
= md
->cmpr_pos
+ 512*b2_md
->filesize
;
2744 ret
= binary_ii_is_member_at(c
->infile
, npos1
, 1, b2_md
->num_members_remaining
-1);
2748 else if(npos2
!=npos1
) {
2749 ret
= binary_ii_is_member_at(c
->infile
, npos2
, 1, b2_md
->num_members_remaining
-1);
2763 de_arch_destroy_md(c
, md
);
2766 if(d
->need_errmsg
) {
2767 de_err(c
, "Bad or unsupported Binary II file");
2769 de_arch_destroy_lctx(c
, d
);
2774 static int de_identify_binary_ii(deark
*c
)
2776 if(!binary_ii_is_member_at(c
->infile
, 0, 0, 0)) return 0;
2780 void de_module_binary_ii(deark
*c
, struct deark_module_info
*mi
)
2782 mi
->id
= "binary_ii";
2783 mi
->desc
= "Binary II";
2784 mi
->run_fn
= de_run_binary_ii
;
2785 mi
->identify_fn
= de_identify_binary_ii
;
2788 // **************************************************************************
2789 // TRS-80 TC archive (.arc, .tc)
2790 // Made by The Compressor, by John Lauro.
2791 // [Written with help from UnTC, public domain software by Tim Koonce.]
2792 // **************************************************************************
2794 static void tc_decompress_rle(struct de_arch_member_data
*md
)
2797 i64 pos
= md
->dcmpri
->pos
;
2798 i64 endpos
= md
->dcmpri
->pos
+ md
->dcmpri
->len
;
2799 i64 nbytes_written
= 0;
2800 static const char *modname
= "TC_RLE";
2802 escchar
= dbuf_getbyte_p(md
->dcmpri
->f
, &pos
);
2809 if(pos
>= endpos
) goto done
;
2811 b0
= dbuf_getbyte_p(md
->dcmpri
->f
, &pos
);
2813 dbuf_writebyte(md
->dcmpro
->f
, b0
);
2818 b1
= dbuf_getbyte_p(md
->dcmpri
->f
, &pos
);
2823 else if(b1
==1) { // run length 256 to 511
2824 b2
= dbuf_getbyte_p(md
->dcmpri
->f
, &pos
);
2825 count
= 256 + (i64
)b2
;
2826 val
= dbuf_getbyte_p(md
->dcmpri
->f
, &pos
);
2828 else if(b1
==2) { // large run length
2829 ctmp
= dbuf_getu16be_p(md
->dcmpri
->f
, &pos
);
2831 val
= dbuf_getbyte_p(md
->dcmpri
->f
, &pos
);
2833 else if(b1
>3) { // small run length
2835 val
= dbuf_getbyte_p(md
->dcmpri
->f
, &pos
);
2838 de_dfilter_set_generic_error(md
->c
, md
->dres
, modname
);
2841 dbuf_write_run(md
->dcmpro
->f
, val
, count
);
2842 nbytes_written
+= count
;
2845 if(md
->dres
->errcode
==0) {
2846 de_dbg(md
->c
, "decompressed %"I64_FMT
" to %"I64_FMT
" bytes",
2847 pos
-md
->dcmpri
->pos
, nbytes_written
);
2851 static void tc_decompressor_fn(struct de_arch_member_data
*md
)
2855 if(md
->cmpr_meth
==0) {
2856 fmtutil_decompress_uncompressed(c
, md
->dcmpri
, md
->dcmpro
, md
->dres
, 0);
2858 else if(md
->cmpr_meth
==1) {
2859 tc_decompress_rle(md
);
2862 de_dfilter_set_generic_error(c
, md
->dres
, NULL
);
2865 static void do_tc_member(deark
*c
, de_arch_lctx
*d
, struct de_arch_member_data
*md
)
2867 int saved_indent_level
;
2869 i64 pos
= md
->member_hdr_pos
;
2870 de_ucstring
*fn_ext
= NULL
;
2875 de_dbg_indent_save(c
, &saved_indent_level
);
2877 de_dbg(c
, "member at %"I64_FMT
, md
->member_hdr_pos
);
2878 de_dbg_indent(c
, 1);
2880 seq_num
= (i64
)de_getbyte_p(&pos
);
2881 de_dbg(c
, "seq num: %d", (int)seq_num
);
2886 if(seq_num
!= md
->member_idx
) {
2887 d
->fatalerrflag
= 1;
2892 // Presumably not a NUL-terminated string, but the flag won't hurt.
2893 dbuf_read_to_ucstring(c
->infile
, pos
, 8, md
->filename
, DE_CONVFLAG_STOP_AT_NUL
,
2896 ucstring_strip_trailing_spaces(md
->filename
);
2898 fn_ext
= ucstring_create(c
);
2899 dbuf_read_to_ucstring(c
->infile
, pos
, 3, fn_ext
, DE_CONVFLAG_STOP_AT_NUL
,
2902 ucstring_strip_trailing_spaces(fn_ext
);
2903 if(ucstring_isnonempty(fn_ext
)) {
2904 ucstring_append_char(md
->filename
, '.');
2905 ucstring_append_ucstring(md
->filename
, fn_ext
);
2908 de_dbg(c
, "filename: \"%s\"", ucstring_getpsz_d(md
->filename
));
2910 ftype
= de_getbyte_p(&pos
);
2911 de_dbg(c
, "file type: %u", (UI
)ftype
);
2912 aflag
= de_getbyte_p(&pos
);
2913 de_dbg(c
, "ascii flag: %u", (UI
)aflag
);
2914 ver
= de_getbyte_p(&pos
);
2916 d
->fatalerrflag
= 1;
2921 md
->cmpr_len
= dbuf_getint_ext(c
->infile
, pos
, 3, 0, 0);
2923 de_dbg(md
->c
, "compressed size: %"I64_FMT
, md
->cmpr_len
);
2925 md
->cmpr_meth
= (UI
)de_getbyte_p(&pos
);
2926 de_dbg(c
, "cmpr. method: %u", md
->cmpr_meth
);
2929 de_dbg(c
, "file data pos: %"I64_FMT
, md
->cmpr_pos
);
2930 md
->member_total_size
= md
->cmpr_pos
+ md
->cmpr_len
- md
->member_hdr_pos
;
2932 if(md
->cmpr_meth
>1) {
2933 de_err(c
, "Unsupported compression: %u", (UI
)md
->cmpr_meth
);
2937 md
->dfn
= tc_decompressor_fn
;
2938 de_arch_extract_member_file(md
);
2941 ucstring_destroy(fn_ext
);
2942 de_dbg_indent_restore(c
, saved_indent_level
);
2945 static void de_run_tc_trs80(deark
*c
, de_module_params
*mparams
)
2947 de_arch_lctx
*d
= NULL
;
2948 struct de_arch_member_data
*md
= NULL
;
2952 d
= de_arch_create_lctx(c
);
2954 d
->input_encoding
= de_get_input_encoding(c
, NULL
, DE_ENCODING_ASCII
);
2957 if(pos
>= c
->infile
->len
) {
2958 d
->fatalerrflag
= 1;
2964 de_arch_destroy_md(c
, md
);
2968 seq_num
++; // 1-based
2969 md
= de_arch_create_md(c
, d
);
2970 md
->member_hdr_pos
= pos
;
2971 md
->member_idx
= seq_num
;
2972 do_tc_member(c
, d
, md
);
2973 if(d
->stop_flag
|| d
->fatalerrflag
|| md
->member_total_size
<1) {
2976 pos
+= md
->member_total_size
;
2982 de_arch_destroy_md(c
, md
);
2985 if(d
->need_errmsg
) {
2986 de_err(c
, "Bad or unsupported TC file");
2988 de_arch_destroy_lctx(c
, d
);
2992 static int de_identify_tc_trs80(deark
*c
)
2999 if(de_getbyte(0)!=1) return 0; // seq num #1
3000 if(de_getbyte(14)!=1) return 0; // format ver
3001 if(de_getbyte(18) >= 2) return 0; // cmpr meth
3003 for(i
=0; i
<11; i
++) { // filename
3004 b
= de_getbyte(1+i
);
3006 if(i
==0 && b
==32) return 0;
3009 b
= de_getbyte(13); // ascii flag
3010 if(b
!=0 && b
!=255) return 0;
3012 n
= (de_getu32be(14) & 0xffffff) + 19; // offset of member #2
3013 if(n
>= c
->infile
->len
) return 0;
3014 b
= de_getbyte(n
); // seq num #2
3015 if(b
!=0 && b
!=2) return 0;
3017 if(n
+1+18+1 > c
->infile
->len
) return 0;
3020 if(de_input_file_has_ext(c
, "arc")) has_ext
= 1;
3021 else if(de_input_file_has_ext(c
, "tc")) has_ext
= 1;
3022 if(has_ext
) return 75;
3026 void de_module_tc_trs80(deark
*c
, struct deark_module_info
*mi
)
3028 mi
->id
= "tc_trs80";
3029 mi
->desc
= "The Compressor (TRS-80 archive)";
3030 mi
->run_fn
= de_run_tc_trs80
;
3031 mi
->identify_fn
= de_identify_tc_trs80
;