nrg: Improved support for v2
[deark.git] / modules / dms.c
blobcf495c641b2911b0deaa9e0a2bb9b75ba2e3c6ad
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 does a thing I call "persistent decompression state".
12 // Roughly speaking, the state of some decompressors is reset only after
13 // processing a track for which the low bit of the track_flags field is 0.
14 // Otherwise it persists, and will most likely be used with a future 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;
86 // Entries in use: 0 <= n < .num_tracks_in_file
87 struct dms_tracks_by_file_order_entry tracks_by_file_order[DMS_MAX_TRACKS];
89 // Entries potentially in use: .first_track <= n <= .last_track
90 struct dms_tracks_by_track_num_entry tracks_by_track_num[DMS_MAX_TRACKS];
92 struct std_saved_state_wrapper saved_medium_state;
93 struct std_saved_state_wrapper saved_deep_cmpr_state;
94 struct dmsheavy_cmpr_state *saved_heavy_state;
97 // For some decompressors, the portion of the decompression context that can
98 // persist between tracks
99 struct dms_std_cmpr_state {
100 UI cmpr_meth;
101 const char *cmpr_meth_name;
103 struct de_dfilter_ctx *dfctx_codec1;
104 struct de_dfilter_ctx *dfctx_rle;
105 i64 intermediate_nbytes;
107 dbuf *outf_codec1;
108 struct de_dfilter_out_params dcmpro1;
109 struct de_dfilter_results dres1;
112 static const char *dms_get_cmprtype_name(UI n)
114 const char *name = NULL;
115 switch(n) {
116 case DMSCMPR_NONE: name="uncompressed"; break;
117 case DMSCMPR_RLE: name="simple (RLE)"; break;
118 case DMSCMPR_QUICK: name="quick"; break;
119 case DMSCMPR_MEDIUM: name="medium (RLE + LZ77)"; break;
120 case DMSCMPR_DEEP: name="deep (RLE + LZ77+dynamic_huffman)"; break;
121 case DMSCMPR_HEAVY1: name="heavy1 (optional RLE + LZ77-4K+Huffman)"; break;
122 case DMSCMPR_HEAVY2: name="heavy2 (optional RLE + LZ77-8K+Huffman)"; break;
124 return name?name:"?";
127 static void read_unix_timestamp(deark *c, i64 pos, struct de_timestamp *ts, const char *name)
129 i64 t;
130 char timestamp_buf[64];
132 t = de_geti32be(pos);
133 de_unix_time_to_timestamp(t, ts, 0x1);
134 de_timestamp_to_string(ts, timestamp_buf, sizeof(timestamp_buf), 0);
135 de_dbg(c, "%s: %"I64_FMT" (%s)", name, t, timestamp_buf);
138 static void destroy_std_cmpr_state(deark *c, struct dms_std_cmpr_state *cst)
140 if(!cst) return;
141 if(cst->dfctx_codec1) {
142 de_dfilter_destroy(cst->dfctx_codec1);
144 if(cst->dfctx_rle) {
145 de_dfilter_destroy(cst->dfctx_rle);
147 dbuf_close(cst->outf_codec1);
148 de_free(c, cst);
151 static void my_std1_write_cb(dbuf *f, void *userdata,
152 const u8 *buf, i64 size)
154 struct dms_std_cmpr_state *u = (struct dms_std_cmpr_state*)userdata;
156 if(!u->dfctx_rle) {
157 de_internal_err_fatal(f->c, "%s", u->cmpr_meth_name);
159 de_dfilter_addbuf(u->dfctx_rle, buf, size);
160 u->intermediate_nbytes += size;
163 /////// Heavy (LZH) compression ///////
165 // Note: A lot of this is very similar to the code in fmtutil-lzh.c.
166 // The main problem with using standard LZH code for DMS is that some of the
167 // decompression state persists from one track to the next. But not all of it
168 // -- you can't just concatenate the compressed data together before
169 // decompressing it.
171 struct lzh_tree_wrapper {
172 struct fmtutil_huffman_decoder *ht;
175 // The portion of the Heavy decompression context that can persist between tracks.
176 struct dmsheavy_cmpr_state {
177 UI cmpr_type;
178 UI heavy_prev_offset;
179 struct de_lz77buffer *ringbuf;
180 u8 trees_exist;
181 struct lzh_tree_wrapper literals_tree;
182 struct lzh_tree_wrapper offsets_tree;
185 // The portion of the Heavy decompression context that does *not* persist between tracks.
186 struct lzh_ctx {
187 deark *c;
188 struct de_dfilter_in_params *dcmpri;
189 struct de_dfilter_out_params *dcmpro;
190 struct de_dfilter_results *dres;
191 const char *modname;
193 i64 nbytes_written;
194 int err_flag;
196 // bitrd.eof_flag: Always set if err_flag is set.
197 struct de_bitreader bitrd;
199 UI heavy_np;
202 struct dmslzh_params {
203 UI cmpr_type; // 5=heavy1, 6=heavy2
204 u8 dms_track_flags;
205 struct dmsheavy_cmpr_state *heavy_state;
208 static void lzh_set_eof_flag(struct lzh_ctx *cctx)
210 cctx->bitrd.eof_flag = 1;
213 static void lzh_set_err_flag(struct lzh_ctx *cctx)
215 lzh_set_eof_flag(cctx);
216 cctx->err_flag = 1;
219 static int lzh_have_enough_output(struct lzh_ctx *cctx)
221 if(cctx->dcmpro->len_known) {
222 if(cctx->nbytes_written >= cctx->dcmpro->expected_len) {
223 return 1;
226 return 0;
229 static void lha5like_lz77buf_writebytecb(struct de_lz77buffer *rb, u8 n)
231 struct lzh_ctx *cctx = (struct lzh_ctx*)rb->userdata;
233 if(lzh_have_enough_output(cctx)) {
234 return;
236 dbuf_writebyte(cctx->dcmpro->f, n);
237 cctx->nbytes_written++;
240 static UI read_next_code_using_tree(struct lzh_ctx *cctx, struct lzh_tree_wrapper *tree)
242 fmtutil_huffman_valtype val = 0;
243 int ret;
245 if(!tree->ht) goto done;
247 ret = fmtutil_huffman_read_next_value(tree->ht->bk, &cctx->bitrd, &val, NULL);
248 if(cctx->bitrd.eof_flag) {
249 de_dfilter_set_errorf(cctx->c, cctx->dres, cctx->modname,
250 "Unexpected end of compressed data");
251 lzh_set_err_flag(cctx);
252 val = 0;
253 goto done;
255 else if(!ret) {
256 de_dfilter_set_errorf(cctx->c, cctx->dres, cctx->modname,
257 "Huffman decoding error");
258 lzh_set_err_flag(cctx);
259 val = 0;
260 goto done;
263 done:
264 return (UI)val;
267 static int dmsheavy_read_tree(struct lzh_ctx *cctx, struct lzh_tree_wrapper *htw,
268 UI ncodes_nbits, UI symlen_nbits)
270 deark *c = cctx->c;
271 UI ncodes;
272 UI curr_idx;
273 int retval = 0;
275 if(htw->ht) goto done;
277 ncodes = (UI)de_bitreader_getbits(&cctx->bitrd, ncodes_nbits);
278 de_dbg2(c, "num codes: %u", ncodes);
280 htw->ht = fmtutil_huffman_create_decoder(c, (i64)ncodes, (i64)ncodes);
282 if(ncodes==0) {
283 UI null_val;
285 null_val = (UI)de_bitreader_getbits(&cctx->bitrd, ncodes_nbits);
286 fmtutil_huffman_add_code(c, htw->ht->bk, 0, 0, (fmtutil_huffman_valtype)null_val);
287 de_dbg3(c, "val0: %u", null_val);
288 retval = 1;
289 goto done;
292 curr_idx = 0;
293 while(curr_idx < ncodes) {
294 UI symlen;
296 symlen = (UI)de_bitreader_getbits(&cctx->bitrd, symlen_nbits);
297 de_dbg3(c, "len[%u] = %u", curr_idx, symlen);
298 fmtutil_huffman_record_a_code_length(c, htw->ht->builder, (fmtutil_huffman_valtype)curr_idx, symlen);
299 curr_idx++;
301 if(cctx->bitrd.eof_flag) goto done;
303 if(!fmtutil_huffman_make_canonical_code(c, htw->ht->bk, htw->ht->builder, 0)) goto done;
305 retval = 1;
306 done:
307 if(!retval) {
308 lzh_set_err_flag(cctx);
310 return retval;
313 static void dmsheavy_discard_tree(deark *c, struct lzh_tree_wrapper *htw)
315 if(htw->ht) {
316 fmtutil_huffman_destroy_decoder(c, htw->ht);
317 htw->ht = NULL;
321 static void decompress_dms_heavy(struct lzh_ctx *cctx, struct dmslzh_params *lzhp,
322 struct dmsheavy_cmpr_state *hvst)
324 deark *c = cctx->c;
325 UI rb_size;
326 int ret;
327 int saved_indent_level;
328 char pos_descr[32];
330 de_dbg_indent_save(c, &saved_indent_level);
332 if(lzhp->cmpr_type != hvst->cmpr_type) {
333 de_dfilter_set_errorf(c, cctx->dres, cctx->modname,
334 "Mixing Heavy compression types is not supported");
335 goto done;
338 if(lzhp->cmpr_type==DMSCMPR_HEAVY1) {
339 rb_size = 4096;
340 cctx->heavy_np = 14; // for heavy1
342 else {
343 rb_size = 8192;
344 cctx->heavy_np = 15; // for heavy2
347 if(!hvst->ringbuf) {
348 hvst->ringbuf = de_lz77buffer_create(cctx->c, rb_size);
351 hvst->ringbuf->userdata = (void*)cctx;
352 hvst->ringbuf->writebyte_cb = lha5like_lz77buf_writebytecb;
354 if(!cctx->dcmpro->len_known) {
355 // I think we (may) have to know the output length, because zero-length Huffman
356 // codes are(?) possible, and unlike lh5 we aren't told how many codes there are.
357 de_dfilter_set_errorf(cctx->c, cctx->dres, cctx->modname, "Internal error");
358 goto done;
361 if(lzhp->dms_track_flags & 0x02) {
362 dmsheavy_discard_tree(c, &hvst->literals_tree);
363 dmsheavy_discard_tree(c, &hvst->offsets_tree);
364 hvst->trees_exist = 0;
367 if(!hvst->trees_exist) {
368 hvst->trees_exist = 1;
369 de_dbg2(c, "c tree");
370 de_dbg_indent(c, 1);
371 ret = dmsheavy_read_tree(cctx, &hvst->literals_tree, 9, 5);
372 de_dbg_indent(c, -1);
373 if(!ret) goto done;
375 de_dbg2(c, "p tree");
376 de_dbg_indent(c, 1);
377 ret = dmsheavy_read_tree(cctx, &hvst->offsets_tree, 5, 4);
378 de_dbg_indent(c, -1);
379 if(!ret) goto done;
382 de_bitreader_describe_curpos(&cctx->bitrd, pos_descr, sizeof(pos_descr));
383 de_dbg2(c, "cmpr data codes at %s", pos_descr);
384 de_dbg_indent(c, 1);
385 while(1) {
386 UI code;
388 if(cctx->bitrd.eof_flag) goto done;
389 if(lzh_have_enough_output(cctx)) goto done;
391 code = read_next_code_using_tree(cctx, &hvst->literals_tree);
392 if(cctx->bitrd.eof_flag) goto done;
393 if(c->debug_level>=3) {
394 de_dbg3(c, "code: %u (opos=%"I64_FMT")", code, cctx->dcmpro->f->len);
397 if(code < 256) { // literal
398 de_lz77buffer_add_literal_byte(hvst->ringbuf, (u8)code);
400 else { // repeat previous bytes
401 UI offset;
402 UI length;
403 UI ocode1;
405 length = code-253;
406 ocode1 = read_next_code_using_tree(cctx, &hvst->offsets_tree);
407 if(cctx->bitrd.eof_flag) goto done;
409 if(ocode1 == cctx->heavy_np-1) {
410 offset = hvst->heavy_prev_offset;
412 else {
413 if(ocode1 < 1) {
414 offset = ocode1;
416 else {
417 UI ocode2;
419 ocode2 = (UI)de_bitreader_getbits(&cctx->bitrd, ocode1-1);
420 if(cctx->bitrd.eof_flag) goto done;
421 offset = ocode2 | (1U<<(ocode1-1));
423 hvst->heavy_prev_offset = offset;
426 de_lz77buffer_copy_from_hist(hvst->ringbuf,
427 (UI)(hvst->ringbuf->curpos-offset-1), length);
431 done:
432 de_dbg_indent_restore(c, saved_indent_level);
435 static void destroy_heavy_state(deark *c, struct dmsheavy_cmpr_state *hvst)
437 if(!hvst) return;
438 dmsheavy_discard_tree(c, &hvst->literals_tree);
439 dmsheavy_discard_tree(c, &hvst->offsets_tree);
440 de_lz77buffer_destroy(c, hvst->ringbuf);
443 static void dmslzh_codectype1(deark *c, struct de_dfilter_in_params *dcmpri,
444 struct de_dfilter_out_params *dcmpro, struct de_dfilter_results *dres,
445 void *codec_private_params)
447 struct dmslzh_params *lzhp = (struct dmslzh_params*)codec_private_params;
448 struct lzh_ctx *cctx = NULL;
449 struct dmsheavy_cmpr_state *hvst = NULL;
451 cctx = de_malloc(c, sizeof(struct lzh_ctx));
452 cctx->modname = "undmslzh";
453 cctx->c = c;
454 cctx->dcmpri = dcmpri;
455 cctx->dcmpro = dcmpro;
456 cctx->dres = dres;
457 cctx->bitrd.f = dcmpri->f;
458 cctx->bitrd.curpos = dcmpri->pos;
459 cctx->bitrd.endpos = dcmpri->pos + dcmpri->len;
461 if(lzhp->heavy_state) {
462 // If a previous decompression state exists, use it.
463 hvst = lzhp->heavy_state;
464 lzhp->heavy_state = NULL;
466 else {
467 hvst = de_malloc(c, sizeof(struct dmsheavy_cmpr_state));
468 hvst->cmpr_type = lzhp->cmpr_type;
471 decompress_dms_heavy(cctx, lzhp, hvst);
473 hvst->ringbuf->userdata = NULL;
474 hvst->ringbuf->writebyte_cb = NULL;
475 lzhp->heavy_state = hvst;
476 hvst = NULL;
478 if(cctx->err_flag) {
479 // A default error message
480 de_dfilter_set_errorf(c, dres, cctx->modname, "LZH decoding error");
481 goto done;
484 de_bitreader_skip_to_byte_boundary(&cctx->bitrd);
485 cctx->dres->bytes_consumed = cctx->bitrd.curpos - cctx->dcmpri->pos;
486 if(cctx->dres->bytes_consumed<0) {
487 cctx->dres->bytes_consumed = 0;
489 cctx->dres->bytes_consumed_valid = 1;
491 done:
492 if(hvst) destroy_heavy_state(c, hvst);
493 de_free(c, cctx);
496 /////// RLE compression ///////
498 // DMS RLE:
499 // n1 n2 n3 n4 n5
500 // ---------------------------------------------------------
501 // 0x90 0x00 emit 0x90
502 // 0x90 0x01..0xfe n3 emit n2 copies of n3
503 // 0x90 0xff n3 n4 n5 emit (n4#n5) copies of n3
504 // !0x90 emit n1
506 enum dmsrle_state {
507 DMSRLE_STATE_NEUTRAL = 0,
508 DMSRLE_STATE_90,
509 DMSRLE_STATE_90_N2,
510 DMSRLE_STATE_90_FF_N3,
511 DMSRLE_STATE_90_FF_N3_N4
514 struct dmsrle_ctx {
515 enum dmsrle_state state;
516 u8 n2, n3, n4;
519 static void dmsrle_codec_addbuf(struct de_dfilter_ctx *dfctx,
520 const u8 *buf, i64 buf_len)
522 i64 i;
523 struct dmsrle_ctx *rctx = (struct dmsrle_ctx*)dfctx->codec_private;
525 if(!rctx) goto done;
527 for(i=0; i<buf_len; i++) {
528 u8 n;
529 i64 count;
531 n = buf[i];
533 switch(rctx->state) {
534 case DMSRLE_STATE_NEUTRAL:
535 if(n==0x90) {
536 rctx->state = DMSRLE_STATE_90;
538 else {
539 dbuf_writebyte(dfctx->dcmpro->f, n);
541 break;
542 case DMSRLE_STATE_90:
543 if(n==0x00) {
544 dbuf_writebyte(dfctx->dcmpro->f, 0x90);
545 rctx->state = DMSRLE_STATE_NEUTRAL;
547 else {
548 rctx->n2 = n;
549 rctx->state = DMSRLE_STATE_90_N2;
551 break;
552 case DMSRLE_STATE_90_N2:
553 if(rctx->n2==0xff) {
554 rctx->n3 = n;
555 rctx->state = DMSRLE_STATE_90_FF_N3;
557 else {
558 count = (i64)rctx->n2;
559 dbuf_write_run(dfctx->dcmpro->f, n, count);
560 rctx->state = DMSRLE_STATE_NEUTRAL;
562 break;
563 case DMSRLE_STATE_90_FF_N3:
564 rctx->n4 = n;
565 rctx->state = DMSRLE_STATE_90_FF_N3_N4;
566 break;
567 case DMSRLE_STATE_90_FF_N3_N4:
568 count = (i64)(((UI)rctx->n4 << 8) | n);
569 dbuf_write_run(dfctx->dcmpro->f, rctx->n3, count);
570 rctx->state = DMSRLE_STATE_NEUTRAL;
571 break;
574 done:
578 static void dmsrle_codec_destroy(struct de_dfilter_ctx *dfctx)
580 struct dmsrle_ctx *rctx = (struct dmsrle_ctx*)dfctx->codec_private;
582 if(rctx) {
583 de_free(dfctx->c, rctx);
585 dfctx->codec_private = NULL;
588 // codec_private_params: Unused, should be NULL.
589 static void dmsrle_codec(struct de_dfilter_ctx *dfctx, void *codec_private_params)
591 struct dmsrle_ctx *rctx = NULL;
593 rctx = de_malloc(dfctx->c, sizeof(struct dmsrle_ctx));
594 rctx->state = DMSRLE_STATE_NEUTRAL;
595 dfctx->codec_private = (void*)rctx;
596 dfctx->codec_addbuf_fn = dmsrle_codec_addbuf;
597 dfctx->codec_finish_fn = NULL;
598 dfctx->codec_destroy_fn = dmsrle_codec_destroy;
601 ///////////////// Codec for the LZ77 part of "Medium" decompression //////////////
603 enum medlzst_enum {
604 MEDLZST_NEUTRAL=0,
605 MEDLZST_WAITING_FOR_OCODE1,
606 MEDLZST_WAITING_FOR_OCODE2
609 struct medium_state_machine {
610 enum medlzst_enum state;
611 // TODO: We don't really need this much state.
612 UI first_code;
613 UI ocode1_nbits;
614 UI ocode1;
615 UI ocode2_nbits;
616 UI ocode2;
617 UI tmp_code;
618 UI d_code1, d_len1;
619 UI d_code2, d_len2;
622 struct medium_ctx {
623 deark *c;
624 const char *modname;
625 struct de_dfilter_out_params *dcmpro;
626 struct de_dfilter_results *dres;
627 int errflag;
628 i64 nbytes_written;
629 struct de_lz77buffer *ringbuf;
630 struct de_bitbuf_lowlevel bbll;
631 struct medium_state_machine mdst;
634 static int medium_have_enough_output(struct medium_ctx *mctx)
636 if(mctx->dcmpro->len_known) {
637 if(mctx->nbytes_written >= mctx->dcmpro->expected_len) {
638 return 1;
641 return 0;
644 static void mediumlz77_codec_addbuf(struct de_dfilter_ctx *dfctx,
645 const u8 *buf, i64 buf_len)
647 struct medium_ctx *mctx = (struct medium_ctx *)dfctx->codec_private;
648 struct medium_state_machine *mdst = &mctx->mdst;
649 i64 bufpos = 0;
650 UI n;
651 UI offset_rel;
652 UI length;
654 if(dfctx->finished_flag || mctx->errflag) {
655 goto done;
658 while(1) {
659 if(medium_have_enough_output(mctx)) {
660 dfctx->finished_flag = 1;
661 goto done;
664 // Top off the bitbuf, if possible. 32 is an arbitrary number that's
665 // large enough for this format.
666 while(bufpos<buf_len && mctx->bbll.nbits_in_bitbuf<32) {
667 de_bitbuf_lowlevel_add_byte(&mctx->bbll, buf[bufpos++]);
670 if(mdst->state==MEDLZST_WAITING_FOR_OCODE1) goto read_ocode1;
671 if(mdst->state==MEDLZST_WAITING_FOR_OCODE2) goto read_ocode2;
673 // Make sure we have enough source data to read the flag bit,
674 // plus either the literal byte or the first_code.
675 if(mctx->bbll.nbits_in_bitbuf < 9) goto done;
677 n = (UI)de_bitbuf_lowlevel_get_bits(&mctx->bbll, 1);
679 if(n) { // literal byte
680 u8 b;
682 b = (u8)de_bitbuf_lowlevel_get_bits(&mctx->bbll, 8);
683 de_lz77buffer_add_literal_byte(mctx->ringbuf, (u8)b);
684 continue;
687 // TODO: This seems overly complicated. Is there a simpler way to
688 // implement this?
690 mdst->first_code = (UI)de_bitbuf_lowlevel_get_bits(&mctx->bbll, 8);
691 fmtutil_get_lzhuf_d_code_and_len(mdst->first_code, &mdst->d_code1, &mdst->d_len1);
693 read_ocode1:
694 mdst->ocode1_nbits = mdst->d_len1;
695 if(mctx->bbll.nbits_in_bitbuf < mdst->ocode1_nbits) {
696 mdst->state = MEDLZST_WAITING_FOR_OCODE1;
697 goto done;
699 mdst->ocode1 = (UI)de_bitbuf_lowlevel_get_bits(&mctx->bbll, mdst->ocode1_nbits);
701 mdst->tmp_code = ((mdst->first_code << mdst->ocode1_nbits) | mdst->ocode1) & 0xff;
702 fmtutil_get_lzhuf_d_code_and_len(mdst->tmp_code, &mdst->d_code2, &mdst->d_len2);
704 read_ocode2:
705 mdst->ocode2_nbits = mdst->d_len2;
706 if(mctx->bbll.nbits_in_bitbuf < mdst->ocode2_nbits) {
707 mdst->state = MEDLZST_WAITING_FOR_OCODE2;
708 goto done;
710 mdst->ocode2 = (UI)de_bitbuf_lowlevel_get_bits(&mctx->bbll, mdst->ocode2_nbits);
712 offset_rel = (mdst->d_code2 << 8) | (((mdst->tmp_code << mdst->ocode2_nbits) | mdst->ocode2) & 0xff);
713 length = mdst->d_code1 + 3;
714 de_lz77buffer_copy_from_hist(mctx->ringbuf, mctx->ringbuf->curpos - 1 - offset_rel, length);
715 mdst->state = MEDLZST_NEUTRAL;
718 done:
719 if(!dfctx->finished_flag && !mctx->errflag) {
720 if(bufpos<buf_len) {
721 // It shouldn't be possible to get here. If we do, it means some input
722 // bytes will be lost.
723 de_dfilter_set_generic_error(dfctx->c, mctx->dres, mctx->modname);
724 mctx->errflag = 1;
729 static void medium_lz77buf_writebytecb(struct de_lz77buffer *rb, u8 n)
731 struct medium_ctx *mctx = (struct medium_ctx *)rb->userdata;
733 if(medium_have_enough_output(mctx)) {
734 return;
736 dbuf_writebyte(mctx->dcmpro->f, n);
737 mctx->nbytes_written++;
740 static void mediumlz77_codec_command(struct de_dfilter_ctx *dfctx, int cmd, UI flags)
742 struct medium_ctx *mctx = (struct medium_ctx*)dfctx->codec_private;
744 if(cmd==DE_DFILTER_COMMAND_FINISH_BLOCK) {
745 de_bitbuf_lowlevel_empty(&mctx->bbll);
746 mctx->mdst.state = MEDLZST_NEUTRAL;
747 de_lz77buffer_set_curpos(mctx->ringbuf, mctx->ringbuf->curpos + 66);
749 else if(cmd==DE_DFILTER_COMMAND_RESET_COUNTERS) {
750 mctx->nbytes_written = 0;
751 mctx->errflag = 0;
752 dfctx->finished_flag = 0;
756 static void mediumlz77_codec_destroy(struct de_dfilter_ctx *dfctx)
758 struct medium_ctx *mctx = (struct medium_ctx*)dfctx->codec_private;
760 if(mctx) {
761 de_lz77buffer_destroy(dfctx->c, mctx->ringbuf);
762 de_free(dfctx->c, mctx);
764 dfctx->codec_private = NULL;
767 // codec_private_params: unused
768 static void dmsmediumlz77_codec(struct de_dfilter_ctx *dfctx, void *codec_private_params)
770 struct medium_ctx *mctx = NULL;
772 mctx = de_malloc(dfctx->c, sizeof(struct medium_ctx));
773 mctx->c = dfctx->c;
774 mctx->modname = "dmsmedium_lz77";
775 mctx->dcmpro = dfctx->dcmpro;
776 mctx->dres = dfctx->dres;
778 dfctx->codec_private = (void*)mctx;
779 dfctx->codec_addbuf_fn = mediumlz77_codec_addbuf;
780 dfctx->codec_command_fn = mediumlz77_codec_command;
781 dfctx->codec_destroy_fn = mediumlz77_codec_destroy;
783 mctx->ringbuf = de_lz77buffer_create(dfctx->c, 16*1024);
784 // The set_curpos isn't needed, but could help with debugging. It makes our
785 // internal state the same as most other DMS software.
786 de_lz77buffer_set_curpos(mctx->ringbuf, 0x3fbe);
788 mctx->ringbuf->userdata = (void*)mctx;
789 mctx->ringbuf->writebyte_cb = medium_lz77buf_writebytecb;
792 ///////////////// "Medium" and "Deep" decompression //////////////
794 // Note: The Medium and Deep decompressors have a different design than Heavy.
795 // Both the codecs are "pushable", which is a good thing for DMS's segmented
796 // format.
797 // It may look more complicated, but ultimately it's cleaner and less hacky.
799 static void do_decompress_track_medium_or_deep(deark *c, struct dmsctx *d, struct dms_track_info *tri,
800 struct de_dfilter_in_params *dcmpri, struct de_dfilter_out_params *dcmpro,
801 struct de_dfilter_results *dres)
803 struct dms_std_cmpr_state *cst = NULL;
804 struct std_saved_state_wrapper *ssw;
806 if(tri->cmpr_type==DMSCMPR_DEEP) {
807 ssw = &d->saved_deep_cmpr_state;
809 else {
810 ssw = &d->saved_medium_state;
813 if(ssw->saved_state) {
814 if(tri->is_real) {
815 // Reclaim saved decompression state, if any
816 cst = ssw->saved_state;
817 ssw->saved_state = NULL;
819 cst->dcmpro1.len_known = 1;
820 cst->dcmpro1.expected_len = tri->intermediate_len;
822 de_dfilter_command(cst->dfctx_codec1, DE_DFILTER_COMMAND_RESET_COUNTERS, 0);
824 else {
825 destroy_std_cmpr_state(c, ssw->saved_state);
826 ssw->saved_state = NULL;
830 if(!cst) {
831 cst = de_malloc(c, sizeof(struct dms_std_cmpr_state));
832 cst->cmpr_meth = tri->cmpr_type;
833 if(cst->cmpr_meth==DMSCMPR_DEEP) {
834 cst->cmpr_meth_name = "deepcmpr";
836 else {
837 cst->cmpr_meth_name = "mediumcmpr";
840 cst->outf_codec1 = dbuf_create_custom_dbuf(c, 0, 0);
841 cst->outf_codec1->userdata_for_customwrite = (void*)cst;
842 cst->outf_codec1->customwrite_fn = my_std1_write_cb;
844 cst->dcmpro1.f = cst->outf_codec1;
845 cst->dcmpro1.len_known = 1;
846 cst->dcmpro1.expected_len = tri->intermediate_len;
849 // Reset the "length" of this virtual dbuf.
850 dbuf_truncate(cst->outf_codec1, 0);
852 if(cst->dfctx_rle) {
853 de_dfilter_destroy(cst->dfctx_rle);
854 cst->dfctx_rle = NULL;
856 cst->dfctx_rle = de_dfilter_create(c, dmsrle_codec, NULL, dcmpro, dres);
858 if(!cst->dfctx_codec1) {
859 if(cst->cmpr_meth==DMSCMPR_DEEP) {
860 struct de_lh1_params lh1p;
862 de_zeromem(&lh1p, sizeof(struct de_lh1_params));
863 lh1p.is_dms_deep = 1;
864 cst->dfctx_codec1 = de_dfilter_create(c, dfilter_lh1_codec,
865 (void*)&lh1p, &cst->dcmpro1, &cst->dres1);
867 else {
868 cst->dfctx_codec1 = de_dfilter_create(c, dmsmediumlz77_codec,
869 NULL, &cst->dcmpro1, &cst->dres1);
873 cst->dfctx_codec1->input_file_offset = dcmpri->pos;
874 cst->intermediate_nbytes = 0;
876 de_dfilter_addslice(cst->dfctx_codec1, dcmpri->f, dcmpri->pos, dcmpri->len);
877 de_dfilter_command(cst->dfctx_codec1, DE_DFILTER_COMMAND_FINISH_BLOCK, 0);
878 de_dfilter_finish(cst->dfctx_rle);
880 // If cst->dfctx_lzah->dres has the error message we need, copy it to the
881 // real dres.
882 de_dfilter_transfer_error(c, cst->dfctx_codec1->dres, dres);
884 if(tri->is_real) {
885 // Note that this saved state may be deleted soon, in dms_decompress_track().
886 ssw->saved_state = cst;
888 else {
889 destroy_std_cmpr_state(c, cst);
893 ///////////////////////////////////
895 static void do_decompress_heavy_lzh_rle(deark *c, struct dmsctx *d, struct dms_track_info *tri,
896 struct de_dfilter_in_params *dcmpri, struct de_dfilter_out_params *dcmpro,
897 struct de_dfilter_results *dres, struct dmslzh_params *lzhparams)
899 struct de_dcmpr_two_layer_params tlp;
901 de_zeromem(&tlp, sizeof(struct de_dcmpr_two_layer_params));
902 tlp.codec1_type1 = dmslzh_codectype1;
903 tlp.codec1_private_params = (void*)lzhparams;
904 tlp.codec2 = dmsrle_codec;
905 tlp.dcmpri = dcmpri;
906 tlp.dcmpro = dcmpro;
907 tlp.dres = dres;
908 tlp.intermed_expected_len = tri->intermediate_len;
909 tlp.intermed_len_known = 1;
910 de_dfilter_decompress_two_layer(c, &tlp);
913 static void do_decompress_heavy(deark *c, struct dmsctx *d, struct dms_track_info *tri,
914 struct de_dfilter_in_params *dcmpri, struct de_dfilter_out_params *dcmpro,
915 struct de_dfilter_results *dres)
917 struct dmslzh_params lzhparams;
919 de_zeromem(&lzhparams, sizeof(struct dmslzh_params));
920 lzhparams.cmpr_type = tri->cmpr_type;
921 lzhparams.dms_track_flags = tri->track_flags;
922 if(tri->is_real) {
923 lzhparams.heavy_state = d->saved_heavy_state;
924 d->saved_heavy_state = NULL;
927 if(tri->track_flags & 0x04) {
928 do_decompress_heavy_lzh_rle(c, d, tri, dcmpri, dcmpro, dres, &lzhparams);
930 else {
931 // LZH, no RLE
932 dmslzh_codectype1(c, dcmpri, dcmpro, dres, (void*)&lzhparams);
935 if(tri->is_real) {
936 d->saved_heavy_state = lzhparams.heavy_state;
938 else {
939 destroy_heavy_state(c, lzhparams.heavy_state);
943 static void destroy_saved_dcrmpr_state(deark *c, struct dmsctx *d)
945 if(d->saved_medium_state.saved_state) {
946 destroy_std_cmpr_state(c, d->saved_medium_state.saved_state);
947 d->saved_medium_state.saved_state = NULL;
949 if(d->saved_deep_cmpr_state.saved_state) {
950 destroy_std_cmpr_state(c, d->saved_deep_cmpr_state.saved_state);
951 d->saved_deep_cmpr_state.saved_state = NULL;
953 if(d->saved_heavy_state) {
954 destroy_heavy_state(c, d->saved_heavy_state);
955 d->saved_heavy_state = NULL;
959 static int dms_decompress_track(deark *c, struct dmsctx *d, struct dms_track_info *tri,
960 dbuf *outf)
962 int retval = 0;
963 i64 unc_nbytes;
964 struct de_dfilter_in_params dcmpri;
965 struct de_dfilter_out_params dcmpro;
966 struct de_dfilter_results dres;
968 if(outf->len!=0) goto done;
970 if(tri->dpos + tri->cmpr_len > c->infile->len) {
971 de_err(c, "Track goes beyond end of file");
972 goto done;
975 de_dfilter_init_objects(c, &dcmpri, &dcmpro, &dres);
976 dcmpri.f = c->infile;
977 dcmpri.pos = tri->dpos;
978 dcmpri.len = tri->cmpr_len;
979 dcmpro.f = outf;
980 dcmpro.len_known = 1;
981 dcmpro.expected_len = tri->uncmpr_len;
983 tri->cksum_calc = 0;
985 if(tri->cmpr_type==DMSCMPR_NONE) {
986 fmtutil_decompress_uncompressed(c, &dcmpri, &dcmpro, &dres, 0);
988 else if(tri->cmpr_type==DMSCMPR_RLE) {
989 de_dfilter_decompress_oneshot(c, dmsrle_codec, NULL,
990 &dcmpri, &dcmpro, &dres);
992 else if(tri->cmpr_type==DMSCMPR_MEDIUM) {
993 do_decompress_track_medium_or_deep(c, d, tri, &dcmpri, &dcmpro, &dres);
995 else if(tri->cmpr_type==DMSCMPR_DEEP) {
996 do_decompress_track_medium_or_deep(c, d, tri, &dcmpri, &dcmpro, &dres);
998 else if(tri->cmpr_type==DMSCMPR_HEAVY1 || tri->cmpr_type==DMSCMPR_HEAVY2) {
999 do_decompress_heavy(c, d, tri, &dcmpri, &dcmpro, &dres);
1001 else {
1002 de_err(c, "[%s] Unsupported compression method: %u (%s)",
1003 tri->shortname, tri->cmpr_type,
1004 dms_get_cmprtype_name(tri->cmpr_type));
1005 goto done;
1008 if(dres.errcode) {
1009 de_err(c, "[%s] Decompression failed: %s", tri->shortname,
1010 de_dfilter_get_errmsg(c, &dres));
1011 goto done;
1014 unc_nbytes = outf->len;
1016 dbuf_truncate(outf, tri->uncmpr_len);
1018 if(unc_nbytes < tri->uncmpr_len) {
1019 de_err(c, "[%s] Expected %"I64_FMT" decompressed bytes, got %"I64_FMT,
1020 tri->shortname, tri->uncmpr_len, unc_nbytes);
1021 goto done;
1023 if(unc_nbytes > tri->uncmpr_len) {
1024 de_warn(c, "[%s] Expected %"I64_FMT" decompressed bytes, got %"I64_FMT,
1025 tri->shortname, tri->uncmpr_len, unc_nbytes);
1028 retval = 1;
1030 done:
1031 if(tri->is_real && !(tri->track_flags & 0x1)) {
1032 destroy_saved_dcrmpr_state(c, d);
1034 return retval;
1037 static int dms_checksum_cbfn(struct de_bufferedreadctx *brctx, const u8 *buf,
1038 i64 buf_len)
1040 u32 *cksum = (u32*)brctx->userdata;
1041 i64 i;
1043 for(i=0; i<buf_len; i++) {
1044 *cksum += (u32)buf[i];
1046 return 1;
1049 // outf is presumed to be membuf containing one track, and nothing else.
1050 static u32 dms_calc_checksum(deark *c, dbuf *outf)
1052 u32 cksum = 0;
1054 dbuf_buffered_read(outf, 0, outf->len, dms_checksum_cbfn, (void*)&cksum);
1055 cksum &= 0xffff;
1056 return cksum;
1059 static void get_trackflags_descr(deark *c, de_ucstring *s, UI tflags1, UI cmpr)
1061 UI tflags = tflags1;
1063 if(cmpr==5 || cmpr==6) {
1064 if(tflags & 0x4) {
1065 ucstring_append_flags_item(s, "w/RLE");
1066 tflags -= 0x4;
1068 if(tflags & 0x2) {
1069 ucstring_append_flags_item(s, "track has Huffman tree defs");
1070 tflags -= 0x2;
1073 if(tflags & 0x1) {
1074 ucstring_append_flags_item(s, "persist decompr. state");
1075 tflags -= 0x1;
1077 if(tflags>0) ucstring_append_flags_itemf(s, "0x%02x", tflags);
1080 // Read track and decompress to outf (which caller supplies as an empty membuf).
1081 // track_idx: the index into d->tracks_by_file_order
1082 // Returns nonzero if successfully decompressed.
1083 static int dms_read_and_decompress_track(deark *c, struct dmsctx *d,
1084 i64 track_idx, dbuf *outf)
1086 i64 pos1, pos;
1087 struct dms_track_info *tri = NULL;
1088 de_ucstring *descr = NULL;
1089 int retval = 0;
1090 int saved_indent_level;
1092 de_dbg_indent_save(c, &saved_indent_level);
1094 tri = de_malloc(c, sizeof(struct dms_track_info));
1095 pos1 = d->tracks_by_file_order[track_idx].file_pos;
1096 tri->track_num = (i64)d->tracks_by_file_order[track_idx].track_num;
1097 tri->is_real = d->tracks_by_file_order[track_idx].is_real;
1098 de_snprintf(tri->shortname, sizeof(tri->shortname), "%strack %d",
1099 (tri->is_real?"":"extra "), (int)tri->track_num);
1101 de_dbg(c, "%s at %"I64_FMT, tri->shortname, pos1);
1102 de_dbg_indent(c, 1);
1103 pos = pos1;
1104 pos += 2; // signature, already checked
1105 pos += 2; // reported track number, already read
1106 pos += 2; // Unknown field
1107 tri->cmpr_len = de_getu16be_p(&pos);
1108 de_dbg(c, "cmpr len: %"I64_FMT, tri->cmpr_len);
1109 tri->intermediate_len = de_getu16be_p(&pos);
1110 de_dbg(c, "intermediate len: %"I64_FMT, tri->intermediate_len);
1111 tri->uncmpr_len = de_getu16be_p(&pos);
1112 de_dbg(c, "uncmpr len: %"I64_FMT, tri->uncmpr_len);
1114 tri->track_flags = (UI)de_getbyte_p(&pos);
1115 tri->cmpr_type = (UI)de_getbyte_p(&pos);
1117 descr = ucstring_create(c);
1118 get_trackflags_descr(c, descr, tri->track_flags, tri->cmpr_type);
1119 de_dbg(c, "track flags: 0x%02x (%s)", tri->track_flags, ucstring_getpsz_d(descr));
1121 de_dbg(c, "track cmpr type: %u (%s)", tri->cmpr_type, dms_get_cmprtype_name(tri->cmpr_type));
1122 tri->cksum_reported = (u32)de_getu16be_p(&pos);
1123 de_dbg(c, "checksum (reported): 0x%04x", (UI)tri->cksum_reported);
1124 tri->crc_cmprdata_reported = (u32)de_getu16be_p(&pos);
1125 de_dbg(c, "crc of cmpr data (reported): 0x%04x", (UI)tri->crc_cmprdata_reported);
1126 tri->crc_header_reported = (u32)de_getu16be_p(&pos);
1127 de_dbg(c, "crc of header (reported): 0x%04x", (UI)tri->crc_header_reported);
1129 tri->dpos = pos1 + DMS_TRACK_HDR_LEN;
1130 de_dbg(c, "cmpr data at %"I64_FMT, tri->dpos);
1131 de_dbg_indent(c, 1);
1132 if(!dms_decompress_track(c, d, tri, outf)) goto done;
1133 de_dbg_indent(c, -1);
1135 tri->cksum_calc = dms_calc_checksum(c, outf);
1136 de_dbg(c, "checksum (calculated): 0x%04x", (UI)tri->cksum_calc);
1137 if(tri->cksum_calc != tri->cksum_reported) {
1138 de_err(c, "[%s] Checksum check failed", tri->shortname);
1139 goto done;
1141 retval = 1;
1143 done:
1144 ucstring_destroy(descr);
1145 de_free(c, tri);
1146 de_dbg_indent_restore(c, saved_indent_level);
1147 return retval;
1150 static void write_extra_track(deark *c, struct dmsctx *d, i64 track_idx, dbuf *trackbuf)
1152 char ext[80];
1153 dbuf *outf_extra = NULL;
1155 de_snprintf(ext, sizeof(ext), "extratrack%d.bin",
1156 (int)d->tracks_by_file_order[track_idx].track_num);
1157 outf_extra = dbuf_create_output_file(c, ext, NULL, DE_CREATEFLAG_IS_AUX);
1158 dbuf_copy(trackbuf, 0, trackbuf->len, outf_extra);
1159 dbuf_close(outf_extra);
1162 // Write out all the tracks, whether real or extra.
1163 static void do_dms_main(deark *c, struct dmsctx *d)
1165 i64 i;
1166 int real_track_failure_flag = 0;
1167 dbuf *outf = NULL;
1168 dbuf *trackbuf = NULL;
1170 trackbuf = dbuf_create_membuf(c, 11264, 0);
1171 outf = dbuf_create_output_file(c, "adf", NULL, 0);
1173 for(i=0; i<d->num_tracks_in_file; i++) {
1174 int ret_dcmpr;
1176 if(real_track_failure_flag && d->tracks_by_file_order[i].is_real) {
1177 continue;
1180 dbuf_truncate(trackbuf, 0);
1182 ret_dcmpr = dms_read_and_decompress_track(c, d, i, trackbuf);
1184 if(!ret_dcmpr) {
1185 if(d->tracks_by_file_order[i].is_real) {
1186 real_track_failure_flag = 1;
1188 continue;
1191 if(d->tracks_by_file_order[i].is_real) {
1192 dbuf_copy(trackbuf, 0, trackbuf->len, outf);
1194 else {
1195 write_extra_track(c, d, i, trackbuf);
1199 dbuf_close(outf);
1200 dbuf_close(trackbuf);
1203 static int do_dms_header(deark *c, struct dmsctx *d, i64 pos1)
1205 i64 n;
1206 i64 pos = pos1;
1207 struct de_timestamp cr_time;
1208 int retval = 0;
1210 de_dbg(c, "header at %"I64_FMT, pos1);
1211 de_dbg_indent(c, 1);
1213 // [0..3] = signature
1214 pos = pos1+8;
1215 d->info_bits = (UI)de_getu32be_p(&pos); // [8..11] = info bits
1216 de_dbg(c, "infobits: 0x%08x", d->info_bits);
1218 de_zeromem(&cr_time, sizeof(struct de_timestamp));
1219 read_unix_timestamp(c, pos, &cr_time, "creation time");
1220 pos += 4;
1222 d->first_track = de_getu16be_p(&pos); // [16..17] = firsttrack
1223 de_dbg(c, "first track: %d", (int)d->first_track);
1224 if(d->first_track >= DMS_MAX_TRACKS) goto done;
1225 if(d->first_track != 0) {
1226 de_info(c, "Note: First track is #%d, not #0. This may be a partial disk image.",
1227 (int)d->first_track);
1230 d->last_track = de_getu16be_p(&pos); // [18..19] = lasttrack
1231 de_dbg(c, "last track: %u", (int)d->last_track);
1232 if(d->last_track < d->first_track) goto done;
1233 if(d->last_track >= DMS_MAX_TRACKS) goto done;
1235 n = de_getu32be_p(&pos); // [20..23] = packed len
1236 de_dbg(c, "compressed len: %"I64_FMT, n);
1238 n = de_getu32be_p(&pos); // [24..27] = unpacked len
1239 de_dbg(c, "decompressed len: %"I64_FMT, n);
1241 // [46..47] = creating software version
1242 pos = pos1+50;
1243 n = de_getu16be_p(&pos); // [50..51] = disk type
1244 de_dbg(c, "disk type: %u", (UI)n);
1246 d->cmpr_type = (UI)de_getu16be_p(&pos); // [52..53] = compression mode
1247 de_dbg(c, "compression type: %u (%s)", d->cmpr_type,
1248 dms_get_cmprtype_name(d->cmpr_type));
1250 n = de_getu16be_p(&pos); // [54..55] = crc
1251 de_dbg(c, "crc (reported): 0x%04x", (UI)n);
1253 retval = 1;
1255 done:
1256 de_dbg_indent(c, -1);
1257 return retval;
1260 static int dms_scan_file(deark *c, struct dmsctx *d, i64 pos1)
1262 i64 pos = pos1;
1263 i64 i;
1264 u32 next_real_tracknum_expected;
1265 int retval = 0;
1267 de_dbg(c, "scanning file");
1268 de_dbg_indent(c, 1);
1270 d->num_tracks_in_file = 0;
1272 while(1) {
1273 i64 track_num_reported;
1274 i64 cmpr_len;
1275 i64 uncmpr_len;
1276 u8 track_flags;
1277 u8 cmpr_type;
1279 if(pos+DMS_TRACK_HDR_LEN > c->infile->len) break;
1281 if(dbuf_memcmp(c->infile, pos, "TR", 2)) {
1282 de_dbg(c, "[track not found at %"I64_FMT"; assuming disk image ends here]", pos);
1283 break;
1285 if(d->num_tracks_in_file >= DMS_MAX_TRACKS) {
1286 de_err(c, "Too many tracks in file");
1287 break;
1290 track_num_reported = de_getu16be(pos+2);
1291 cmpr_len = de_getu16be(pos+6);
1292 uncmpr_len = de_getu16be(pos+10);
1293 track_flags = de_getbyte(pos+12);
1294 cmpr_type = de_getbyte(pos+13);
1296 de_dbg(c, "track[%d] at %"I64_FMT", #%d, len=%"I64_FMT"/%"I64_FMT", cmpr=%u, flags=0x%02x",
1297 (int)d->num_tracks_in_file, pos, (int)track_num_reported, cmpr_len, uncmpr_len,
1298 (UI)cmpr_type, (UI)track_flags);
1300 d->tracks_by_file_order[d->num_tracks_in_file].file_pos = pos;
1301 d->tracks_by_file_order[d->num_tracks_in_file].track_num = (u32)track_num_reported;
1303 if(track_num_reported>=d->first_track && track_num_reported<=d->last_track) {
1304 d->tracks_by_track_num[track_num_reported].order_in_file = (u32)d->num_tracks_in_file;
1305 d->tracks_by_track_num[track_num_reported].in_use = 1;
1308 d->num_tracks_in_file++;
1309 pos += DMS_TRACK_HDR_LEN + cmpr_len;
1312 // Make sure all expected tracks are present, and mark the "real" tracks in
1313 // tracks_by_file_order[].
1314 // One reason for doing it this way is that there may be two tracks numbered 0,
1315 // with the second one being the real one.
1316 for(i=d->first_track; i<=d->last_track; i++) {
1317 if(!d->tracks_by_track_num[i].in_use) {
1318 // TODO: Maybe we should write a track of all zeroes instead (but how many zeroes?)
1319 de_err(c, "Could not find track #%d", (int)i);
1320 goto done;
1323 d->tracks_by_file_order[d->tracks_by_track_num[i].order_in_file].is_real = 1;
1327 next_real_tracknum_expected = (u32)d->first_track;
1328 for(i=0; i<d->num_tracks_in_file; i++) {
1329 if(d->tracks_by_file_order[i].is_real) {
1330 // I'm not going to bother supporting out-of-order tracks, at least until
1331 // I learn that such files exist.
1332 if(d->tracks_by_file_order[i].track_num != next_real_tracknum_expected) {
1333 de_err(c, "Track numbers not in order. Not supported.");
1334 goto done;
1336 next_real_tracknum_expected = d->tracks_by_file_order[i].track_num + 1;
1340 retval = 1;
1341 done:
1342 de_dbg_indent(c, -1);
1343 return retval;
1346 static void de_run_amiga_dms(deark *c, de_module_params *mparams)
1348 struct dmsctx *d = NULL;
1350 d = de_malloc(c, sizeof(struct dmsctx));
1351 if(!do_dms_header(c, d, 0)) goto done;
1352 if(!dms_scan_file(c, d, DMS_FILE_HDR_LEN)) goto done;
1353 do_dms_main(c, d);
1355 done:
1356 if(d) {
1357 destroy_saved_dcrmpr_state(c, d);
1358 de_free(c, d);
1362 static int de_identify_amiga_dms(deark *c)
1364 i64 dcmpr_size;
1366 if(dbuf_memcmp(c->infile, 0, "DMS!", 4)) return 0;
1367 dcmpr_size = de_getu32be(24);
1368 if(dcmpr_size==901120) return 100;
1369 return 85;
1372 void de_module_amiga_dms(deark *c, struct deark_module_info *mi)
1374 mi->id = "amiga_dms";
1375 mi->desc = "Amiga DMS disk image";
1376 mi->run_fn = de_run_amiga_dms;
1377 mi->identify_fn = de_identify_amiga_dms;