Made Unix builds more likely to be Y2038-compliant
[deark.git] / modules / misc3.c
blobe48b3d0704fd0ee40e7ca2a6639c64aeb16779e3
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)
31 u8 b;
33 b = de_getbyte(pos);
34 if(b>1) return 0;
35 b = de_getbyte(pos+1);
36 if(b<4 || b>6) return 0;
37 return 1;
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 // **************************************************************************
54 // CP Shrink (.cpz)
55 // **************************************************************************
57 static void cpshrink_decompressor_fn(struct de_arch_member_data *md)
59 deark *c = md->c;
61 switch(md->cmpr_meth) {
62 case 0:
63 case 1:
64 fmtutil_dclimplode_codectype1(c, md->dcmpri, md->dcmpro, md->dres, NULL);
65 break;
66 case 2:
67 fmtutil_decompress_uncompressed(c, md->dcmpri, md->dcmpro, md->dres, 0);
68 break;
69 default:
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;
79 UI cdata_crc_calc;
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);
88 de_dbg_indent(c, 1);
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,
94 d->input_encoding);
95 pos += 15;
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)) {
109 d->fatalerrflag = 1;
110 goto done;
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,
120 (UI)cdata_crc_calc);
123 md->dfn = cpshrink_decompressor_fn;
124 de_arch_extract_member_file(md);
126 done:
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;
133 i64 pos;
134 i64 member_hdrs_pos;
135 i64 member_hdrs_len;
136 u32 member_hdrs_crc_reported;
137 u32 member_hdrs_crc_calc;
138 i64 i;
139 int saved_indent_level;
141 de_dbg_indent_save(c, &saved_indent_level);
142 d = de_arch_create_lctx(c);
143 d->is_le = 1;
144 d->input_encoding = de_get_input_encoding(c, NULL, DE_ENCODING_CP437);
146 pos = 0;
147 de_dbg(c, "archive header at %d", (int)pos);
148 de_dbg_indent(c, 1);
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");
155 goto done;
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);
166 de_dbg_indent(c, 1);
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);
184 md->member_idx = i;
185 md->member_hdr_pos = pos;
186 pos += 32;
188 cpshrink_do_member(c, d, md);
189 de_arch_destroy_md(c, md);
190 if(d->fatalerrflag) goto done;
193 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)
200 i64 n;
202 if(!de_input_file_has_ext(c, "cpz")) return 0;
203 n = de_getu32le(0);
204 if(n<1 || n>0xffff) return 0;
205 if(de_getbyte(27)>2) return 0; // cmpr meth of 1st file
206 return 25;
209 void de_module_cpshrink(deark *c, struct deark_module_info *mi)
211 mi->id = "cpshrink";
212 mi->desc = "CP Shrink .CPZ";
213 mi->run_fn = de_run_cpshrink;
214 mi->identify_fn = de_identify_cpshrink;
217 // **************************************************************************
218 // DWC archive
219 // **************************************************************************
221 static void dwc_decompressor_fn(struct de_arch_member_data *md)
223 deark *c = md->c;
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);
235 else {
236 de_dfilter_set_generic_error(c, md->dres, NULL);
240 static void squash_slashes(de_ucstring *s)
242 i64 i;
244 for(i=0; i<s->len; i++) {
245 if(s->str[i]=='/') {
246 s->str[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);
258 return;
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, '_');
267 else {
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)
274 i64 pos = pos1;
275 struct de_arch_member_data *md = NULL;
276 i64 cmt_len = 0;
277 i64 path_len = 0;
278 UI cdata_crc_reported = 0;
279 UI cdata_crc_calc;
280 u8 have_cdata_crc = 0;
281 u8 b;
282 de_ucstring *comment = NULL;
284 md = de_arch_create_md(c, d);
286 de_dbg(c, "member header at %"I64_FMT, pos1);
287 de_dbg_indent(c, 1);
288 md->tmpfn_base = ucstring_create(c);
289 dbuf_read_to_ucstring(c->infile, pos, 12, md->tmpfn_base, DE_CONVFLAG_STOP_AT_NUL,
290 d->input_encoding);
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);
294 pos += 13;
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;
312 if(fhsize>=31) {
313 cmt_len = (i64)de_getbyte_p(&pos);
314 de_dbg(c, "comment len: %d", (int)cmt_len);
316 if(fhsize>=32) {
317 path_len = (i64)de_getbyte_p(&pos);
318 de_dbg(c, "path len: %d", (int)path_len);
320 if(fhsize>=34) {
321 cdata_crc_reported = (u32)de_getu16le_p(&pos);
322 de_dbg(c, "CRC of cmpr. data (reported): 0x%04x", (UI)cdata_crc_reported);
323 have_cdata_crc = 1;
326 if(!de_arch_good_cmpr_data_pos(md)) {
327 goto done;
330 if(path_len>1) {
331 md->tmpfn_path = ucstring_create(c);
332 dbuf_read_to_ucstring(c->infile, md->cmpr_pos+md->cmpr_len,
333 path_len-1,
334 md->tmpfn_path, DE_CONVFLAG_STOP_AT_NUL, d->input_encoding);
335 de_dbg(c, "path: \"%s\"", ucstring_getpsz_d(md->tmpfn_path));
337 if(cmt_len>1) {
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);
346 if(have_cdata_crc) {
347 if(!d->crco) {
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,
357 (UI)cdata_crc_calc);
361 if(d->private1) {
362 md->dfn = dwc_decompressor_fn;
363 de_arch_extract_member_file(md);
366 done:
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;
380 i64 trailer_pos;
381 i64 trailer_len;
382 i64 nmembers;
383 i64 fhsize; // size of each file header
384 i64 pos;
385 i64 i;
386 struct de_timestamp tmpts;
387 int need_errmsg = 0;
388 int saved_indent_level;
390 de_dbg_indent_save(c, &saved_indent_level);
392 d = de_arch_create_lctx(c);
393 d->is_le = 1;
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");
399 goto done;
401 de_declare_fmt(c, "DWC archive");
403 if(!d->private1) {
404 de_info(c, "Note: Use \"-opt dwc:extract\" to attempt decompression "
405 "(works for most small files).");
408 de_dbg(c, "trailer");
409 de_dbg_indent(c, 1);
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) {
416 need_errmsg = 1;
417 goto done;
420 fhsize = (i64)de_getbyte_p(&pos);
421 de_dbg(c, "file header entry size: %d", (int)fhsize);
422 if(fhsize<30) {
423 need_errmsg = 1;
424 goto done;
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;
435 if(pos<0) {
436 need_errmsg = 1;
437 goto done;
439 for(i=0; i<nmembers; i++) {
440 do_dwc_member(c, d, pos, fhsize);
441 if(d->fatalerrflag) goto done;
442 pos += fhsize;
445 done:
446 if(need_errmsg) {
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)
455 i64 tsize;
456 int has_ext;
457 u8 dsize;
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;
467 return 60;
469 if(has_ext) return 10;
470 return 0;
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)
480 mi->id = "dwc";
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
501 // allowed.
502 static int edi_is_filename_at(deark *c, de_arch_lctx *d, i64 pos)
504 u8 buf[13];
505 size_t i;
506 int found_nul = 0;
507 int found_dot = 0;
508 int base_len = 0;
509 int ext_len = 0;
511 if(pos+13 > c->infile->len) return 0;
512 de_read(buf, pos, 13);
514 for(i=0; i<13; i++) {
515 u8 b;
517 b = buf[i];
518 if(b==0) {
519 found_nul = 1;
520 break;
522 else if(b=='.') {
523 if(found_dot) return 0;
524 found_dot = 1;
526 else if(b<33 || b=='"' || b=='*' || b=='+' || b==',' || b=='/' ||
527 b==':' || b==';' || b=='<' || b=='=' || b=='>' || b=='?' ||
528 b=='[' || b=='\\' || b==']' || b=='|' || b==127)
530 return 0;
532 else {
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++;
536 else base_len++;
540 if(!found_nul || base_len<1 || base_len>8 || ext_len>3) return 0;
541 return 1;
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)
554 u8 ver;
555 i64 pos = 0;
557 if(dbuf_memcmp(c->infile, pos, g_edilzss_sig, 7)) {
558 d->need_errmsg = 1;
559 return;
561 pos += 7;
563 ver = de_getbyte_p(&pos);
564 if(ver=='1') {
565 // There's no easy way to distinguish some LZSS1 formats. This will not
566 // always work.
567 if(edi_is_filename_at(c, d, pos)) {
568 d->fmtver = 1;
570 else {
571 d->fmtver = 10;
574 else if(ver=='2') {
575 d->fmtver = 2;
577 else {
578 d->need_errmsg = 1;
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;
586 i64 pos = 0;
588 d = de_arch_create_lctx(c);
589 d->is_le = 1;
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");
597 else {
598 de_declare_fmtf(c, "EDI Pack LZSS%d", d->fmtver);
600 pos = 8;
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,
605 d->input_encoding);
606 de_dbg(c, "filename: \"%s\"", ucstring_getpsz_d(md->filename));
607 pos += 13;
610 if(d->fmtver==2) {
611 de_arch_read_field_orig_len_p(md, &pos);
614 if(pos > c->infile->len) {
615 d->need_errmsg = 1;
616 goto done;
619 md->cmpr_pos = pos;
620 md->cmpr_len = c->infile->len - md->cmpr_pos;
621 md->dfn = edi_pack_decompressor_fn;
622 de_arch_extract_member_file(md);
624 done:
625 de_arch_destroy_md(c, md);
626 if(d->need_errmsg) {
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)) {
635 u8 v;
637 v = de_getbyte(7);
638 if(v=='1' || v=='2') return 100;
639 return 0;
641 return 0;
644 void de_module_edi_pack(deark *c, struct deark_module_info *mi)
646 mi->id = "edi_pack";
647 mi->desc = "EDI Install packed file";
648 mi->run_fn = de_run_edi_pack;
649 mi->identify_fn = de_identify_edi_pack;
652 // **************************************************************************
653 // Quarterdeck QIP
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;
660 i64 pos;
661 UI index;
662 int retval = 0;
664 de_dbg_indent_save(c, &saved_indent_level);
665 de_dbg(c, "member at %"I64_FMT, md->member_hdr_pos);
666 de_dbg_indent(c, 1);
667 pos = md->member_hdr_pos;
668 if(dbuf_memcmp(c->infile, pos, "QD", 2)) goto done;
669 pos += 2;
670 retval = 1;
671 pos += 2; // ?
672 de_arch_read_field_cmpr_len_p(md, &pos);
673 index = (UI)de_getu16le_p(&pos); // ?
674 de_dbg(c, "index: %u", index);
676 if(d->fmtver>=2) {
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,
687 d->input_encoding);
688 de_dbg(c, "filename: \"%s\"", ucstring_getpsz_d(md->filename));
689 pos += 12;
690 pos += 1; // Maybe to allow the name to always be NUL terminated?
692 md->cmpr_pos = pos;
693 de_dbg(c, "cmpr data at %"I64_FMT, md->cmpr_pos);
694 md->dfn = dclimplode_decompressor_fn;
695 if(d->fmtver>=2) {
696 md->validate_crc = 1;
699 de_arch_extract_member_file(md);
701 done:
702 de_dbg_indent_restore(c, saved_indent_level);
703 return retval;
706 static void qip_do_v1(deark *c, de_arch_lctx *d)
708 i64 pos = 0;
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.
714 while(1) {
715 i64 cmpr_len;
717 if(pos+32 >= c->infile->len) goto done;
719 if(md) {
720 de_arch_destroy_md(c, md);
721 md = NULL;
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)) {
728 goto done;
730 pos += 32 + cmpr_len;
733 done:
734 if(md) {
735 de_arch_destroy_md(c, md);
739 static void qip_do_v2(deark *c, de_arch_lctx *d)
741 i64 pos;
742 i64 index_pos;
743 i64 index_len;
744 i64 index_endpos;
745 i64 i;
746 struct de_arch_member_data *md = NULL;
748 pos = 2;
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);
754 index_pos = 16;
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;
759 pos = index_pos;
761 for(i=0; i<d->num_members; i++) {
762 if(pos+16 > index_endpos) goto done;
764 if(md) {
765 de_arch_destroy_md(c, md);
766 md = NULL;
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);
772 pos += 12;
775 done:
776 if(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;
784 u8 b;
785 int unsupp_flag = 0;
787 d = de_arch_create_lctx(c);
788 d->is_le = 1;
789 d->input_encoding = de_get_input_encoding(c, NULL, DE_ENCODING_CP437);
791 b = de_getbyte(1);
792 if(b=='P') {
793 d->fmtver = 2;
795 else if(b=='D') {
796 d->fmtver = 1;
798 else {
799 unsupp_flag = 1;
800 goto done;
803 if(d->fmtver==2) {
804 if(de_getbyte(8)!=0x02) {
805 unsupp_flag = 1;
806 goto done;
810 if(d->fmtver==1) {
811 qip_do_v1(c, d);
813 else {
814 qip_do_v2(c, d);
817 done:
818 if(unsupp_flag) {
819 de_err(c, "Not a supported QIP format");
821 de_arch_destroy_lctx(c, d);
824 static int de_identify_qip(deark *c)
826 u8 b;
827 i64 n;
829 if(de_getbyte(0)!='Q') return 0;
830 b = de_getbyte(1);
831 if(b=='P') {
832 if(de_getbyte(8)!=0x02) return 0;
833 n = de_getu32le(16);
834 if(n>c->infile->len) return 0;
835 if(!dbuf_memcmp(c->infile, n, "QD", 2)) return 100;
837 else if(b=='D') {
838 if(de_getu16le(2)==0 &&
839 de_getu16le(8)==1)
841 return 70;
844 return 0;
847 void de_module_qip(deark *c, struct deark_module_info *mi)
849 mi->id = "qip";
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 // **************************************************************************
859 #define FMT_PCXLIB 0
860 #define FMT_GXLIB 1
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,
868 i64 pos)
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,
874 d->input_encoding);
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,
879 d->input_encoding);
880 ucstring_strip_trailing_spaces(tmps);
881 if(tmps->len>1) {
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);
896 de_dbg_indent(c, 1);
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));
907 pos += 13;
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;
928 else {
929 de_err(c, "Unsupported compression: %u", (UI)md->cmpr_meth);
930 goto done;
933 if(d->fmtcode==FMT_PCXLIB) {
934 pos += 40; // note
935 pos += 20; // unused
938 if(d->fmtcode==FMT_PCXLIB) {
939 md->cmpr_pos = pos;
942 if(!de_arch_good_cmpr_data_pos(md)) {
943 d->fatalerrflag = 1;
944 goto done;
947 if(d->fmtcode==FMT_GXLIB) {
948 md->member_total_size = pos - md->member_hdr_pos;
950 else {
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);
957 done:
958 de_dbg_indent_restore(c, saved_indent_level);
961 static void do_pcxgxlib_main(deark *c, de_module_params *mparams, UI fmtcode)
963 i64 pos = 0;
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;
970 d->is_le = 1;
971 d->input_encoding = de_get_input_encoding(c, NULL, DE_ENCODING_CP437);
973 if(d->fmtcode==FMT_GXLIB) {
974 pos += 2;
975 pos += 50; // copyright message
976 d->fmtver = (int)de_getu16le_p(&pos);
977 de_dbg(c, "gxLib ver: %d", d->fmtver);
978 pos += 40; // label
979 d->num_members = de_getu16le_p(&pos);
980 de_dbg(c, "number of members: %"I64_FMT, d->num_members);
981 pos += 32; // unused
983 else {
984 pos += 10;
985 pos += 50;
986 d->fmtver = (int)de_getu16le_p(&pos);
987 de_dbg(c, "pcxLib ver: %d", d->fmtver);
988 pos += 40; // TODO: volume label
989 pos += 20; // unused
992 while(1) {
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) {
1000 u8 b;
1002 b = de_getbyte(pos);
1003 if(b != 0x01) goto done;
1006 if(md) {
1007 de_arch_destroy_md(c, md);
1008 md = NULL;
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;
1016 member_count++;
1019 done:
1020 if(md) {
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;
1034 return 0;
1037 void de_module_pcxlib(deark *c, struct deark_module_info *mi)
1039 mi->id = "pcxlib";
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)
1056 UI n;
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);
1063 has_ver = (n==100);
1064 if(has_copyr && has_ver) return 100;
1065 if(has_copyr || has_ver) return 25;
1066 return 0;
1069 void de_module_gxlib(deark *c, struct deark_module_info *mi)
1071 mi->id = "gxlib";
1072 mi->desc = "GX Library";
1073 mi->run_fn = de_run_gxlib;
1074 mi->identify_fn = de_identify_gxlib;
1077 // **************************************************************************
1078 // MDCD
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)
1090 deark *c = md->c;
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);
1102 else {
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)
1110 i64 pos;
1111 i64 s_len;
1112 i64 hdrlen;
1113 UI attr;
1114 int saved_indent_level;
1115 int retval = 0;
1116 int have_path = 0;
1117 u8 hdrtype;
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");
1129 else {
1130 de_dbg(c, "[member not found at %"I64_FMT"]", pos);
1132 goto done;
1134 pos += 4;
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);
1141 if(hdrtype!=1) {
1142 d->need_errmsg = 1;
1143 goto done;
1145 hdrlen = de_getu16le_p(&pos);
1146 de_dbg(c, "header len: %"I64_FMT, hdrlen);
1147 if(hdrlen<MDCD_MINHEADERLEN) {
1148 d->need_errmsg = 1;
1149 goto done;
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)) {
1161 goto done;
1163 md->member_total_size = hdrlen + md->cmpr_len;
1164 retval = 1;
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);
1176 if(s_len>12) {
1177 d->need_errmsg = 1;
1178 goto done;
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));
1183 pos += 12;
1185 if(hdrlen>=55) {
1186 s_len = de_getbyte_p(&pos);
1188 else {
1189 s_len = 0;
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]=='/'))
1201 have_path = 0;
1203 if(have_path && md->tmpfn_path->len >= 2 && md->tmpfn_path->str[1]==':') {
1204 have_path = 0;
1207 if(have_path) {
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);
1218 goto done;
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);
1233 done:
1234 de_dbg_indent_restore(c, saved_indent_level);
1235 return retval;
1238 static void de_run_mdcd(deark *c, de_module_params *mparams)
1240 de_arch_lctx *d = NULL;
1241 i64 pos = 0;
1242 struct de_arch_member_data *md = NULL;
1244 d = de_arch_create_lctx(c);
1245 d->is_le = 1;
1246 d->input_encoding = de_get_input_encoding(c, NULL, DE_ENCODING_CP437);
1247 d->crco = de_crcobj_create(c, DE_CRCOBJ_CRC16_XMODEM);
1249 while(1) {
1250 if(pos+MDCD_MINHEADERLEN >= c->infile->len) goto done;
1252 if(md) {
1253 de_arch_destroy_md(c, md);
1254 md = NULL;
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;
1263 done:
1264 if(md) {
1265 de_arch_destroy_md(c, md);
1267 if(d) {
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)) {
1278 return 91;
1280 return 0;
1283 void de_module_mdcd(deark *c, struct deark_module_info *mi)
1285 mi->id = "mdcd";
1286 mi->desc = "MDCD archive";
1287 mi->run_fn = de_run_mdcd;
1288 mi->identify_fn = de_identify_mdcd;
1291 // **************************************************************************
1292 // CAZIP
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;
1299 i64 pos;
1300 UI verfield, field10, field12, field18;
1302 d = de_arch_create_lctx(c);
1303 d->is_le = 1;
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);
1309 pos = 8;
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");
1323 md->cmpr_pos = pos;
1324 md->cmpr_len = c->infile->len - md->cmpr_pos;
1325 if(!de_arch_good_cmpr_data_pos(md)) {
1326 goto done;
1329 md->dfn = dclimplode_decompressor_fn;
1330 md->validate_crc = 1;
1331 de_arch_extract_member_file(md);
1333 done:
1334 if(md) {
1335 de_arch_destroy_md(c, md);
1336 md = NULL;
1338 if(d) {
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)) {
1347 return 0;
1350 return 100;
1353 void de_module_cazip(deark *c, struct deark_module_info *mi)
1355 mi->id = "cazip";
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)
1375 i64 pos;
1376 i64 namelen;
1377 int saved_indent_level;
1378 int retval = 0;
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");
1387 else {
1388 de_err(c, "Bad data found at %"I64_FMT, pos);
1390 goto done;
1392 pos += 4;
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);
1403 if(namelen>255) {
1404 d->need_errmsg = 1;
1405 goto done;
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));
1411 pos += namelen;
1413 md->cmpr_pos = pos;
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)) {
1416 goto done;
1419 md->member_total_size = md->cmpr_pos + md->cmpr_len - md->member_hdr_pos;
1420 retval = 1;
1422 md->dfn = dclimplode_decompressor_fn;
1423 de_arch_extract_member_file(md);
1425 done:
1426 de_dbg_indent_restore(c, saved_indent_level);
1427 return retval;
1430 static void de_run_cmz(deark *c, de_module_params *mparams)
1432 de_arch_lctx *d = NULL;
1433 i64 pos = 0;
1434 struct de_arch_member_data *md = NULL;
1436 d = de_arch_create_lctx(c);
1437 d->is_le = 1;
1438 d->input_encoding = de_get_input_encoding(c, NULL, DE_ENCODING_CP437);
1440 while(1) {
1441 if(pos+CMZ_MINHEADERLEN >= c->infile->len) goto done;
1443 if(md) {
1444 de_arch_destroy_md(c, md);
1445 md = NULL;
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;
1454 done:
1455 if(md) {
1456 de_arch_destroy_md(c, md);
1457 md = NULL;
1459 if(d) {
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)
1469 i64 name_len;
1470 i64 cmpr_len;
1472 if(!cmz_sig_at(c, 0)) {
1473 return 0;
1476 cmpr_len = de_getu32le(4);
1477 name_len = de_getu16le(16);
1478 if(cmpr_len==0 && name_len>0) {
1479 return 60;
1481 if(dclimplode_header_at(c, 20+name_len)) {
1482 return 100;
1485 return 0;
1488 void de_module_cmz(deark *c, struct deark_module_info *mi)
1490 mi->id = "cmz";
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
1502 // Returns:
1503 // 0 - not SHR
1504 // 1 - old format
1505 // 2 - new format
1506 static int detect_pcshrink_internal(deark *c, u8 *pmultipart_flag)
1508 u8 b;
1509 UI n;
1510 i64 cmpr_len;
1512 *pmultipart_flag = 0;
1513 b = de_getbyte(13);
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);
1519 if(cmpr_len!=0) {
1520 if(!dclimplode_header_at(c, 104)) return 0;
1522 return 1;
1524 else if(b==0) { // maybe new format
1525 if(de_getu16le(14)!=0x74) return 0;
1526 n = (UI)de_getu16le(18);
1527 if(n==0x75) {
1528 *pmultipart_flag = 1;
1530 else if(n!=0x74) {
1531 return 0;
1533 if(n==0x74) {
1534 if(de_getbyte(58)==0) return 0; // 1st byte of filename
1535 cmpr_len = de_getu32le(194);
1536 if(cmpr_len!=0) {
1537 if(!dclimplode_header_at(c, 226)) return 0;
1540 if(de_getbyte(0)!=0) {
1541 *pmultipart_flag = 1;
1543 return 2;
1545 return 0;
1548 static int do_pcshrink_member(deark *c, de_arch_lctx *d, struct de_arch_member_data *md)
1550 i64 pos;
1551 int saved_indent_level;
1552 int retval = 0;
1553 i64 fnfieldlen;
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);
1560 if(d->fmtver==2) {
1561 fnfieldlen = 128;
1563 else {
1564 fnfieldlen = 14;
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));
1570 if(d->fmtver==2) {
1571 de_arch_fixup_path(md->filename, 0);
1572 md->set_name_flags |= DE_SNFLAG_FULLPATH;
1574 pos += fnfieldlen;
1576 pos++; // attributes?
1578 if(d->fmtver==2) {
1579 pos += 7;
1581 else {
1582 pos += 5;
1585 de_arch_read_field_cmpr_len_p(md, &pos);
1587 if(d->fmtver==2) {
1588 de_arch_read_field_dttm_p(d, &md->fi->timestamp[DE_TIMESTAMPIDX_MODIFY], "mod",
1589 DE_ARCH_TSTYPE_DOS_DXT, &pos);
1591 else {
1592 de_arch_read_field_dttm_p(d, &md->fi->timestamp[DE_TIMESTAMPIDX_MODIFY], "mod",
1593 DE_ARCH_TSTYPE_DOS_DT, &pos);
1596 if(d->fmtver==2) {
1597 md->cmpr_pos = md->member_hdr_pos + 168;
1599 else {
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)) {
1605 goto done;
1608 md->member_total_size = md->cmpr_pos + md->cmpr_len - md->member_hdr_pos;
1609 retval = 1;
1611 md->dfn = dclimplode_decompressor_fn;
1612 de_arch_extract_member_file(md);
1614 done:
1615 de_dbg_indent_restore(c, saved_indent_level);
1616 return retval;
1619 static void de_run_pcshrink(deark *c, de_module_params *mparams)
1621 de_arch_lctx *d = NULL;
1622 i64 pos = 0;
1623 i64 idx;
1624 u8 multipart_flag;
1625 struct de_arch_member_data *md = NULL;
1627 d = de_arch_create_lctx(c);
1628 d->is_le = 1;
1630 // Detect version
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");
1634 goto done;
1636 de_dbg(c, "format version: %d", d->fmtver);
1638 // Read archive header
1639 if(d->fmtver==2) {
1640 d->num_members = de_getu16le(16);
1642 else {
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);
1650 if(d->fmtver==2) {
1651 pos = 58;
1653 else {
1654 pos = 56;
1657 if(multipart_flag) {
1658 de_err(c, "Multi-part PC-Shrink files are not supported");
1659 goto done;
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");
1665 goto done;
1668 if(md) {
1669 de_arch_destroy_md(c, md);
1670 md = NULL;
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;
1679 done:
1680 if(md) {
1681 de_arch_destroy_md(c, md);
1682 md = NULL;
1684 if(d) {
1685 de_arch_destroy_lctx(c, d);
1689 static int de_identify_pcshrink(deark *c)
1691 u8 multipart_flag;
1692 int ver;
1694 ver = detect_pcshrink_internal(c, &multipart_flag);
1695 if(ver==1) {
1696 return 60;
1698 else if(ver==2) {
1699 if(multipart_flag) return 40;
1700 return 80;
1703 return 0;
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
1719 // or misleading.
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)
1733 i64 fnlen;
1734 UI fver_maj1, fver_min1;
1735 UI fver_maj2, fver_min2;
1736 i64 pos = pos1;
1737 UI attr;
1739 fnlen = (i64)de_getbyte_p(&pos);
1740 dbuf_read_to_ucstring(c->infile, pos, fnlen, md->filename, 0,
1741 d->input_encoding);
1742 de_dbg(c, "filename: \"%s\"", ucstring_getpsz_d(md->filename));
1743 pos += fnlen;
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;
1772 i64 arcv_hdr_len;
1773 i64 chnk_pos;
1774 i64 chnk_hdr_len;
1775 i64 chnk_dlen;
1776 i64 pos;
1777 i64 nbytes_consumed = 0;
1778 u32 id;
1779 int saved_indent_level;
1781 de_dbg_indent_save(c, &saved_indent_level);
1782 md = de_arch_create_md(c, d);
1784 pos = 6;
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
1793 chnk_pos = pos;
1794 id = (u32)de_getu32be_p(&pos);
1795 if(id != CODE_CHNK) {
1796 d->need_errmsg = 1;
1797 goto done;
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);
1802 pos += 4; // flags?
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);
1807 done:
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)
1814 i64 pos = 0;
1815 i64 arcv_hdr_len;
1816 i64 blck_hdr_len;
1817 i64 blck_dlen;
1818 u32 id;
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);
1827 pos = arcv_hdr_len;
1828 while(1) {
1829 i64 nbytes_consumed = 0;
1831 if(pos >= c->infile->len) {
1832 goto done;
1834 de_dbg(c, "member at %"I64_FMT, pos);
1836 if(md) {
1837 de_arch_destroy_md(c, md);
1838 md = NULL;
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);
1845 if(id!=CODE_BLCK) {
1846 de_dbg(c, "can't find item at %"I64_FMT, md->member_hdr_pos);
1847 goto done;
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);
1852 pos += 4; // ?
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;
1862 md->cmpr_pos = pos;
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);
1869 done:
1870 de_dbg_indent_restore(c, saved_indent_level);
1871 if(md) {
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;
1879 UI ver_maj;
1881 d = de_arch_create_lctx(c);
1882 d->is_le = 1;
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);
1888 if(ver_maj==1) {
1889 do_arcv_v1(c, d);
1891 else if(ver_maj==2) {
1892 do_arcv_v2(c, d);
1894 else {
1895 d->need_errmsg = 1;
1896 goto done;
1899 done:
1900 if(d) {
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)
1910 u8 ver;
1912 if((UI)de_getu32be(0) != CODE_ARCV) return 0;
1913 ver = de_getbyte(5);
1914 if(ver==1 || ver==2) {
1915 return 100;
1917 return 0;
1920 void de_module_arcv(deark *c, struct deark_module_info *mi)
1922 mi->id = "arcv";
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;
1936 i64 pos = 0;
1937 UI id;
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);
1943 d->is_le = 1;
1944 d->input_encoding = de_get_input_encoding(c, NULL, DE_ENCODING_CP437);
1946 while(1) {
1947 u8 b;
1949 if(pos >= c->infile->len) goto done;
1950 if(md) {
1951 de_arch_destroy_md(c, md);
1952 md = NULL;
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);
1962 goto done;
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,
1975 d->input_encoding);
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;
1987 done:
1988 de_dbg_indent_restore(c, saved_indent_level);
1989 if(md) {
1990 de_arch_destroy_md(c, md);
1991 md = NULL;
1993 if(d) {
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;
2004 return 100;
2007 void de_module_red(deark *c, struct deark_module_info *mi)
2009 mi->id = "red";
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)
2026 i64 val = 0;
2027 i64 i;
2028 i64 pos = pos1;
2030 for(i=0; i<len; i++) {
2031 u8 b;
2032 i64 nv;
2034 b = dbuf_getbyte_p(f, &pos);
2035 if(b>='0' && b<='9') {
2036 nv = b - 48;
2038 else if(b>='a' && b<='f') {
2039 nv = b - 87;
2041 else {
2042 *perrflag = 1;
2043 return 0;
2046 val = (val<<4) | nv;
2048 return val;
2051 static int lif_kdc_convert_hdr(deark *c, i64 pos1, dbuf *f2)
2053 i64 pos = pos1;
2054 int i;
2055 int errorflag = 0;
2057 for(i=0; i<17; i++) {
2058 u8 b0, b1;
2059 u8 x0, x1;
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));
2069 return 1;
2072 static void lif_method2_decompressor_fn(struct de_arch_member_data *md)
2074 deark *c = md->c;
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;
2085 i64 pos = 0;
2086 struct de_arch_member_data *md = NULL;
2087 dbuf *f2 = NULL;
2088 int saved_indent_level;
2090 de_dbg_indent_save(c, &saved_indent_level);
2091 d = de_arch_create_lctx(c);
2092 d->is_le = 0;
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);
2097 while(1) {
2098 i64 f2_pos;
2099 u32 crc1_reported;
2101 if(pos >= c->infile->len) goto done;
2102 if(md) {
2103 de_arch_destroy_md(c, md);
2104 md = NULL;
2107 dbuf_empty(f2);
2108 // Decode the hex-encoded part of the header, so that we can read it
2109 // more easily.
2110 if(!lif_kdc_convert_hdr(c, pos, f2)) {
2111 d->need_errmsg = 1;
2112 goto done;
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);
2122 d->inf = f2;
2123 f2_pos = 0;
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);
2137 d->inf = c->infile;
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,
2142 d->input_encoding);
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);
2157 else {
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;
2165 done:
2166 de_dbg_indent_restore(c, saved_indent_level);
2167 if(md) {
2168 de_arch_destroy_md(c, md);
2169 md = NULL;
2171 if(d) {
2172 if(d->need_errmsg) {
2173 de_err(c, "Bad or unsupported LIF file");
2175 de_arch_destroy_lctx(c, d);
2177 dbuf_close(f2);
2180 static int de_identify_lif_kdc(deark *c)
2182 i64 cmprmeth;
2183 int errflag = 0;
2184 int has_ext;
2185 u8 b;
2186 i64 i;
2187 i64 n[4];
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
2194 if(b<32) return 0;
2195 b = de_getbyte(53); // last char of NUL-padded filename field??
2196 if(b!=0) return 0;
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)
2210 mi->id = "lif_kdc";
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
2221 // some checksums.
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.
2227 *pchksum ^= 0x5555;
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;
2237 u8 b;
2238 UI upd_speed;
2239 UI cmpr_meth;
2240 UI member_hdrs_checksum_reported;
2241 UI member_hdrs_checksum_calc = 0;
2242 UI volume;
2243 i64 pos;
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");
2250 pos = 1;
2251 b = de_getbyte_p(&pos);
2252 upd_speed = b >> 4;
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);
2257 pos += 1; // ?
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);
2264 pos = 8;
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);
2282 pos += 2; // ?
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);
2299 d->is_le = 1;
2300 do_ain_main(c, d);
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;
2308 int has_ext;
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)
2324 mi->id = "ain";
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)
2337 u8 sig[2];
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
2345 done:
2346 return ext;
2349 static void de_run_hta(deark *c, de_module_params *mparams)
2351 struct de_arch_member_data *md = NULL;
2352 i64 pos;
2353 i64 num_members;
2354 UI fmtver;
2355 i64 idx;
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);
2363 d->is_le = 1;
2365 pos = 8;
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++) {
2374 i64 endpos;
2375 const char *ext;
2377 if(md) {
2378 de_arch_destroy_md(c, md);
2379 md = NULL;
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);
2403 done:
2404 de_dbg_indent_restore(c, saved_indent_level);
2405 if(md) {
2406 de_arch_destroy_md(c, md);
2407 md = NULL;
2409 if(d) {
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;
2420 return 0;
2423 void de_module_hta(deark *c, struct deark_module_info *mi)
2425 mi->id = "hta";
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;
2440 UI x;
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);
2447 x &= 0xff;
2448 de_crcobj_destroy(crco);
2449 return x;
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;
2455 i64 fnlen;
2456 UI hdr_checksum_reported;
2457 UI hdr_checksum_calc;
2458 UI flags_and_cmpr_meth;
2459 int saved_indent_level;
2460 int retval = 0;
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);
2484 pos += 1; // ??
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);
2494 pos += 2; // ??
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,
2501 d->input_encoding);
2502 de_dbg(c, "filename: \"%s\"", ucstring_getpsz_d(md->filename));
2503 pos += fnlen;
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;
2508 retval = 1;
2509 de_dbg_indent(c, -1);
2511 done:
2512 de_dbg_indent_restore(c, saved_indent_level);
2513 return retval;
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;
2520 i64 pos;
2522 d = de_arch_create_lctx(c);
2523 d->is_le = 1;
2524 d->input_encoding = de_get_input_encoding(c, NULL, DE_ENCODING_CP437);
2526 pos = 2;
2528 while(1) {
2529 if(pos+HIT_MINHEADERLEN > c->infile->len) goto done;
2531 if(md) {
2532 de_arch_destroy_md(c, md);
2533 md = NULL;
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;
2543 done:
2544 if(md) {
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"
2554 return 45;
2557 void de_module_hit(deark *c, struct deark_module_info *mi)
2559 mi->id = "hit";
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)
2579 i64 pos1;
2580 i64 pos;
2581 UI filesize_hi;
2582 i64 space_reqd_in_blocks;
2583 i64 fnlen;
2584 UI auxtype, auxtype_hi;
2585 UI accesscode, accesscode_hi;
2586 UI filetype, filetype_hi;
2587 UI storagetype, storagetype_hi;
2588 UI eof_hi;
2589 UI crdate_raw;
2590 UI crtime_raw;
2591 UI moddate_raw;
2592 UI modtime_raw;
2593 UI ostype;
2594 UI data_flags;
2595 u8 fmt_ver;
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.
2604 pos = pos1 + 109;
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);
2612 pos = pos1 + 3;
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");
2645 pos = pos1 + 20;
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;
2651 pos += 3;
2653 fnlen = de_getbyte_p(&pos);
2654 if(fnlen>64) {
2655 d->need_errmsg = 1;
2656 goto done;
2658 dbuf_read_to_ucstring(c->infile, pos, fnlen, md->filename, DE_CONVFLAG_STOP_AT_NUL,
2659 d->input_encoding);
2660 de_dbg(c, "filename: \"%s\"", ucstring_getpsz_d(md->filename));
2662 pos = pos1 + 117;
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);
2681 md->cmpr_pos = pos;
2682 md->dfn = noncompressed_decompressor_fn;
2683 de_arch_extract_member_file(md);
2685 done:
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;
2693 if(check_nr) {
2694 int nr;
2696 nr = (int)dbuf_getbyte(f, pos+127);
2697 if(nr != nr_expected) return 0;
2699 return 1;
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;
2707 i64 pos = 0;
2709 b2_md = de_malloc(c, sizeof(struct binary_ii_extra_md));
2711 d = de_arch_create_lctx(c);
2712 d->is_le = 1;
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)) {
2716 d->need_errmsg = 1;
2717 goto done;
2720 while(1) {
2721 i64 npos1, npos2;
2722 int ret;
2724 if(md) {
2725 de_arch_destroy_md(c, md);
2726 md = NULL;
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);
2745 if(ret) {
2746 pos = npos1;
2748 else if(npos2!=npos1) {
2749 ret = binary_ii_is_member_at(c->infile, npos2, 1, b2_md->num_members_remaining-1);
2750 if(ret) {
2751 pos = npos2;
2755 if(!ret) {
2756 d->need_errmsg = 1;
2757 goto done;
2761 done:
2762 if(md) {
2763 de_arch_destroy_md(c, md);
2765 if(d) {
2766 if(d->need_errmsg) {
2767 de_err(c, "Bad or unsupported Binary II file");
2769 de_arch_destroy_lctx(c, d);
2771 de_free(c, b2_md);
2774 static int de_identify_binary_ii(deark *c)
2776 if(!binary_ii_is_member_at(c->infile, 0, 0, 0)) return 0;
2777 return 90;
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)
2796 u8 escchar;
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);
2803 while(1) {
2804 u8 b0, b1, b2;
2805 i64 ctmp;
2806 i64 count = 0;
2807 u8 val = 0;
2809 if(pos >= endpos) goto done;
2811 b0 = dbuf_getbyte_p(md->dcmpri->f, &pos);
2812 if(b0 != escchar) {
2813 dbuf_writebyte(md->dcmpro->f, b0);
2814 nbytes_written++;
2815 continue;
2818 b1 = dbuf_getbyte_p(md->dcmpri->f, &pos);
2819 if(b1==0) {
2820 count = 1;
2821 val = escchar;
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);
2830 count = 512 + ctmp;
2831 val = dbuf_getbyte_p(md->dcmpri->f, &pos);
2833 else if(b1>3) { // small run length
2834 count = (i64)b1;
2835 val = dbuf_getbyte_p(md->dcmpri->f, &pos);
2837 else {
2838 de_dfilter_set_generic_error(md->c, md->dres, modname);
2839 goto done;
2841 dbuf_write_run(md->dcmpro->f, val, count);
2842 nbytes_written += count;
2844 done:
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)
2853 deark *c = md->c;
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);
2861 else {
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;
2868 i64 seq_num;
2869 i64 pos = md->member_hdr_pos;
2870 de_ucstring *fn_ext = NULL;
2871 u8 ver;
2872 u8 ftype;
2873 u8 aflag;
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);
2882 if(seq_num == 0) {
2883 d->stop_flag = 1;
2884 goto done;
2886 if(seq_num != md->member_idx) {
2887 d->fatalerrflag = 1;
2888 d->need_errmsg = 1;
2889 goto done;
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,
2894 d->input_encoding);
2895 pos += 8;
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,
2900 d->input_encoding);
2901 pos += 3;
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);
2915 if(ver != 1) {
2916 d->fatalerrflag = 1;
2917 d->need_errmsg = 1;
2918 goto done;
2921 md->cmpr_len = dbuf_getint_ext(c->infile, pos, 3, 0, 0);
2922 pos += 3;
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);
2928 md->cmpr_pos = pos;
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);
2934 goto done;
2937 md->dfn = tc_decompressor_fn;
2938 de_arch_extract_member_file(md);
2940 done:
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;
2949 i64 pos = 0;
2950 i64 seq_num = 0;
2952 d = de_arch_create_lctx(c);
2953 d->is_le = 0;
2954 d->input_encoding = de_get_input_encoding(c, NULL, DE_ENCODING_ASCII);
2956 while(1) {
2957 if(pos >= c->infile->len) {
2958 d->fatalerrflag = 1;
2959 d->need_errmsg = 1;
2960 goto done;
2963 if(md) {
2964 de_arch_destroy_md(c, md);
2965 md = NULL;
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) {
2974 goto done;
2976 pos += md->member_total_size;
2979 done:
2981 if(md) {
2982 de_arch_destroy_md(c, md);
2984 if(d) {
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)
2994 int has_ext = 0;
2995 u8 b;
2996 i64 n;
2997 i64 i;
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);
3005 if(b<32) return 0;
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;
3016 if(b!=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;
3023 else return 15;
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;