bmp: Rewrote the RLE decompressor
[deark.git] / modules / dms.c
blob631c8c04668979a4526f0b32e551f5af26a379d5
1 // This file is part of Deark.
2 // Copyright (C) 2020 Jason Summers
3 // See the file COPYING for terms of use.
5 // Amiga DMS (Disk Masher System) disk image
7 // The DMS module was developed with the help of information from xDMS -
8 // public domain(-ish) software by Andre Rodrigues de la Rocha.
10 // Note:
11 // DMS has a feature, which I guess is its version of a "solid" archive, whereby
12 // part of the state of some decompressors is reset only after processing a
13 // track for which the low bit of the track_flags field is 0. Otherwise it
14 // persists, to be used by the next suitable track.
15 // I don't understand the fine details of how this works, especially when it
16 // comes to "extra" tracks that are not part of the main track sequence.
17 // xDMS behaves as if the state from an extra track never persists into a
18 // "real" track, or vice versa, even if its flag says it does. I have a file
19 // that seems to confirm that that is the case.
20 // But that still leaves a lot of open questions. What's the precise
21 // definition of an extra track? What if there are multiple compressed extra
22 // tracks? Or an extra track in the middle of the real tracks? To what extent
23 // can multiple compression methods be used in the same file, and how do they
24 // interact?
26 #include <deark-private.h>
27 #include <deark-fmtutil.h>
29 DE_DECLARE_MODULE(de_module_amiga_dms);
31 // Used as both the maximum number of physical tracks in the file, and (one more
32 // than) the highest logical track number allowed for a "real" track.
33 #define DMS_MAX_TRACKS 200
35 #define DMS_FILE_HDR_LEN 56
36 #define DMS_TRACK_HDR_LEN 20
38 #define DMSCMPR_NONE 0
39 #define DMSCMPR_RLE 1
40 #define DMSCMPR_QUICK 2
41 #define DMSCMPR_MEDIUM 3
42 #define DMSCMPR_DEEP 4
43 #define DMSCMPR_HEAVY1 5
44 #define DMSCMPR_HEAVY2 6
46 struct dms_track_info {
47 i64 track_num; // The reported (logical) track number
48 i64 dpos;
49 i64 cmpr_len;
50 i64 intermediate_len;
51 i64 uncmpr_len;
52 UI track_flags;
53 UI cmpr_type;
54 u8 is_real;
55 u32 cksum_reported;
56 u32 crc_cmprdata_reported;
57 u32 crc_header_reported;
58 u32 cksum_calc;
59 char shortname[80];
62 struct dms_tracks_by_file_order_entry {
63 i64 file_pos;
64 u32 track_num;
65 u8 is_real;
68 struct dms_tracks_by_track_num_entry {
69 u32 order_in_file;
70 u8 in_use;
73 struct dms_std_cmpr_state;
74 struct dmsheavy_cmpr_state;
76 struct std_saved_state_wrapper {
77 struct dms_std_cmpr_state *saved_state;
80 struct dmsctx {
81 UI info_bits;
82 UI cmpr_type;
83 i64 first_track, last_track;
84 i64 num_tracks_in_file;
85 u8 have_first_last_track_info;
86 struct de_crcobj *crco_cksum;
88 // Entries in use: 0 <= n < .num_tracks_in_file
89 struct dms_tracks_by_file_order_entry tracks_by_file_order[DMS_MAX_TRACKS];
91 // Entries potentially in use: .first_track <= n <= .last_track
92 struct dms_tracks_by_track_num_entry tracks_by_track_num[DMS_MAX_TRACKS];
94 struct std_saved_state_wrapper saved_quick_state;
95 struct std_saved_state_wrapper saved_medium_state;
96 struct std_saved_state_wrapper saved_deep_cmpr_state;
97 struct dmsheavy_cmpr_state *saved_heavy_state;
100 // For some decompressors, the portion of the decompression context that can
101 // persist between tracks
102 struct dms_std_cmpr_state {
103 UI cmpr_meth;
104 const char *cmpr_meth_name;
106 struct de_dfilter_ctx *dfctx_codec1;
107 struct de_dfilter_ctx *dfctx_rle;
108 i64 intermediate_nbytes;
110 dbuf *outf_codec1;
111 struct de_dfilter_out_params dcmpro1;
112 struct de_dfilter_results dres1;
115 static const char *dms_get_cmprtype_name(UI n)
117 const char *name = NULL;
118 switch(n) {
119 case DMSCMPR_NONE: name="uncompressed"; break;
120 case DMSCMPR_RLE: name="simple (RLE)"; break;
121 case DMSCMPR_QUICK: name="quick"; break;
122 case DMSCMPR_MEDIUM: name="medium (RLE + LZ77)"; break;
123 case DMSCMPR_DEEP: name="deep (RLE + LZ77+dynamic_huffman)"; break;
124 case DMSCMPR_HEAVY1: name="heavy1 (optional RLE + LZ77-4K+Huffman)"; break;
125 case DMSCMPR_HEAVY2: name="heavy2 (optional RLE + LZ77-8K+Huffman)"; break;
127 return name?name:"?";
130 static void read_unix_timestamp(deark *c, i64 pos, struct de_timestamp *ts, const char *name)
132 i64 t;
133 char timestamp_buf[64];
135 t = de_geti32be(pos);
136 de_unix_time_to_timestamp(t, ts, 0x1);
137 de_timestamp_to_string(ts, timestamp_buf, sizeof(timestamp_buf), 0);
138 de_dbg(c, "%s: %"I64_FMT" (%s)", name, t, timestamp_buf);
141 static void destroy_std_cmpr_state(deark *c, struct dms_std_cmpr_state *cst)
143 if(!cst) return;
144 if(cst->dfctx_codec1) {
145 de_dfilter_destroy(cst->dfctx_codec1);
147 if(cst->dfctx_rle) {
148 de_dfilter_destroy(cst->dfctx_rle);
150 dbuf_close(cst->outf_codec1);
151 de_free(c, cst);
154 static void my_std1_write_cb(dbuf *f, void *userdata,
155 const u8 *buf, i64 size)
157 struct dms_std_cmpr_state *u = (struct dms_std_cmpr_state*)userdata;
159 if(!u->dfctx_rle) {
160 de_internal_err_fatal(f->c, "%s", u->cmpr_meth_name);
162 de_dfilter_addbuf(u->dfctx_rle, buf, size);
163 u->intermediate_nbytes += size;
166 /////// Heavy (LZH) compression ///////
168 // Note: A lot of this is very similar to the code in fmtutil-lzh.c.
169 // The main problem with using standard LZH code for DMS is that some of the
170 // decompression state persists from one track to the next. But not all of it
171 // -- you can't just concatenate the compressed data together before
172 // decompressing it.
174 struct lzh_tree_wrapper {
175 struct fmtutil_huffman_decoder *ht;
178 // The portion of the Heavy decompression context that can persist between tracks.
179 struct dmsheavy_cmpr_state {
180 UI cmpr_type;
181 UI heavy_prev_offset;
182 struct de_lz77buffer *ringbuf;
183 u8 trees_exist;
184 struct lzh_tree_wrapper literals_tree;
185 struct lzh_tree_wrapper offsets_tree;
188 // The portion of the Heavy decompression context that does *not* persist between tracks.
189 struct lzh_ctx {
190 deark *c;
191 struct de_dfilter_in_params *dcmpri;
192 struct de_dfilter_out_params *dcmpro;
193 struct de_dfilter_results *dres;
194 const char *modname;
196 i64 nbytes_written;
197 int err_flag;
199 // bitrd.eof_flag: Always set if err_flag is set.
200 struct de_bitreader bitrd;
202 UI heavy_np;
205 struct dmslzh_params {
206 UI cmpr_type; // 5=heavy1, 6=heavy2
207 u8 dms_track_flags;
208 struct dmsheavy_cmpr_state *heavy_state;
211 static void lzh_set_eof_flag(struct lzh_ctx *cctx)
213 cctx->bitrd.eof_flag = 1;
216 static void lzh_set_err_flag(struct lzh_ctx *cctx)
218 lzh_set_eof_flag(cctx);
219 cctx->err_flag = 1;
222 static int lzh_have_enough_output(struct lzh_ctx *cctx)
224 if(cctx->dcmpro->len_known) {
225 if(cctx->nbytes_written >= cctx->dcmpro->expected_len) {
226 return 1;
229 return 0;
232 static void lha5like_lz77buf_writebytecb(struct de_lz77buffer *rb, u8 n)
234 struct lzh_ctx *cctx = (struct lzh_ctx*)rb->userdata;
236 if(lzh_have_enough_output(cctx)) {
237 return;
239 dbuf_writebyte(cctx->dcmpro->f, n);
240 cctx->nbytes_written++;
243 static UI read_next_code_using_tree(struct lzh_ctx *cctx, struct lzh_tree_wrapper *tree)
245 fmtutil_huffman_valtype val = 0;
246 int ret;
248 if(!tree->ht) goto done;
250 ret = fmtutil_huffman_read_next_value(tree->ht->bk, &cctx->bitrd, &val, NULL);
251 if(cctx->bitrd.eof_flag) {
252 de_dfilter_set_errorf(cctx->c, cctx->dres, cctx->modname,
253 "Unexpected end of compressed data");
254 lzh_set_err_flag(cctx);
255 val = 0;
256 goto done;
258 else if(!ret) {
259 de_dfilter_set_errorf(cctx->c, cctx->dres, cctx->modname,
260 "Huffman decoding error");
261 lzh_set_err_flag(cctx);
262 val = 0;
263 goto done;
266 done:
267 return (UI)val;
270 static int dmsheavy_read_tree(struct lzh_ctx *cctx, struct lzh_tree_wrapper *htw,
271 UI ncodes_nbits, UI symlen_nbits)
273 deark *c = cctx->c;
274 UI ncodes;
275 UI curr_idx;
276 int retval = 0;
278 if(htw->ht) goto done;
280 ncodes = (UI)de_bitreader_getbits(&cctx->bitrd, ncodes_nbits);
281 de_dbg2(c, "num codes: %u", ncodes);
283 htw->ht = fmtutil_huffman_create_decoder(c, (i64)ncodes, (i64)ncodes);
285 if(ncodes==0) {
286 UI null_val;
288 null_val = (UI)de_bitreader_getbits(&cctx->bitrd, ncodes_nbits);
289 fmtutil_huffman_add_code(c, htw->ht->bk, 0, 0, (fmtutil_huffman_valtype)null_val);
290 de_dbg3(c, "val0: %u", null_val);
291 retval = 1;
292 goto done;
295 curr_idx = 0;
296 while(curr_idx < ncodes) {
297 UI symlen;
299 symlen = (UI)de_bitreader_getbits(&cctx->bitrd, symlen_nbits);
300 de_dbg3(c, "len[%u] = %u", curr_idx, symlen);
301 fmtutil_huffman_record_a_code_length(c, htw->ht->builder, (fmtutil_huffman_valtype)curr_idx, symlen);
302 curr_idx++;
304 if(cctx->bitrd.eof_flag) goto done;
306 if(!fmtutil_huffman_make_canonical_code(c, htw->ht->bk, htw->ht->builder, 0, NULL)) goto done;
308 retval = 1;
309 done:
310 if(!retval) {
311 lzh_set_err_flag(cctx);
313 return retval;
316 static void dmsheavy_discard_tree(deark *c, struct lzh_tree_wrapper *htw)
318 if(htw->ht) {
319 fmtutil_huffman_destroy_decoder(c, htw->ht);
320 htw->ht = NULL;
324 static void decompress_dms_heavy(struct lzh_ctx *cctx, struct dmslzh_params *lzhp,
325 struct dmsheavy_cmpr_state *hvst)
327 deark *c = cctx->c;
328 UI rb_size;
329 int ret;
330 int saved_indent_level;
331 char pos_descr[32];
333 de_dbg_indent_save(c, &saved_indent_level);
335 if(lzhp->cmpr_type != hvst->cmpr_type) {
336 de_dfilter_set_errorf(c, cctx->dres, cctx->modname,
337 "Mixing Heavy compression types is not supported");
338 goto done;
341 if(lzhp->cmpr_type==DMSCMPR_HEAVY1) {
342 rb_size = 4096;
343 cctx->heavy_np = 14; // for heavy1
345 else {
346 rb_size = 8192;
347 cctx->heavy_np = 15; // for heavy2
350 if(!hvst->ringbuf) {
351 hvst->ringbuf = de_lz77buffer_create(cctx->c, rb_size);
354 hvst->ringbuf->userdata = (void*)cctx;
355 hvst->ringbuf->writebyte_cb = lha5like_lz77buf_writebytecb;
357 if(!cctx->dcmpro->len_known) {
358 // I think we (may) have to know the output length, because zero-length Huffman
359 // codes are(?) possible, and unlike lh5 we aren't told how many codes there are.
360 de_dfilter_set_errorf(cctx->c, cctx->dres, cctx->modname, "Internal error");
361 goto done;
364 if(lzhp->dms_track_flags & 0x02) {
365 dmsheavy_discard_tree(c, &hvst->literals_tree);
366 dmsheavy_discard_tree(c, &hvst->offsets_tree);
367 hvst->trees_exist = 0;
370 if(!hvst->trees_exist) {
371 hvst->trees_exist = 1;
372 de_dbg2(c, "c tree");
373 de_dbg_indent(c, 1);
374 ret = dmsheavy_read_tree(cctx, &hvst->literals_tree, 9, 5);
375 de_dbg_indent(c, -1);
376 if(!ret) goto done;
378 de_dbg2(c, "p tree");
379 de_dbg_indent(c, 1);
380 ret = dmsheavy_read_tree(cctx, &hvst->offsets_tree, 5, 4);
381 de_dbg_indent(c, -1);
382 if(!ret) goto done;
385 de_bitreader_describe_curpos(&cctx->bitrd, pos_descr, sizeof(pos_descr));
386 de_dbg2(c, "cmpr data codes at %s", pos_descr);
387 de_dbg_indent(c, 1);
388 while(1) {
389 UI code;
391 if(cctx->bitrd.eof_flag) goto done;
392 if(lzh_have_enough_output(cctx)) goto done;
394 code = read_next_code_using_tree(cctx, &hvst->literals_tree);
395 if(cctx->bitrd.eof_flag) goto done;
396 if(c->debug_level>=3) {
397 de_dbg3(c, "code: %u (opos=%"I64_FMT")", code, cctx->nbytes_written);
400 if(code < 256) { // literal
401 de_lz77buffer_add_literal_byte(hvst->ringbuf, (u8)code);
403 else { // repeat previous bytes
404 UI offset;
405 UI length;
406 UI ocode1;
408 length = code-253;
409 ocode1 = read_next_code_using_tree(cctx, &hvst->offsets_tree);
410 if(cctx->bitrd.eof_flag) goto done;
412 if(ocode1 == cctx->heavy_np-1) {
413 offset = hvst->heavy_prev_offset;
415 else {
416 if(ocode1 < 1) {
417 offset = ocode1;
419 else {
420 UI ocode2;
422 ocode2 = (UI)de_bitreader_getbits(&cctx->bitrd, ocode1-1);
423 if(cctx->bitrd.eof_flag) goto done;
424 offset = ocode2 | (1U<<(ocode1-1));
426 hvst->heavy_prev_offset = offset;
429 de_lz77buffer_copy_from_hist(hvst->ringbuf,
430 (UI)(hvst->ringbuf->curpos-offset-1), length);
434 done:
435 de_dbg_indent_restore(c, saved_indent_level);
438 static void destroy_heavy_state(deark *c, struct dmsheavy_cmpr_state *hvst)
440 if(!hvst) return;
441 dmsheavy_discard_tree(c, &hvst->literals_tree);
442 dmsheavy_discard_tree(c, &hvst->offsets_tree);
443 de_lz77buffer_destroy(c, hvst->ringbuf);
446 static void dmslzh_codectype1(deark *c, struct de_dfilter_in_params *dcmpri,
447 struct de_dfilter_out_params *dcmpro, struct de_dfilter_results *dres,
448 void *codec_private_params)
450 struct dmslzh_params *lzhp = (struct dmslzh_params*)codec_private_params;
451 struct lzh_ctx *cctx = NULL;
452 struct dmsheavy_cmpr_state *hvst = NULL;
454 cctx = de_malloc(c, sizeof(struct lzh_ctx));
455 cctx->modname = "undmslzh";
456 cctx->c = c;
457 cctx->dcmpri = dcmpri;
458 cctx->dcmpro = dcmpro;
459 cctx->dres = dres;
460 cctx->bitrd.f = dcmpri->f;
461 cctx->bitrd.curpos = dcmpri->pos;
462 cctx->bitrd.endpos = dcmpri->pos + dcmpri->len;
464 if(lzhp->heavy_state) {
465 // If a previous decompression state exists, use it.
466 hvst = lzhp->heavy_state;
467 lzhp->heavy_state = NULL;
469 else {
470 hvst = de_malloc(c, sizeof(struct dmsheavy_cmpr_state));
471 hvst->cmpr_type = lzhp->cmpr_type;
474 decompress_dms_heavy(cctx, lzhp, hvst);
476 hvst->ringbuf->userdata = NULL;
477 hvst->ringbuf->writebyte_cb = NULL;
478 lzhp->heavy_state = hvst;
479 hvst = NULL;
481 if(cctx->err_flag) {
482 // A default error message
483 de_dfilter_set_errorf(c, dres, cctx->modname, "LZH decoding error");
484 goto done;
487 de_bitreader_skip_to_byte_boundary(&cctx->bitrd);
488 cctx->dres->bytes_consumed = cctx->bitrd.curpos - cctx->dcmpri->pos;
489 if(cctx->dres->bytes_consumed<0) {
490 cctx->dres->bytes_consumed = 0;
492 cctx->dres->bytes_consumed_valid = 1;
494 done:
495 if(hvst) destroy_heavy_state(c, hvst);
496 de_free(c, cctx);
499 /////// RLE compression ///////
501 // DMS RLE:
502 // n1 n2 n3 n4 n5
503 // ---------------------------------------------------------
504 // 0x90 0x00 emit 0x90
505 // 0x90 0x01..0xfe n3 emit n2 copies of n3
506 // 0x90 0xff n3 n4 n5 emit (n4#n5) copies of n3
507 // !0x90 emit n1
509 enum dmsrle_state {
510 DMSRLE_STATE_NEUTRAL = 0,
511 DMSRLE_STATE_90,
512 DMSRLE_STATE_90_N2,
513 DMSRLE_STATE_90_FF_N3,
514 DMSRLE_STATE_90_FF_N3_N4
517 struct dmsrle_ctx {
518 enum dmsrle_state state;
519 u8 n2, n3, n4;
522 static void dmsrle_codec_addbuf(struct de_dfilter_ctx *dfctx,
523 const u8 *buf, i64 buf_len)
525 i64 i;
526 struct dmsrle_ctx *rctx = (struct dmsrle_ctx*)dfctx->codec_private;
528 if(!rctx) goto done;
530 for(i=0; i<buf_len; i++) {
531 u8 n;
532 i64 count;
534 n = buf[i];
536 switch(rctx->state) {
537 case DMSRLE_STATE_NEUTRAL:
538 if(n==0x90) {
539 rctx->state = DMSRLE_STATE_90;
541 else {
542 dbuf_writebyte(dfctx->dcmpro->f, n);
544 break;
545 case DMSRLE_STATE_90:
546 if(n==0x00) {
547 dbuf_writebyte(dfctx->dcmpro->f, 0x90);
548 rctx->state = DMSRLE_STATE_NEUTRAL;
550 else {
551 rctx->n2 = n;
552 rctx->state = DMSRLE_STATE_90_N2;
554 break;
555 case DMSRLE_STATE_90_N2:
556 if(rctx->n2==0xff) {
557 rctx->n3 = n;
558 rctx->state = DMSRLE_STATE_90_FF_N3;
560 else {
561 count = (i64)rctx->n2;
562 dbuf_write_run(dfctx->dcmpro->f, n, count);
563 rctx->state = DMSRLE_STATE_NEUTRAL;
565 break;
566 case DMSRLE_STATE_90_FF_N3:
567 rctx->n4 = n;
568 rctx->state = DMSRLE_STATE_90_FF_N3_N4;
569 break;
570 case DMSRLE_STATE_90_FF_N3_N4:
571 count = (i64)(((UI)rctx->n4 << 8) | n);
572 dbuf_write_run(dfctx->dcmpro->f, rctx->n3, count);
573 rctx->state = DMSRLE_STATE_NEUTRAL;
574 break;
577 done:
581 static void dmsrle_codec_destroy(struct de_dfilter_ctx *dfctx)
583 struct dmsrle_ctx *rctx = (struct dmsrle_ctx*)dfctx->codec_private;
585 if(rctx) {
586 de_free(dfctx->c, rctx);
588 dfctx->codec_private = NULL;
591 // codec_private_params: Unused, should be NULL.
592 static void dmsrle_codec(struct de_dfilter_ctx *dfctx, void *codec_private_params)
594 struct dmsrle_ctx *rctx = NULL;
596 rctx = de_malloc(dfctx->c, sizeof(struct dmsrle_ctx));
597 rctx->state = DMSRLE_STATE_NEUTRAL;
598 dfctx->codec_private = (void*)rctx;
599 dfctx->codec_addbuf_fn = dmsrle_codec_addbuf;
600 dfctx->codec_finish_fn = NULL;
601 dfctx->codec_destroy_fn = dmsrle_codec_destroy;
604 ///////////////// Codec for the LZ77 part of "Medium" decompression //////////////
606 enum medlzst_enum {
607 MEDLZST_NEUTRAL=0,
608 MEDLZST_WAITING_FOR_OCODE1,
609 MEDLZST_WAITING_FOR_OCODE2
612 struct medium_state_machine {
613 enum medlzst_enum state;
614 // TODO: We don't really need this much state.
615 UI first_code;
616 UI ocode1_nbits;
617 UI ocode1;
618 UI ocode2_nbits;
619 UI ocode2;
620 UI tmp_code;
621 UI d_code1, d_len1;
622 UI d_code2, d_len2;
625 struct medium_ctx {
626 deark *c;
627 const char *modname;
628 struct de_dfilter_out_params *dcmpro;
629 struct de_dfilter_results *dres;
630 int errflag;
631 i64 nbytes_written;
632 struct de_lz77buffer *ringbuf;
633 struct de_bitbuf_lowlevel bbll;
634 struct medium_state_machine mdst;
637 static int medium_have_enough_output(struct medium_ctx *mctx)
639 if(mctx->dcmpro->len_known) {
640 if(mctx->nbytes_written >= mctx->dcmpro->expected_len) {
641 return 1;
644 return 0;
647 static void mediumlz77_codec_addbuf(struct de_dfilter_ctx *dfctx,
648 const u8 *buf, i64 buf_len)
650 struct medium_ctx *mctx = (struct medium_ctx *)dfctx->codec_private;
651 struct medium_state_machine *mdst = &mctx->mdst;
652 i64 bufpos = 0;
653 UI n;
654 UI offset_rel;
655 UI length;
657 if(dfctx->finished_flag || mctx->errflag) {
658 goto done;
661 while(1) {
662 if(medium_have_enough_output(mctx)) {
663 dfctx->finished_flag = 1;
664 goto done;
667 // Top off the bitbuf, if possible. 32 is an arbitrary number that's
668 // large enough for this format.
669 while(bufpos<buf_len && mctx->bbll.nbits_in_bitbuf<32) {
670 de_bitbuf_lowlevel_add_byte(&mctx->bbll, buf[bufpos++]);
673 if(mdst->state==MEDLZST_WAITING_FOR_OCODE1) goto read_ocode1;
674 if(mdst->state==MEDLZST_WAITING_FOR_OCODE2) goto read_ocode2;
676 // Make sure we have enough source data to read the flag bit,
677 // plus either the literal byte or the first_code.
678 if(mctx->bbll.nbits_in_bitbuf < 9) goto done;
680 n = (UI)de_bitbuf_lowlevel_get_bits(&mctx->bbll, 1);
682 if(n) { // literal byte
683 u8 b;
685 b = (u8)de_bitbuf_lowlevel_get_bits(&mctx->bbll, 8);
686 de_lz77buffer_add_literal_byte(mctx->ringbuf, (u8)b);
687 continue;
690 // TODO: This seems overly complicated. Is there a simpler way to
691 // implement this?
693 mdst->first_code = (UI)de_bitbuf_lowlevel_get_bits(&mctx->bbll, 8);
694 fmtutil_get_lzhuf_d_code_and_len(mdst->first_code, &mdst->d_code1, &mdst->d_len1);
696 read_ocode1:
697 mdst->ocode1_nbits = mdst->d_len1;
698 if(mctx->bbll.nbits_in_bitbuf < mdst->ocode1_nbits) {
699 mdst->state = MEDLZST_WAITING_FOR_OCODE1;
700 goto done;
702 mdst->ocode1 = (UI)de_bitbuf_lowlevel_get_bits(&mctx->bbll, mdst->ocode1_nbits);
704 mdst->tmp_code = ((mdst->first_code << mdst->ocode1_nbits) | mdst->ocode1) & 0xff;
705 fmtutil_get_lzhuf_d_code_and_len(mdst->tmp_code, &mdst->d_code2, &mdst->d_len2);
707 read_ocode2:
708 mdst->ocode2_nbits = mdst->d_len2;
709 if(mctx->bbll.nbits_in_bitbuf < mdst->ocode2_nbits) {
710 mdst->state = MEDLZST_WAITING_FOR_OCODE2;
711 goto done;
713 mdst->ocode2 = (UI)de_bitbuf_lowlevel_get_bits(&mctx->bbll, mdst->ocode2_nbits);
715 offset_rel = (mdst->d_code2 << 8) | (((mdst->tmp_code << mdst->ocode2_nbits) | mdst->ocode2) & 0xff);
716 length = mdst->d_code1 + 3;
717 de_lz77buffer_copy_from_hist(mctx->ringbuf, mctx->ringbuf->curpos - 1 - offset_rel, length);
718 mdst->state = MEDLZST_NEUTRAL;
721 done:
722 if(!dfctx->finished_flag && !mctx->errflag) {
723 if(bufpos<buf_len) {
724 // It shouldn't be possible to get here. If we do, it means some input
725 // bytes will be lost.
726 de_dfilter_set_generic_error(dfctx->c, mctx->dres, mctx->modname);
727 mctx->errflag = 1;
732 static void medium_lz77buf_writebytecb(struct de_lz77buffer *rb, u8 n)
734 struct medium_ctx *mctx = (struct medium_ctx *)rb->userdata;
736 if(medium_have_enough_output(mctx)) {
737 return;
739 dbuf_writebyte(mctx->dcmpro->f, n);
740 mctx->nbytes_written++;
743 static void mediumlz77_codec_command(struct de_dfilter_ctx *dfctx, int cmd, UI flags)
745 struct medium_ctx *mctx = (struct medium_ctx*)dfctx->codec_private;
747 if(cmd==DE_DFILTER_COMMAND_FINISH_BLOCK) {
748 de_bitbuf_lowlevel_empty(&mctx->bbll);
749 mctx->mdst.state = MEDLZST_NEUTRAL;
750 de_lz77buffer_set_curpos(mctx->ringbuf, mctx->ringbuf->curpos + 66);
752 else if(cmd==DE_DFILTER_COMMAND_RESET_COUNTERS) {
753 mctx->nbytes_written = 0;
754 mctx->errflag = 0;
755 dfctx->finished_flag = 0;
759 static void mediumlz77_codec_destroy(struct de_dfilter_ctx *dfctx)
761 struct medium_ctx *mctx = (struct medium_ctx*)dfctx->codec_private;
763 if(mctx) {
764 de_lz77buffer_destroy(dfctx->c, mctx->ringbuf);
765 de_free(dfctx->c, mctx);
767 dfctx->codec_private = NULL;
770 // codec_private_params: unused
771 static void dmsmediumlz77_codec(struct de_dfilter_ctx *dfctx, void *codec_private_params)
773 struct medium_ctx *mctx = NULL;
775 mctx = de_malloc(dfctx->c, sizeof(struct medium_ctx));
776 mctx->c = dfctx->c;
777 mctx->modname = "dmsmedium_lz77";
778 mctx->dcmpro = dfctx->dcmpro;
779 mctx->dres = dfctx->dres;
781 dfctx->codec_private = (void*)mctx;
782 dfctx->codec_addbuf_fn = mediumlz77_codec_addbuf;
783 dfctx->codec_command_fn = mediumlz77_codec_command;
784 dfctx->codec_destroy_fn = mediumlz77_codec_destroy;
786 mctx->ringbuf = de_lz77buffer_create(dfctx->c, 16*1024);
787 // The set_curpos isn't needed, but could help with debugging. It makes our
788 // internal state the same as most other DMS software.
789 de_lz77buffer_set_curpos(mctx->ringbuf, 0x3fbe);
791 mctx->ringbuf->userdata = (void*)mctx;
792 mctx->ringbuf->writebyte_cb = medium_lz77buf_writebytecb;
795 ///////////////// Codec for the LZ77 part of "Quick" decompression //////////////
797 // This was derived from the Medium codec. Some of the symbol names are
798 // misleading, because of that.
800 static void quicklz77_codec_addbuf(struct de_dfilter_ctx *dfctx,
801 const u8 *buf, i64 buf_len)
803 struct medium_ctx *mctx = (struct medium_ctx *)dfctx->codec_private;
804 struct medium_state_machine *mdst = &mctx->mdst;
805 i64 bufpos = 0;
806 UI n;
807 UI length;
808 UI ocode, lcode;
810 if(dfctx->finished_flag || mctx->errflag) {
811 goto done;
814 while(1) {
815 if(medium_have_enough_output(mctx)) {
816 dfctx->finished_flag = 1;
817 goto done;
820 // Top off the bitbuf, if possible. 32 is an arbitrary number that's
821 // large enough for this format.
822 while(bufpos<buf_len && mctx->bbll.nbits_in_bitbuf<32) {
823 de_bitbuf_lowlevel_add_byte(&mctx->bbll, buf[bufpos++]);
826 if(mdst->state==MEDLZST_WAITING_FOR_OCODE1) goto read_ocode1;
828 // Minimum useful number of bits is 9.
829 if(mctx->bbll.nbits_in_bitbuf < 9) goto done;
831 n = (UI)de_bitbuf_lowlevel_get_bits(&mctx->bbll, 1);
832 if(n) { // literal byte
833 u8 b;
835 b = (u8)de_bitbuf_lowlevel_get_bits(&mctx->bbll, 8);
836 de_lz77buffer_add_literal_byte(mctx->ringbuf, (u8)b);
837 continue;
840 read_ocode1:
841 if(mctx->bbll.nbits_in_bitbuf < 10) {
842 mdst->state = MEDLZST_WAITING_FOR_OCODE1;
843 goto done;
846 lcode = (UI)de_bitbuf_lowlevel_get_bits(&mctx->bbll, 2);
847 ocode = (UI)de_bitbuf_lowlevel_get_bits(&mctx->bbll, 8);
848 length = lcode + 2;
849 de_lz77buffer_copy_from_hist(mctx->ringbuf, mctx->ringbuf->curpos - 1 - ocode, length);
850 mdst->state = MEDLZST_NEUTRAL;
853 done:
854 if(!dfctx->finished_flag && !mctx->errflag) {
855 if(bufpos<buf_len) {
856 // It shouldn't be possible to get here. If we do, it means some input
857 // bytes will be lost.
858 de_dfilter_set_generic_error(dfctx->c, mctx->dres, mctx->modname);
859 mctx->errflag = 1;
864 static void quicklz77_codec_command(struct de_dfilter_ctx *dfctx, int cmd, UI flags)
866 struct medium_ctx *mctx = (struct medium_ctx*)dfctx->codec_private;
868 if(cmd==DE_DFILTER_COMMAND_FINISH_BLOCK) {
869 de_bitbuf_lowlevel_empty(&mctx->bbll);
870 mctx->mdst.state = MEDLZST_NEUTRAL;
871 de_lz77buffer_set_curpos(mctx->ringbuf, mctx->ringbuf->curpos + 5);
873 else if(cmd==DE_DFILTER_COMMAND_RESET_COUNTERS) {
874 mctx->nbytes_written = 0;
875 mctx->errflag = 0;
876 dfctx->finished_flag = 0;
880 // codec_private_params: unused
881 static void dmsquicklz77_codec(struct de_dfilter_ctx *dfctx, void *codec_private_params)
883 struct medium_ctx *mctx = NULL;
885 mctx = de_malloc(dfctx->c, sizeof(struct medium_ctx));
886 mctx->c = dfctx->c;
887 mctx->modname = "dmsquick_lz77";
888 mctx->dcmpro = dfctx->dcmpro;
889 mctx->dres = dfctx->dres;
891 dfctx->codec_private = (void*)mctx;
892 dfctx->codec_addbuf_fn = quicklz77_codec_addbuf;
893 dfctx->codec_command_fn = quicklz77_codec_command;
894 dfctx->codec_destroy_fn = mediumlz77_codec_destroy;
896 mctx->ringbuf = de_lz77buffer_create(dfctx->c, 256);
897 de_lz77buffer_set_curpos(mctx->ringbuf, 251);
899 mctx->ringbuf->userdata = (void*)mctx;
900 mctx->ringbuf->writebyte_cb = medium_lz77buf_writebytecb;
903 ///////////////// "Quick", "Medium", "Deep" decompression //////////////
905 // Note: These decompressors have a different design than Heavy.
906 // The codecs are "pushable", which is a good thing for DMS's segmented format.
907 // It may look more complicated, but ultimately it's cleaner and less hacky.
909 static void do_decompress_track_q_m_d(deark *c, struct dmsctx *d, struct dms_track_info *tri,
910 struct de_dfilter_in_params *dcmpri, struct de_dfilter_out_params *dcmpro,
911 struct de_dfilter_results *dres)
913 struct dms_std_cmpr_state *cst = NULL;
914 struct std_saved_state_wrapper *ssw;
916 if(tri->cmpr_type==DMSCMPR_QUICK) {
917 ssw = &d->saved_quick_state;
919 else if(tri->cmpr_type==DMSCMPR_DEEP) {
920 ssw = &d->saved_deep_cmpr_state;
922 else {
923 ssw = &d->saved_medium_state;
926 if(ssw->saved_state) {
927 if(tri->is_real) {
928 // Reclaim saved decompression state, if any
929 cst = ssw->saved_state;
930 ssw->saved_state = NULL;
932 cst->dcmpro1.len_known = 1;
933 cst->dcmpro1.expected_len = tri->intermediate_len;
935 de_dfilter_command(cst->dfctx_codec1, DE_DFILTER_COMMAND_RESET_COUNTERS, 0);
937 else {
938 destroy_std_cmpr_state(c, ssw->saved_state);
939 ssw->saved_state = NULL;
943 if(!cst) {
944 cst = de_malloc(c, sizeof(struct dms_std_cmpr_state));
945 cst->cmpr_meth = tri->cmpr_type;
946 if(cst->cmpr_meth==DMSCMPR_QUICK) {
947 cst->cmpr_meth_name = "quickcmpr";
949 else if(cst->cmpr_meth==DMSCMPR_DEEP) {
950 cst->cmpr_meth_name = "deepcmpr";
952 else {
953 cst->cmpr_meth_name = "mediumcmpr";
956 cst->outf_codec1 = dbuf_create_custom_dbuf(c, 0, 0);
957 cst->outf_codec1->userdata_for_customwrite = (void*)cst;
958 cst->outf_codec1->customwrite_fn = my_std1_write_cb;
960 cst->dcmpro1.f = cst->outf_codec1;
961 cst->dcmpro1.len_known = 1;
962 cst->dcmpro1.expected_len = tri->intermediate_len;
965 // Reset the "length" of this virtual dbuf.
966 dbuf_truncate(cst->outf_codec1, 0);
968 if(cst->dfctx_rle) {
969 de_dfilter_destroy(cst->dfctx_rle);
970 cst->dfctx_rle = NULL;
972 cst->dfctx_rle = de_dfilter_create(c, dmsrle_codec, NULL, dcmpro, dres);
974 if(!cst->dfctx_codec1) {
975 if(cst->cmpr_meth==DMSCMPR_DEEP) {
976 struct de_lh1_params lh1p;
978 de_zeromem(&lh1p, sizeof(struct de_lh1_params));
979 lh1p.is_dms_deep = 1;
980 cst->dfctx_codec1 = de_dfilter_create(c, dfilter_lh1_codec,
981 (void*)&lh1p, &cst->dcmpro1, &cst->dres1);
983 else if(cst->cmpr_meth==DMSCMPR_QUICK) {
984 cst->dfctx_codec1 = de_dfilter_create(c, dmsquicklz77_codec,
985 NULL, &cst->dcmpro1, &cst->dres1);
987 else {
988 cst->dfctx_codec1 = de_dfilter_create(c, dmsmediumlz77_codec,
989 NULL, &cst->dcmpro1, &cst->dres1);
993 cst->dfctx_codec1->input_file_offset = dcmpri->pos;
994 cst->intermediate_nbytes = 0;
996 de_dfilter_addslice(cst->dfctx_codec1, dcmpri->f, dcmpri->pos, dcmpri->len);
997 de_dfilter_command(cst->dfctx_codec1, DE_DFILTER_COMMAND_FINISH_BLOCK, 0);
998 de_dfilter_finish(cst->dfctx_rle);
1000 // If cst->dfctx_lzah->dres has the error message we need, copy it to the
1001 // real dres.
1002 de_dfilter_transfer_error(c, cst->dfctx_codec1->dres, dres);
1004 if(tri->is_real) {
1005 // Note that this saved state may be deleted soon, in dms_decompress_track().
1006 ssw->saved_state = cst;
1008 else {
1009 destroy_std_cmpr_state(c, cst);
1013 ///////////////////////////////////
1015 static void do_decompress_heavy_lzh_rle(deark *c, struct dmsctx *d, struct dms_track_info *tri,
1016 struct de_dfilter_in_params *dcmpri, struct de_dfilter_out_params *dcmpro,
1017 struct de_dfilter_results *dres, struct dmslzh_params *lzhparams)
1019 struct de_dcmpr_two_layer_params tlp;
1021 de_zeromem(&tlp, sizeof(struct de_dcmpr_two_layer_params));
1022 tlp.codec1_type1 = dmslzh_codectype1;
1023 tlp.codec1_private_params = (void*)lzhparams;
1024 tlp.codec2 = dmsrle_codec;
1025 tlp.dcmpri = dcmpri;
1026 tlp.dcmpro = dcmpro;
1027 tlp.dres = dres;
1028 tlp.intermed_expected_len = tri->intermediate_len;
1029 tlp.intermed_len_known = 1;
1030 de_dfilter_decompress_two_layer(c, &tlp);
1033 static void do_decompress_heavy(deark *c, struct dmsctx *d, struct dms_track_info *tri,
1034 struct de_dfilter_in_params *dcmpri, struct de_dfilter_out_params *dcmpro,
1035 struct de_dfilter_results *dres)
1037 struct dmslzh_params lzhparams;
1039 de_zeromem(&lzhparams, sizeof(struct dmslzh_params));
1040 lzhparams.cmpr_type = tri->cmpr_type;
1041 lzhparams.dms_track_flags = tri->track_flags;
1042 if(tri->is_real) {
1043 lzhparams.heavy_state = d->saved_heavy_state;
1044 d->saved_heavy_state = NULL;
1047 if(tri->track_flags & 0x04) {
1048 do_decompress_heavy_lzh_rle(c, d, tri, dcmpri, dcmpro, dres, &lzhparams);
1050 else {
1051 // LZH, no RLE
1052 dmslzh_codectype1(c, dcmpri, dcmpro, dres, (void*)&lzhparams);
1055 if(tri->is_real) {
1056 d->saved_heavy_state = lzhparams.heavy_state;
1058 else {
1059 destroy_heavy_state(c, lzhparams.heavy_state);
1063 static void destroy_saved_dcrmpr_state(deark *c, struct dmsctx *d)
1065 if(d->saved_quick_state.saved_state) {
1066 destroy_std_cmpr_state(c, d->saved_quick_state.saved_state);
1067 d->saved_quick_state.saved_state = NULL;
1069 if(d->saved_medium_state.saved_state) {
1070 destroy_std_cmpr_state(c, d->saved_medium_state.saved_state);
1071 d->saved_medium_state.saved_state = NULL;
1073 if(d->saved_deep_cmpr_state.saved_state) {
1074 destroy_std_cmpr_state(c, d->saved_deep_cmpr_state.saved_state);
1075 d->saved_deep_cmpr_state.saved_state = NULL;
1077 if(d->saved_heavy_state) {
1078 destroy_heavy_state(c, d->saved_heavy_state);
1079 d->saved_heavy_state = NULL;
1083 static int dms_decompress_track(deark *c, struct dmsctx *d, struct dms_track_info *tri,
1084 dbuf *outf)
1086 int retval = 0;
1087 i64 unc_nbytes;
1088 struct de_dfilter_in_params dcmpri;
1089 struct de_dfilter_out_params dcmpro;
1090 struct de_dfilter_results dres;
1092 if(outf->len!=0) goto done;
1094 if(tri->dpos + tri->cmpr_len > c->infile->len) {
1095 de_err(c, "Track goes beyond end of file");
1096 goto done;
1099 de_dfilter_init_objects(c, &dcmpri, &dcmpro, &dres);
1100 dcmpri.f = c->infile;
1101 dcmpri.pos = tri->dpos;
1102 dcmpri.len = tri->cmpr_len;
1103 dcmpro.f = outf;
1104 dcmpro.len_known = 1;
1105 dcmpro.expected_len = tri->uncmpr_len;
1107 tri->cksum_calc = 0;
1109 if(tri->cmpr_type==DMSCMPR_NONE) {
1110 fmtutil_decompress_uncompressed(c, &dcmpri, &dcmpro, &dres, 0);
1112 else if(tri->cmpr_type==DMSCMPR_RLE) {
1113 de_dfilter_decompress_oneshot(c, dmsrle_codec, NULL,
1114 &dcmpri, &dcmpro, &dres);
1116 else if(tri->cmpr_type==DMSCMPR_QUICK || tri->cmpr_type==DMSCMPR_MEDIUM ||
1117 tri->cmpr_type==DMSCMPR_DEEP)
1119 do_decompress_track_q_m_d(c, d, tri, &dcmpri, &dcmpro, &dres);
1121 else if(tri->cmpr_type==DMSCMPR_HEAVY1 || tri->cmpr_type==DMSCMPR_HEAVY2) {
1122 do_decompress_heavy(c, d, tri, &dcmpri, &dcmpro, &dres);
1124 else {
1125 de_err(c, "[%s] Unsupported compression method: %u (%s)",
1126 tri->shortname, tri->cmpr_type,
1127 dms_get_cmprtype_name(tri->cmpr_type));
1128 goto done;
1130 dbuf_flush(dcmpro.f);
1132 if(dres.errcode) {
1133 de_err(c, "[%s] Decompression failed: %s", tri->shortname,
1134 de_dfilter_get_errmsg(c, &dres));
1135 goto done;
1138 unc_nbytes = outf->len;
1140 dbuf_truncate(outf, tri->uncmpr_len);
1142 if(unc_nbytes < tri->uncmpr_len) {
1143 de_err(c, "[%s] Expected %"I64_FMT" decompressed bytes, got %"I64_FMT,
1144 tri->shortname, tri->uncmpr_len, unc_nbytes);
1145 goto done;
1147 if(unc_nbytes > tri->uncmpr_len) {
1148 de_warn(c, "[%s] Expected %"I64_FMT" decompressed bytes, got %"I64_FMT,
1149 tri->shortname, tri->uncmpr_len, unc_nbytes);
1152 retval = 1;
1154 done:
1155 if(tri->is_real && !(tri->track_flags & 0x1)) {
1156 destroy_saved_dcrmpr_state(c, d);
1158 return retval;
1161 // f is presumed to be membuf containing one track, and nothing else.
1162 static u32 dms_calc_checksum(deark *c, struct dmsctx *d, dbuf *f)
1164 u32 cksum;
1166 de_crcobj_reset(d->crco_cksum);
1167 de_crcobj_addslice(d->crco_cksum, f, 0, f->len);
1168 cksum = de_crcobj_getval(d->crco_cksum);
1169 cksum &= 0xffff;
1170 return cksum;
1173 static void get_trackflags_descr(deark *c, de_ucstring *s, UI tflags1, UI cmpr)
1175 UI tflags = tflags1;
1177 if(cmpr==5 || cmpr==6) {
1178 if(tflags & 0x4) {
1179 ucstring_append_flags_item(s, "w/RLE");
1180 tflags -= 0x4;
1182 if(tflags & 0x2) {
1183 ucstring_append_flags_item(s, "track has Huffman tree defs");
1184 tflags -= 0x2;
1187 if(tflags & 0x1) {
1188 ucstring_append_flags_item(s, "solid");
1189 tflags -= 0x1;
1191 if(tflags>0) ucstring_append_flags_itemf(s, "0x%02x", tflags);
1194 // Read track and decompress to outf (which caller supplies as an empty membuf).
1195 // track_idx: the index into d->tracks_by_file_order
1196 // Returns nonzero if successfully decompressed.
1197 static int dms_read_and_decompress_track(deark *c, struct dmsctx *d,
1198 i64 track_idx, dbuf *outf)
1200 i64 pos1, pos;
1201 struct dms_track_info *tri = NULL;
1202 de_ucstring *descr = NULL;
1203 int retval = 0;
1204 int saved_indent_level;
1206 de_dbg_indent_save(c, &saved_indent_level);
1208 tri = de_malloc(c, sizeof(struct dms_track_info));
1209 pos1 = d->tracks_by_file_order[track_idx].file_pos;
1210 tri->track_num = (i64)d->tracks_by_file_order[track_idx].track_num;
1211 tri->is_real = d->tracks_by_file_order[track_idx].is_real;
1212 de_snprintf(tri->shortname, sizeof(tri->shortname), "%strack %d",
1213 (tri->is_real?"":"extra "), (int)tri->track_num);
1215 de_dbg(c, "%s at %"I64_FMT, tri->shortname, pos1);
1216 de_dbg_indent(c, 1);
1217 pos = pos1;
1218 pos += 2; // signature, already checked
1219 pos += 2; // reported track number, already read
1220 pos += 2; // Unknown field
1221 tri->cmpr_len = de_getu16be_p(&pos);
1222 de_dbg(c, "cmpr len: %"I64_FMT, tri->cmpr_len);
1223 tri->intermediate_len = de_getu16be_p(&pos);
1224 de_dbg(c, "intermediate len: %"I64_FMT, tri->intermediate_len);
1225 tri->uncmpr_len = de_getu16be_p(&pos);
1226 de_dbg(c, "uncmpr len: %"I64_FMT, tri->uncmpr_len);
1228 tri->track_flags = (UI)de_getbyte_p(&pos);
1229 tri->cmpr_type = (UI)de_getbyte_p(&pos);
1231 descr = ucstring_create(c);
1232 get_trackflags_descr(c, descr, tri->track_flags, tri->cmpr_type);
1233 de_dbg(c, "track flags: 0x%02x (%s)", tri->track_flags, ucstring_getpsz_d(descr));
1235 de_dbg(c, "track cmpr type: %u (%s)", tri->cmpr_type, dms_get_cmprtype_name(tri->cmpr_type));
1236 tri->cksum_reported = (u32)de_getu16be_p(&pos);
1237 de_dbg(c, "checksum (reported): 0x%04x", (UI)tri->cksum_reported);
1238 tri->crc_cmprdata_reported = (u32)de_getu16be_p(&pos);
1239 de_dbg(c, "crc of cmpr data (reported): 0x%04x", (UI)tri->crc_cmprdata_reported);
1240 tri->crc_header_reported = (u32)de_getu16be_p(&pos);
1241 de_dbg(c, "crc of header (reported): 0x%04x", (UI)tri->crc_header_reported);
1243 tri->dpos = pos1 + DMS_TRACK_HDR_LEN;
1244 de_dbg(c, "cmpr data at %"I64_FMT, tri->dpos);
1245 de_dbg_indent(c, 1);
1246 if(!dms_decompress_track(c, d, tri, outf)) goto done;
1247 de_dbg_indent(c, -1);
1249 tri->cksum_calc = dms_calc_checksum(c, d, outf);
1250 de_dbg(c, "checksum (calculated): 0x%04x", (UI)tri->cksum_calc);
1251 if(tri->cksum_calc != tri->cksum_reported) {
1252 de_err(c, "[%s] Checksum check failed", tri->shortname);
1253 goto done;
1255 retval = 1;
1257 done:
1258 ucstring_destroy(descr);
1259 de_free(c, tri);
1260 de_dbg_indent_restore(c, saved_indent_level);
1261 return retval;
1264 static void write_extra_track(deark *c, struct dmsctx *d, i64 track_idx, dbuf *trackbuf)
1266 char ext[80];
1267 dbuf *outf_extra = NULL;
1269 de_snprintf(ext, sizeof(ext), "extratrack%d.bin",
1270 (int)d->tracks_by_file_order[track_idx].track_num);
1271 outf_extra = dbuf_create_output_file(c, ext, NULL, DE_CREATEFLAG_IS_AUX);
1272 dbuf_copy(trackbuf, 0, trackbuf->len, outf_extra);
1273 dbuf_close(outf_extra);
1276 // Write out all the tracks, whether real or extra.
1277 static void do_dms_main(deark *c, struct dmsctx *d)
1279 i64 i;
1280 int real_track_failure_flag = 0;
1281 dbuf *outf = NULL;
1282 dbuf *trackbuf = NULL;
1284 trackbuf = dbuf_create_membuf(c, 11264, 0);
1285 dbuf_enable_wbuffer(trackbuf);
1286 outf = dbuf_create_output_file(c, "adf", NULL, 0);
1288 for(i=0; i<d->num_tracks_in_file; i++) {
1289 int ret_dcmpr;
1291 if(real_track_failure_flag && d->tracks_by_file_order[i].is_real) {
1292 continue;
1295 dbuf_truncate(trackbuf, 0);
1297 ret_dcmpr = dms_read_and_decompress_track(c, d, i, trackbuf);
1299 if(!ret_dcmpr) {
1300 if(d->tracks_by_file_order[i].is_real) {
1301 real_track_failure_flag = 1;
1303 continue;
1306 if(d->tracks_by_file_order[i].is_real) {
1307 dbuf_copy(trackbuf, 0, trackbuf->len, outf);
1309 else {
1310 write_extra_track(c, d, i, trackbuf);
1314 dbuf_close(outf);
1315 dbuf_close(trackbuf);
1318 static int do_dms_header(deark *c, struct dmsctx *d, i64 pos1)
1320 i64 n;
1321 i64 pos = pos1;
1322 i64 cmpr_len, orig_len;
1323 struct de_timestamp cr_time;
1324 int retval = 0;
1326 de_dbg(c, "header at %"I64_FMT, pos1);
1327 de_dbg_indent(c, 1);
1329 // [0..3] = signature
1330 pos = pos1+8;
1331 d->info_bits = (UI)de_getu32be_p(&pos); // [8..11] = info bits
1332 de_dbg(c, "infobits: 0x%08x", d->info_bits);
1334 de_zeromem(&cr_time, sizeof(struct de_timestamp));
1335 read_unix_timestamp(c, pos, &cr_time, "creation time");
1336 pos += 4;
1338 d->first_track = de_getu16be_p(&pos); // [16..17] = firsttrack
1339 de_dbg(c, "first track: %d", (int)d->first_track);
1340 if(d->first_track >= DMS_MAX_TRACKS) goto done;
1341 if(d->first_track != 0) {
1342 de_info(c, "Note: First track is #%d, not #0. This may be a partial disk image.",
1343 (int)d->first_track);
1346 d->last_track = de_getu16be_p(&pos); // [18..19] = lasttrack
1347 de_dbg(c, "last track: %u", (int)d->last_track);
1348 if(d->last_track < d->first_track) goto done;
1349 if(d->last_track >= DMS_MAX_TRACKS) goto done;
1351 cmpr_len = de_getu32be_p(&pos); // [20..23] = packed len
1352 de_dbg(c, "compressed len: %"I64_FMT, cmpr_len);
1354 orig_len = de_getu32be_p(&pos); // [24..27] = unpacked len
1355 de_dbg(c, "decompressed len: %"I64_FMT, orig_len);
1357 if(d->first_track==0 && d->last_track==0 && (cmpr_len==0 || orig_len==0)) {
1358 de_warn(c, "File lacks info about which tracks are present");
1360 else {
1361 d->have_first_last_track_info = 1;
1364 // [46..47] = creating software version
1365 pos = pos1+50;
1366 n = de_getu16be_p(&pos); // [50..51] = disk type
1367 de_dbg(c, "disk type: %u", (UI)n);
1369 d->cmpr_type = (UI)de_getu16be_p(&pos); // [52..53] = compression mode
1370 de_dbg(c, "compression type: %u (%s)", d->cmpr_type,
1371 dms_get_cmprtype_name(d->cmpr_type));
1373 n = de_getu16be_p(&pos); // [54..55] = crc
1374 de_dbg(c, "crc (reported): 0x%04x", (UI)n);
1376 retval = 1;
1378 done:
1379 de_dbg_indent(c, -1);
1380 return retval;
1383 static int dms_scan_file(deark *c, struct dmsctx *d, i64 pos1)
1385 i64 pos = pos1;
1386 i64 i;
1387 i64 lowest_real_track_num = DMS_MAX_TRACKS;
1388 i64 highest_real_track_num = 0;
1389 u32 next_real_tracknum_expected;
1390 int retval = 0;
1392 de_dbg(c, "scanning file");
1393 de_dbg_indent(c, 1);
1395 d->num_tracks_in_file = 0;
1397 while(1) {
1398 i64 track_num_reported;
1399 i64 cmpr_len;
1400 i64 uncmpr_len;
1401 u8 track_flags;
1402 u8 cmpr_type;
1404 if(pos+DMS_TRACK_HDR_LEN > c->infile->len) break;
1406 if(dbuf_memcmp(c->infile, pos, "TR", 2)) {
1407 de_dbg(c, "[track not found at %"I64_FMT"; assuming disk image ends here]", pos);
1408 break;
1410 if(d->num_tracks_in_file >= DMS_MAX_TRACKS) {
1411 de_err(c, "Too many tracks in file");
1412 break;
1415 track_num_reported = de_getu16be(pos+2);
1416 cmpr_len = de_getu16be(pos+6);
1417 uncmpr_len = de_getu16be(pos+10);
1418 track_flags = de_getbyte(pos+12);
1419 cmpr_type = de_getbyte(pos+13);
1421 de_dbg(c, "track[%d] at %"I64_FMT", #%d, len=%"I64_FMT"/%"I64_FMT", cmpr=%u, flags=0x%02x",
1422 (int)d->num_tracks_in_file, pos, (int)track_num_reported, cmpr_len, uncmpr_len,
1423 (UI)cmpr_type, (UI)track_flags);
1425 d->tracks_by_file_order[d->num_tracks_in_file].file_pos = pos;
1426 d->tracks_by_file_order[d->num_tracks_in_file].track_num = (u32)track_num_reported;
1428 if(d->have_first_last_track_info) {
1429 if(track_num_reported>=d->first_track && track_num_reported<=d->last_track) {
1430 d->tracks_by_track_num[track_num_reported].order_in_file = (u32)d->num_tracks_in_file;
1431 d->tracks_by_track_num[track_num_reported].in_use = 1;
1434 else {
1435 if(track_num_reported<DMS_MAX_TRACKS) {
1436 d->tracks_by_track_num[track_num_reported].order_in_file = (u32)d->num_tracks_in_file;
1437 d->tracks_by_track_num[track_num_reported].in_use = 1;
1441 if(track_num_reported<DMS_MAX_TRACKS) {
1442 if(track_num_reported<lowest_real_track_num) {
1443 lowest_real_track_num = track_num_reported;
1445 if(track_num_reported>highest_real_track_num) {
1446 highest_real_track_num = track_num_reported;
1450 d->num_tracks_in_file++;
1451 pos += DMS_TRACK_HDR_LEN + cmpr_len;
1454 if(d->num_tracks_in_file==0) {
1455 de_err(c, "No tracks found");
1456 goto done;
1459 if(!d->have_first_last_track_info) {
1460 d->first_track = lowest_real_track_num;
1461 d->last_track = highest_real_track_num;
1464 // Make sure all expected tracks are present, and mark the "real" tracks in
1465 // tracks_by_file_order[].
1466 // One reason for doing it this way is that there may be two tracks numbered 0,
1467 // with the second one being the real one.
1468 for(i=d->first_track; i<=d->last_track; i++) {
1469 if(!d->tracks_by_track_num[i].in_use) {
1470 // TODO: Maybe we should write a track of all zeroes instead (but how many zeroes?)
1471 de_err(c, "Could not find track #%d", (int)i);
1472 goto done;
1475 d->tracks_by_file_order[d->tracks_by_track_num[i].order_in_file].is_real = 1;
1478 next_real_tracknum_expected = (u32)d->first_track;
1479 for(i=0; i<d->num_tracks_in_file; i++) {
1480 if(d->tracks_by_file_order[i].is_real) {
1481 // I'm not going to bother supporting out-of-order tracks, at least until
1482 // I learn that such files exist.
1483 if(d->tracks_by_file_order[i].track_num != next_real_tracknum_expected) {
1484 de_err(c, "Track numbers not in order. Not supported.");
1485 goto done;
1487 next_real_tracknum_expected = d->tracks_by_file_order[i].track_num + 1;
1491 retval = 1;
1492 done:
1493 de_dbg_indent(c, -1);
1494 return retval;
1497 static void de_run_amiga_dms(deark *c, de_module_params *mparams)
1499 struct dmsctx *d = NULL;
1501 d = de_malloc(c, sizeof(struct dmsctx));
1502 d->crco_cksum = de_crcobj_create(c, DE_CRCOBJ_SUM_BYTES);
1503 if(!do_dms_header(c, d, 0)) goto done;
1504 if(!dms_scan_file(c, d, DMS_FILE_HDR_LEN)) goto done;
1505 do_dms_main(c, d);
1507 done:
1508 if(d) {
1509 destroy_saved_dcrmpr_state(c, d);
1510 de_crcobj_destroy(d->crco_cksum);
1511 de_free(c, d);
1515 static int de_identify_amiga_dms(deark *c)
1517 i64 dcmpr_size;
1519 if(dbuf_memcmp(c->infile, 0, "DMS!", 4)) return 0;
1520 dcmpr_size = de_getu32be(24);
1521 if(dcmpr_size==901120) return 100;
1522 return 85;
1525 void de_module_amiga_dms(deark *c, struct deark_module_info *mi)
1527 mi->id = "amiga_dms";
1528 mi->desc = "Amiga DMS disk image";
1529 mi->run_fn = de_run_amiga_dms;
1530 mi->identify_fn = de_identify_amiga_dms;