New "ea_data" module
[deark.git] / modules / dms.c
blobdb37784eaee856be554290662e291dafb34cf9e3
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 dmsheavy_cmpr_state;
75 struct dmsctx {
76 UI info_bits;
77 UI cmpr_type;
78 i64 first_track, last_track;
79 i64 num_tracks_in_file;
81 // Entries in use: 0 <= n < .num_tracks_in_file
82 struct dms_tracks_by_file_order_entry tracks_by_file_order[DMS_MAX_TRACKS];
84 // Entries potentially in use: .first_track <= n <= .last_track
85 struct dms_tracks_by_track_num_entry tracks_by_track_num[DMS_MAX_TRACKS];
87 struct dmsmedium_cmpr_state *saved_medium_state;
88 struct dmsheavy_cmpr_state *saved_heavy_state;
91 static const char *dms_get_cmprtype_name(UI n)
93 const char *name = NULL;
94 switch(n) {
95 case DMSCMPR_NONE: name="uncompressed"; break;
96 case DMSCMPR_RLE: name="simple (RLE)"; break;
97 case DMSCMPR_QUICK: name="quick"; break;
98 case DMSCMPR_MEDIUM: name="medium (RLE + LZ77)"; break;
99 case DMSCMPR_DEEP: name="deep (RLE + LZ77+dynamic_huffman)"; break;
100 case DMSCMPR_HEAVY1: name="heavy1 (optional RLE + LZ77-4K+Huffman)"; break;
101 case DMSCMPR_HEAVY2: name="heavy2 (optional RLE + LZ77-8K+Huffman)"; break;
103 return name?name:"?";
106 static void read_unix_timestamp(deark *c, i64 pos, struct de_timestamp *ts, const char *name)
108 i64 t;
109 char timestamp_buf[64];
111 t = de_geti32be(pos);
112 de_unix_time_to_timestamp(t, ts, 0x1);
113 de_timestamp_to_string(ts, timestamp_buf, sizeof(timestamp_buf), 0);
114 de_dbg(c, "%s: %"I64_FMT" (%s)", name, t, timestamp_buf);
117 /////// Heavy (LZH) compression ///////
119 // Note: A lot of this is very similar to the code in fmtutil-lzh.c.
120 // The main problem with using standard LZH code for DMS is that some of the
121 // decompression state persists from one track to the next. But not all of it
122 // -- you can't just concatenate the compressed data together before
123 // decompressing it.
125 struct lzh_tree_wrapper {
126 struct fmtutil_huffman_decoder *ht;
129 // The portion of the Heavy decompression context that can persist between tracks.
130 struct dmsheavy_cmpr_state {
131 UI cmpr_type;
132 UI heavy_prev_offset;
133 struct de_lz77buffer *ringbuf;
134 u8 trees_exist;
135 struct lzh_tree_wrapper literals_tree;
136 struct lzh_tree_wrapper offsets_tree;
139 // The portion of the Heavy decompression context that does *not* persist between tracks.
140 struct lzh_ctx {
141 deark *c;
142 struct de_dfilter_in_params *dcmpri;
143 struct de_dfilter_out_params *dcmpro;
144 struct de_dfilter_results *dres;
145 const char *modname;
147 i64 nbytes_written;
148 int err_flag;
150 // bitrd.eof_flag: Always set if err_flag is set.
151 struct de_bitreader bitrd;
153 UI heavy_np;
156 struct dmslzh_params {
157 UI cmpr_type; // 5=heavy1, 6=heavy2
158 u8 dms_track_flags;
159 struct dmsheavy_cmpr_state *heavy_state;
162 static void lzh_set_eof_flag(struct lzh_ctx *cctx)
164 cctx->bitrd.eof_flag = 1;
167 static void lzh_set_err_flag(struct lzh_ctx *cctx)
169 lzh_set_eof_flag(cctx);
170 cctx->err_flag = 1;
173 static int lzh_have_enough_output(struct lzh_ctx *cctx)
175 if(cctx->dcmpro->len_known) {
176 if(cctx->nbytes_written >= cctx->dcmpro->expected_len) {
177 return 1;
180 return 0;
183 static void lha5like_lz77buf_writebytecb(struct de_lz77buffer *rb, u8 n)
185 struct lzh_ctx *cctx = (struct lzh_ctx*)rb->userdata;
187 if(lzh_have_enough_output(cctx)) {
188 return;
190 dbuf_writebyte(cctx->dcmpro->f, n);
191 cctx->nbytes_written++;
194 static UI read_next_code_using_tree(struct lzh_ctx *cctx, struct lzh_tree_wrapper *tree)
196 fmtutil_huffman_valtype val = 0;
197 int ret;
199 if(!tree->ht) goto done;
201 ret = fmtutil_huffman_read_next_value(tree->ht->bk, &cctx->bitrd, &val, NULL);
202 if(cctx->bitrd.eof_flag) {
203 de_dfilter_set_errorf(cctx->c, cctx->dres, cctx->modname,
204 "Unexpected end of compressed data");
205 lzh_set_err_flag(cctx);
206 val = 0;
207 goto done;
209 else if(!ret) {
210 de_dfilter_set_errorf(cctx->c, cctx->dres, cctx->modname,
211 "Huffman decoding error");
212 lzh_set_err_flag(cctx);
213 val = 0;
214 goto done;
217 done:
218 return (UI)val;
221 static int dmsheavy_read_tree(struct lzh_ctx *cctx, struct lzh_tree_wrapper *htw,
222 UI ncodes_nbits, UI symlen_nbits)
224 deark *c = cctx->c;
225 UI ncodes;
226 UI curr_idx;
227 int retval = 0;
229 if(htw->ht) goto done;
231 ncodes = (UI)de_bitreader_getbits(&cctx->bitrd, ncodes_nbits);
232 de_dbg2(c, "num codes: %u", ncodes);
234 htw->ht = fmtutil_huffman_create_decoder(c, (i64)ncodes, (i64)ncodes);
236 if(ncodes==0) {
237 UI null_val;
239 null_val = (UI)de_bitreader_getbits(&cctx->bitrd, ncodes_nbits);
240 fmtutil_huffman_add_code(c, htw->ht->bk, 0, 0, (fmtutil_huffman_valtype)null_val);
241 de_dbg3(c, "val0: %u", null_val);
242 retval = 1;
243 goto done;
246 curr_idx = 0;
247 while(curr_idx < ncodes) {
248 UI symlen;
250 symlen = (UI)de_bitreader_getbits(&cctx->bitrd, symlen_nbits);
251 de_dbg3(c, "len[%u] = %u", curr_idx, symlen);
252 fmtutil_huffman_record_a_code_length(c, htw->ht->builder, (fmtutil_huffman_valtype)curr_idx, symlen);
253 curr_idx++;
255 if(cctx->bitrd.eof_flag) goto done;
257 if(!fmtutil_huffman_make_canonical_code(c, htw->ht->bk, htw->ht->builder, 0)) goto done;
259 retval = 1;
260 done:
261 if(!retval) {
262 lzh_set_err_flag(cctx);
264 return retval;
267 static void dmsheavy_discard_tree(deark *c, struct lzh_tree_wrapper *htw)
269 if(htw->ht) {
270 fmtutil_huffman_destroy_decoder(c, htw->ht);
271 htw->ht = NULL;
275 static void decompress_dms_heavy(struct lzh_ctx *cctx, struct dmslzh_params *lzhp,
276 struct dmsheavy_cmpr_state *hvst)
278 deark *c = cctx->c;
279 UI rb_size;
280 int ret;
281 int saved_indent_level;
282 char pos_descr[32];
284 de_dbg_indent_save(c, &saved_indent_level);
286 if(lzhp->cmpr_type != hvst->cmpr_type) {
287 de_dfilter_set_errorf(c, cctx->dres, cctx->modname,
288 "Mixing Heavy compression types is not supported");
289 goto done;
292 if(lzhp->cmpr_type==DMSCMPR_HEAVY1) {
293 rb_size = 4096;
294 cctx->heavy_np = 14; // for heavy1
296 else {
297 rb_size = 8192;
298 cctx->heavy_np = 15; // for heavy2
301 if(!hvst->ringbuf) {
302 hvst->ringbuf = de_lz77buffer_create(cctx->c, rb_size);
305 hvst->ringbuf->userdata = (void*)cctx;
306 hvst->ringbuf->writebyte_cb = lha5like_lz77buf_writebytecb;
308 if(!cctx->dcmpro->len_known) {
309 // I think we (may) have to know the output length, because zero-length Huffman
310 // codes are(?) possible, and unlike lh5 we aren't told how many codes there are.
311 de_dfilter_set_errorf(cctx->c, cctx->dres, cctx->modname, "Internal error");
312 goto done;
315 if(lzhp->dms_track_flags & 0x02) {
316 dmsheavy_discard_tree(c, &hvst->literals_tree);
317 dmsheavy_discard_tree(c, &hvst->offsets_tree);
318 hvst->trees_exist = 0;
321 if(!hvst->trees_exist) {
322 hvst->trees_exist = 1;
323 de_dbg2(c, "c tree");
324 de_dbg_indent(c, 1);
325 ret = dmsheavy_read_tree(cctx, &hvst->literals_tree, 9, 5);
326 de_dbg_indent(c, -1);
327 if(!ret) goto done;
329 de_dbg2(c, "p tree");
330 de_dbg_indent(c, 1);
331 ret = dmsheavy_read_tree(cctx, &hvst->offsets_tree, 5, 4);
332 de_dbg_indent(c, -1);
333 if(!ret) goto done;
336 de_bitreader_describe_curpos(&cctx->bitrd, pos_descr, sizeof(pos_descr));
337 de_dbg2(c, "cmpr data codes at %s", pos_descr);
338 de_dbg_indent(c, 1);
339 while(1) {
340 UI code;
342 if(cctx->bitrd.eof_flag) goto done;
343 if(lzh_have_enough_output(cctx)) goto done;
345 code = read_next_code_using_tree(cctx, &hvst->literals_tree);
346 if(cctx->bitrd.eof_flag) goto done;
347 if(c->debug_level>=3) {
348 de_dbg3(c, "code: %u (opos=%"I64_FMT")", code, cctx->dcmpro->f->len);
351 if(code < 256) { // literal
352 de_lz77buffer_add_literal_byte(hvst->ringbuf, (u8)code);
354 else { // repeat previous bytes
355 UI offset;
356 UI length;
357 UI ocode1;
359 length = code-253;
360 ocode1 = read_next_code_using_tree(cctx, &hvst->offsets_tree);
361 if(cctx->bitrd.eof_flag) goto done;
363 if(ocode1 == cctx->heavy_np-1) {
364 offset = hvst->heavy_prev_offset;
366 else {
367 if(ocode1 < 1) {
368 offset = ocode1;
370 else {
371 UI ocode2;
373 ocode2 = (UI)de_bitreader_getbits(&cctx->bitrd, ocode1-1);
374 if(cctx->bitrd.eof_flag) goto done;
375 offset = ocode2 | (1U<<(ocode1-1));
377 hvst->heavy_prev_offset = offset;
380 de_lz77buffer_copy_from_hist(hvst->ringbuf,
381 (UI)(hvst->ringbuf->curpos-offset-1), length);
385 done:
386 de_dbg_indent_restore(c, saved_indent_level);
389 static void destroy_heavy_state(deark *c, struct dmsheavy_cmpr_state *hvst)
391 if(!hvst) return;
392 dmsheavy_discard_tree(c, &hvst->literals_tree);
393 dmsheavy_discard_tree(c, &hvst->offsets_tree);
394 de_lz77buffer_destroy(c, hvst->ringbuf);
397 static void dmslzh_codectype1(deark *c, struct de_dfilter_in_params *dcmpri,
398 struct de_dfilter_out_params *dcmpro, struct de_dfilter_results *dres,
399 void *codec_private_params)
401 struct dmslzh_params *lzhp = (struct dmslzh_params*)codec_private_params;
402 struct lzh_ctx *cctx = NULL;
403 struct dmsheavy_cmpr_state *hvst = NULL;
405 cctx = de_malloc(c, sizeof(struct lzh_ctx));
406 cctx->modname = "undmslzh";
407 cctx->c = c;
408 cctx->dcmpri = dcmpri;
409 cctx->dcmpro = dcmpro;
410 cctx->dres = dres;
411 cctx->bitrd.f = dcmpri->f;
412 cctx->bitrd.curpos = dcmpri->pos;
413 cctx->bitrd.endpos = dcmpri->pos + dcmpri->len;
415 if(lzhp->heavy_state) {
416 // If a previous decompression state exists, use it.
417 hvst = lzhp->heavy_state;
418 lzhp->heavy_state = NULL;
420 else {
421 hvst = de_malloc(c, sizeof(struct dmsheavy_cmpr_state));
422 hvst->cmpr_type = lzhp->cmpr_type;
425 decompress_dms_heavy(cctx, lzhp, hvst);
427 hvst->ringbuf->userdata = NULL;
428 hvst->ringbuf->writebyte_cb = NULL;
429 lzhp->heavy_state = hvst;
430 hvst = NULL;
432 if(cctx->err_flag) {
433 // A default error message
434 de_dfilter_set_errorf(c, dres, cctx->modname, "LZH decoding error");
435 goto done;
438 de_bitreader_skip_to_byte_boundary(&cctx->bitrd);
439 cctx->dres->bytes_consumed = cctx->bitrd.curpos - cctx->dcmpri->pos;
440 if(cctx->dres->bytes_consumed<0) {
441 cctx->dres->bytes_consumed = 0;
443 cctx->dres->bytes_consumed_valid = 1;
445 done:
446 if(hvst) destroy_heavy_state(c, hvst);
447 de_free(c, cctx);
450 /////// RLE compression ///////
452 // DMS RLE:
453 // n1 n2 n3 n4 n5
454 // ---------------------------------------------------------
455 // 0x90 0x00 emit 0x90
456 // 0x90 0x01..0xfe n3 emit n2 copies of n3
457 // 0x90 0xff n3 n4 n5 emit (n4#n5) copies of n3
458 // !0x90 emit n1
460 enum dmsrle_state {
461 DMSRLE_STATE_NEUTRAL = 0,
462 DMSRLE_STATE_90,
463 DMSRLE_STATE_90_N2,
464 DMSRLE_STATE_90_FF_N3,
465 DMSRLE_STATE_90_FF_N3_N4
468 struct dmsrle_ctx {
469 enum dmsrle_state state;
470 u8 n2, n3, n4;
473 static void dmsrle_codec_addbuf(struct de_dfilter_ctx *dfctx,
474 const u8 *buf, i64 buf_len)
476 i64 i;
477 struct dmsrle_ctx *rctx = (struct dmsrle_ctx*)dfctx->codec_private;
479 if(!rctx) goto done;
481 for(i=0; i<buf_len; i++) {
482 u8 n;
483 i64 count;
485 n = buf[i];
487 switch(rctx->state) {
488 case DMSRLE_STATE_NEUTRAL:
489 if(n==0x90) {
490 rctx->state = DMSRLE_STATE_90;
492 else {
493 dbuf_writebyte(dfctx->dcmpro->f, n);
495 break;
496 case DMSRLE_STATE_90:
497 if(n==0x00) {
498 dbuf_writebyte(dfctx->dcmpro->f, 0x90);
499 rctx->state = DMSRLE_STATE_NEUTRAL;
501 else {
502 rctx->n2 = n;
503 rctx->state = DMSRLE_STATE_90_N2;
505 break;
506 case DMSRLE_STATE_90_N2:
507 if(rctx->n2==0xff) {
508 rctx->n3 = n;
509 rctx->state = DMSRLE_STATE_90_FF_N3;
511 else {
512 count = (i64)rctx->n2;
513 dbuf_write_run(dfctx->dcmpro->f, n, count);
514 rctx->state = DMSRLE_STATE_NEUTRAL;
516 break;
517 case DMSRLE_STATE_90_FF_N3:
518 rctx->n4 = n;
519 rctx->state = DMSRLE_STATE_90_FF_N3_N4;
520 break;
521 case DMSRLE_STATE_90_FF_N3_N4:
522 count = (i64)(((UI)rctx->n4 << 8) | n);
523 dbuf_write_run(dfctx->dcmpro->f, rctx->n3, count);
524 rctx->state = DMSRLE_STATE_NEUTRAL;
525 break;
528 done:
532 static void dmsrle_codec_destroy(struct de_dfilter_ctx *dfctx)
534 struct dmsrle_ctx *rctx = (struct dmsrle_ctx*)dfctx->codec_private;
536 if(rctx) {
537 de_free(dfctx->c, rctx);
539 dfctx->codec_private = NULL;
542 // codec_private_params: Unused, should be NULL.
543 static void dmsrle_codec(struct de_dfilter_ctx *dfctx, void *codec_private_params)
545 struct dmsrle_ctx *rctx = NULL;
547 rctx = de_malloc(dfctx->c, sizeof(struct dmsrle_ctx));
548 rctx->state = DMSRLE_STATE_NEUTRAL;
549 dfctx->codec_private = (void*)rctx;
550 dfctx->codec_addbuf_fn = dmsrle_codec_addbuf;
551 dfctx->codec_finish_fn = NULL;
552 dfctx->codec_destroy_fn = dmsrle_codec_destroy;
555 ///////////////// "Medium" decompression //////////////
557 // The portion of the Medium decompression context that can persist between tracks.
558 struct dmsmedium_cmpr_state {
559 struct de_lz77buffer *ringbuf;
562 struct dmsmedium_params {
563 struct dmsmedium_cmpr_state *medium_state;
566 struct medium_ctx {
567 deark *c;
568 struct de_dfilter_out_params *dcmpro;
569 i64 nbytes_written;
570 struct de_bitreader bitrd;
573 static void destroy_medium_state(deark *c, struct dmsmedium_cmpr_state *mdst)
575 if(!mdst) return;
576 de_lz77buffer_destroy(c, mdst->ringbuf);
577 de_free(c, mdst);
580 static UI get_medium_d_code(UI n)
582 static const u8 medium_d_code[256] = {
583 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
584 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
585 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,
586 6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,
587 0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,
588 0x0c,0x0c,0x0c,0x0c,0x0d,0x0d,0x0d,0x0d,0x0e,0x0e,0x0e,0x0e,0x0f,0x0f,0x0f,0x0f,
589 0x10,0x10,0x10,0x10,0x11,0x11,0x11,0x11,0x12,0x12,0x12,0x12,0x13,0x13,0x13,0x13,
590 0x14,0x14,0x14,0x14,0x15,0x15,0x15,0x15,0x16,0x16,0x16,0x16,0x17,0x17,0x17,0x17,
591 0x18,0x18,0x19,0x19,0x1a,0x1a,0x1b,0x1b,0x1c,0x1c,0x1d,0x1d,0x1e,0x1e,0x1f,0x1f,
592 0x20,0x20,0x21,0x21,0x22,0x22,0x23,0x23,0x24,0x24,0x25,0x25,0x26,0x26,0x27,0x27,
593 0x28,0x28,0x29,0x29,0x2a,0x2a,0x2b,0x2b,0x2c,0x2c,0x2d,0x2d,0x2e,0x2e,0x2f,0x2f,
594 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f
597 return (UI)medium_d_code[(n&0xff)];
600 static UI get_medium_d_len(UI n)
602 static const u8 medium_d_len[16] = {
603 3,3,4,4,4,5,5,5,5,6,6,6,7,7,7,8
606 return (UI)medium_d_len[(n&0xff)/16];
609 static int medium_have_enough_output(struct medium_ctx *mctx)
611 if(mctx->dcmpro->len_known) {
612 if(mctx->nbytes_written >= mctx->dcmpro->expected_len) {
613 return 1;
616 return 0;
619 static void do_mediumlz77_internal(struct medium_ctx *mctx, struct dmsmedium_cmpr_state *mdst)
621 while(1) {
622 UI n;
624 if(mctx->bitrd.eof_flag) break;
625 if(medium_have_enough_output(mctx)) break;
627 n = (UI)de_bitreader_getbits(&mctx->bitrd, 1);
628 if(n) {
629 u8 b;
631 b = (u8)de_bitreader_getbits(&mctx->bitrd, 8);
632 de_lz77buffer_add_literal_byte(mdst->ringbuf, (u8)b);
633 } else {
634 UI first_code;
635 UI ocode1_nbits;
636 UI ocode1;
637 UI ocode2_nbits;
638 UI ocode2;
639 UI tmp_code;
640 UI length;
641 UI offset_rel;
643 // TODO: This seems overly complicated. Is there a simpler way to
644 // implement this?
646 first_code = (UI)de_bitreader_getbits(&mctx->bitrd, 8);
647 length = (UI)get_medium_d_code(first_code) + 3;
649 ocode1_nbits = (UI)get_medium_d_len(first_code);
650 ocode1 = (UI)de_bitreader_getbits(&mctx->bitrd, ocode1_nbits);
652 tmp_code = ((first_code << ocode1_nbits) | ocode1) & 0xff;
653 ocode2_nbits = (UI)get_medium_d_len(tmp_code);
654 ocode2 = (UI)de_bitreader_getbits(&mctx->bitrd, ocode2_nbits);
656 offset_rel = ((UI)get_medium_d_code(tmp_code) << 8) | (((tmp_code << ocode2_nbits) | ocode2) & 0xff);
657 de_lz77buffer_copy_from_hist(mdst->ringbuf, mdst->ringbuf->curpos - 1 - offset_rel, length);
661 de_lz77buffer_set_curpos(mdst->ringbuf, mdst->ringbuf->curpos + 66);
664 static void medium_lz77buf_writebytecb(struct de_lz77buffer *rb, u8 n)
666 struct medium_ctx *mctx = (struct medium_ctx *)rb->userdata;
668 if(medium_have_enough_output(mctx)) {
669 return;
671 dbuf_writebyte(mctx->dcmpro->f, n);
672 mctx->nbytes_written++;
675 static void mediumlz77_codectype1(deark *c, struct de_dfilter_in_params *dcmpri,
676 struct de_dfilter_out_params *dcmpro, struct de_dfilter_results *dres,
677 void *codec_private_params)
679 struct medium_ctx *mctx = NULL;
680 struct dmsmedium_params *mdparams = (struct dmsmedium_params*)codec_private_params;
681 struct dmsmedium_cmpr_state *mdst = NULL;
683 mctx = de_malloc(c, sizeof(struct medium_ctx));
684 mctx->c = c;
685 mctx->dcmpro = dcmpro;
686 mctx->bitrd.f = dcmpri->f;
687 mctx->bitrd.curpos = dcmpri->pos;
688 mctx->bitrd.endpos = dcmpri->pos + dcmpri->len;
690 if(mdparams->medium_state) {
691 // Acquire the previous 'state' object from the caller
692 mdst = mdparams->medium_state;
693 mdparams->medium_state = NULL;
695 else {
696 mdst = de_malloc(c, sizeof(struct dmsmedium_cmpr_state));
697 mdst->ringbuf = de_lz77buffer_create(c, 16*1024);
698 de_lz77buffer_set_curpos(mdst->ringbuf, 0x3fbe);
700 mdst->ringbuf->userdata = (void*)mctx;
701 mdst->ringbuf->writebyte_cb = medium_lz77buf_writebytecb;
703 do_mediumlz77_internal(mctx, mdst);
705 // Give the 'state' object back to the caller.
706 mdst->ringbuf->writebyte_cb = NULL;
707 mdst->ringbuf->userdata = NULL;
708 mdparams->medium_state = mdst;
709 mdst = NULL;
711 if(mctx) {
712 de_free(c, mctx);
714 if(mdst) {
715 destroy_medium_state(c, mdst);
719 static void do_decompress_medium(deark *c, struct dmsctx *d, struct dms_track_info *tri,
720 struct de_dfilter_in_params *dcmpri, struct de_dfilter_out_params *dcmpro,
721 struct de_dfilter_results *dres)
723 struct de_dcmpr_two_layer_params tlp;
724 struct dmsmedium_params mdparams;
726 de_zeromem(&mdparams, sizeof(struct dmsmedium_params));
727 if(tri->is_real) {
728 mdparams.medium_state = d->saved_medium_state;
729 d->saved_medium_state = NULL;
732 de_zeromem(&tlp, sizeof(struct de_dcmpr_two_layer_params));
733 tlp.codec1_type1 = mediumlz77_codectype1;
734 tlp.codec1_private_params = (void*)&mdparams;
735 tlp.codec2 = dmsrle_codec;
736 tlp.dcmpri = dcmpri;
737 tlp.dcmpro = dcmpro;
738 tlp.dres = dres;
739 tlp.intermed_expected_len = tri->intermediate_len;
740 tlp.intermed_len_known = 1;
741 de_dfilter_decompress_two_layer(c, &tlp);
743 if(tri->is_real) {
744 d->saved_medium_state = mdparams.medium_state;
746 else {
747 destroy_medium_state(c, mdparams.medium_state);
751 ///////////////////////////////////
753 static void do_decompress_heavy_lzh_rle(deark *c, struct dmsctx *d, struct dms_track_info *tri,
754 struct de_dfilter_in_params *dcmpri, struct de_dfilter_out_params *dcmpro,
755 struct de_dfilter_results *dres, struct dmslzh_params *lzhparams)
757 struct de_dcmpr_two_layer_params tlp;
759 de_zeromem(&tlp, sizeof(struct de_dcmpr_two_layer_params));
760 tlp.codec1_type1 = dmslzh_codectype1;
761 tlp.codec1_private_params = (void*)lzhparams;
762 tlp.codec2 = dmsrle_codec;
763 tlp.dcmpri = dcmpri;
764 tlp.dcmpro = dcmpro;
765 tlp.dres = dres;
766 tlp.intermed_expected_len = tri->intermediate_len;
767 tlp.intermed_len_known = 1;
768 de_dfilter_decompress_two_layer(c, &tlp);
771 static void do_decompress_heavy(deark *c, struct dmsctx *d, struct dms_track_info *tri,
772 struct de_dfilter_in_params *dcmpri, struct de_dfilter_out_params *dcmpro,
773 struct de_dfilter_results *dres)
775 struct dmslzh_params lzhparams;
777 de_zeromem(&lzhparams, sizeof(struct dmslzh_params));
778 lzhparams.cmpr_type = tri->cmpr_type;
779 lzhparams.dms_track_flags = tri->track_flags;
780 if(tri->is_real) {
781 lzhparams.heavy_state = d->saved_heavy_state;
782 d->saved_heavy_state = NULL;
785 if(tri->track_flags & 0x04) {
786 do_decompress_heavy_lzh_rle(c, d, tri, dcmpri, dcmpro, dres, &lzhparams);
788 else {
789 // LZH, no RLE
790 dmslzh_codectype1(c, dcmpri, dcmpro, dres, (void*)&lzhparams);
793 if(tri->is_real) {
794 d->saved_heavy_state = lzhparams.heavy_state;
796 else {
797 destroy_heavy_state(c, lzhparams.heavy_state);
801 static void destroy_saved_dcrmpr_state(deark *c, struct dmsctx *d)
803 if(d->saved_medium_state) {
804 destroy_medium_state(c, d->saved_medium_state);
805 d->saved_medium_state = NULL;
807 if(d->saved_heavy_state) {
808 destroy_heavy_state(c, d->saved_heavy_state);
809 d->saved_heavy_state = NULL;
813 static int dms_decompress_track(deark *c, struct dmsctx *d, struct dms_track_info *tri,
814 dbuf *outf)
816 int retval = 0;
817 i64 unc_nbytes;
818 struct de_dfilter_in_params dcmpri;
819 struct de_dfilter_out_params dcmpro;
820 struct de_dfilter_results dres;
822 if(outf->len!=0) goto done;
824 if(tri->dpos + tri->cmpr_len > c->infile->len) {
825 de_err(c, "Track goes beyond end of file");
826 goto done;
829 de_dfilter_init_objects(c, &dcmpri, &dcmpro, &dres);
830 dcmpri.f = c->infile;
831 dcmpri.pos = tri->dpos;
832 dcmpri.len = tri->cmpr_len;
833 dcmpro.f = outf;
834 dcmpro.len_known = 1;
835 dcmpro.expected_len = tri->uncmpr_len;
837 tri->cksum_calc = 0;
839 if(tri->cmpr_type==DMSCMPR_NONE) {
840 fmtutil_decompress_uncompressed(c, &dcmpri, &dcmpro, &dres, 0);
842 else if(tri->cmpr_type==DMSCMPR_RLE) {
843 de_dfilter_decompress_oneshot(c, dmsrle_codec, NULL,
844 &dcmpri, &dcmpro, &dres);
846 else if(tri->cmpr_type==DMSCMPR_MEDIUM) {
847 do_decompress_medium(c, d, tri, &dcmpri, &dcmpro, &dres);
849 else if(tri->cmpr_type==DMSCMPR_HEAVY1 || tri->cmpr_type==DMSCMPR_HEAVY2) {
850 do_decompress_heavy(c, d, tri, &dcmpri, &dcmpro, &dres);
852 else {
853 de_err(c, "[%s] Unsupported compression method: %u (%s)",
854 tri->shortname, tri->cmpr_type,
855 dms_get_cmprtype_name(tri->cmpr_type));
856 goto done;
859 if(dres.errcode) {
860 de_err(c, "[%s] Decompression failed: %s", tri->shortname,
861 de_dfilter_get_errmsg(c, &dres));
862 goto done;
865 unc_nbytes = outf->len;
867 dbuf_truncate(outf, tri->uncmpr_len);
869 if(unc_nbytes < tri->uncmpr_len) {
870 de_err(c, "[%s] Expected %"I64_FMT" decompressed bytes, got %"I64_FMT,
871 tri->shortname, tri->uncmpr_len, unc_nbytes);
872 goto done;
874 if(unc_nbytes > tri->uncmpr_len) {
875 de_warn(c, "[%s] Expected %"I64_FMT" decompressed bytes, got %"I64_FMT,
876 tri->shortname, tri->uncmpr_len, unc_nbytes);
879 retval = 1;
881 done:
882 if(tri->is_real && !(tri->track_flags & 0x1)) {
883 destroy_saved_dcrmpr_state(c, d);
885 return retval;
888 static int dms_checksum_cbfn(struct de_bufferedreadctx *brctx, const u8 *buf,
889 i64 buf_len)
891 u32 *cksum = (u32*)brctx->userdata;
892 i64 i;
894 for(i=0; i<buf_len; i++) {
895 *cksum += (u32)buf[i];
897 return 1;
900 // outf is presumed to be membuf containing one track, and nothing else.
901 static u32 dms_calc_checksum(deark *c, dbuf *outf)
903 u32 cksum = 0;
905 dbuf_buffered_read(outf, 0, outf->len, dms_checksum_cbfn, (void*)&cksum);
906 cksum &= 0xffff;
907 return cksum;
910 static void get_trackflags_descr(deark *c, de_ucstring *s, UI tflags1, UI cmpr)
912 UI tflags = tflags1;
914 if(cmpr==5 || cmpr==6) {
915 if(tflags & 0x4) {
916 ucstring_append_flags_item(s, "w/RLE");
917 tflags -= 0x4;
919 if(tflags & 0x2) {
920 ucstring_append_flags_item(s, "track has Huffman tree defs");
921 tflags -= 0x2;
924 if(tflags & 0x1) {
925 ucstring_append_flags_item(s, "persist decompr. state");
926 tflags -= 0x1;
928 if(tflags>0) ucstring_append_flags_itemf(s, "0x%02x", tflags);
931 // Read track and decompress to outf (which caller supplies as an empty membuf).
932 // track_idx: the index into d->tracks_by_file_order
933 // Returns nonzero if successfully decompressed.
934 static int dms_read_and_decompress_track(deark *c, struct dmsctx *d,
935 i64 track_idx, dbuf *outf)
937 i64 pos1, pos;
938 struct dms_track_info *tri = NULL;
939 de_ucstring *descr = NULL;
940 int retval = 0;
941 int saved_indent_level;
943 de_dbg_indent_save(c, &saved_indent_level);
945 tri = de_malloc(c, sizeof(struct dms_track_info));
946 pos1 = d->tracks_by_file_order[track_idx].file_pos;
947 tri->track_num = (i64)d->tracks_by_file_order[track_idx].track_num;
948 tri->is_real = d->tracks_by_file_order[track_idx].is_real;
949 de_snprintf(tri->shortname, sizeof(tri->shortname), "%strack %d",
950 (tri->is_real?"":"extra "), (int)tri->track_num);
952 de_dbg(c, "%s at %"I64_FMT, tri->shortname, pos1);
953 de_dbg_indent(c, 1);
954 pos = pos1;
955 pos += 2; // signature, already checked
956 pos += 2; // reported track number, already read
957 pos += 2; // Unknown field
958 tri->cmpr_len = de_getu16be_p(&pos);
959 de_dbg(c, "cmpr len: %"I64_FMT, tri->cmpr_len);
960 tri->intermediate_len = de_getu16be_p(&pos);
961 de_dbg(c, "intermediate len: %"I64_FMT, tri->intermediate_len);
962 tri->uncmpr_len = de_getu16be_p(&pos);
963 de_dbg(c, "uncmpr len: %"I64_FMT, tri->uncmpr_len);
965 tri->track_flags = (UI)de_getbyte_p(&pos);
966 tri->cmpr_type = (UI)de_getbyte_p(&pos);
968 descr = ucstring_create(c);
969 get_trackflags_descr(c, descr, tri->track_flags, tri->cmpr_type);
970 de_dbg(c, "track flags: 0x%02x (%s)", tri->track_flags, ucstring_getpsz_d(descr));
972 de_dbg(c, "track cmpr type: %u (%s)", tri->cmpr_type, dms_get_cmprtype_name(tri->cmpr_type));
973 tri->cksum_reported = (u32)de_getu16be_p(&pos);
974 de_dbg(c, "checksum (reported): 0x%04x", (UI)tri->cksum_reported);
975 tri->crc_cmprdata_reported = (u32)de_getu16be_p(&pos);
976 de_dbg(c, "crc of cmpr data (reported): 0x%04x", (UI)tri->crc_cmprdata_reported);
977 tri->crc_header_reported = (u32)de_getu16be_p(&pos);
978 de_dbg(c, "crc of header (reported): 0x%04x", (UI)tri->crc_header_reported);
980 tri->dpos = pos1 + DMS_TRACK_HDR_LEN;
981 de_dbg(c, "cmpr data at %"I64_FMT, tri->dpos);
982 de_dbg_indent(c, 1);
983 if(!dms_decompress_track(c, d, tri, outf)) goto done;
984 de_dbg_indent(c, -1);
986 tri->cksum_calc = dms_calc_checksum(c, outf);
987 de_dbg(c, "checksum (calculated): 0x%04x", (UI)tri->cksum_calc);
988 if(tri->cksum_calc != tri->cksum_reported) {
989 de_err(c, "[%s] Checksum check failed", tri->shortname);
990 goto done;
992 retval = 1;
994 done:
995 ucstring_destroy(descr);
996 de_free(c, tri);
997 de_dbg_indent_restore(c, saved_indent_level);
998 return retval;
1001 static void write_extra_track(deark *c, struct dmsctx *d, i64 track_idx, dbuf *trackbuf)
1003 char ext[80];
1004 dbuf *outf_extra = NULL;
1006 de_snprintf(ext, sizeof(ext), "extratrack%d.bin",
1007 (int)d->tracks_by_file_order[track_idx].track_num);
1008 outf_extra = dbuf_create_output_file(c, ext, NULL, DE_CREATEFLAG_IS_AUX);
1009 dbuf_copy(trackbuf, 0, trackbuf->len, outf_extra);
1010 dbuf_close(outf_extra);
1013 // Write out all the tracks, whether real or extra.
1014 static void do_dms_main(deark *c, struct dmsctx *d)
1016 i64 i;
1017 int real_track_failure_flag = 0;
1018 dbuf *outf = NULL;
1019 dbuf *trackbuf = NULL;
1021 trackbuf = dbuf_create_membuf(c, 11264, 0);
1022 outf = dbuf_create_output_file(c, "adf", NULL, 0);
1024 for(i=0; i<d->num_tracks_in_file; i++) {
1025 int ret_dcmpr;
1027 if(real_track_failure_flag && d->tracks_by_file_order[i].is_real) {
1028 continue;
1031 dbuf_truncate(trackbuf, 0);
1033 ret_dcmpr = dms_read_and_decompress_track(c, d, i, trackbuf);
1035 if(!ret_dcmpr) {
1036 if(d->tracks_by_file_order[i].is_real) {
1037 real_track_failure_flag = 1;
1039 continue;
1042 if(d->tracks_by_file_order[i].is_real) {
1043 dbuf_copy(trackbuf, 0, trackbuf->len, outf);
1045 else {
1046 write_extra_track(c, d, i, trackbuf);
1050 dbuf_close(outf);
1051 dbuf_close(trackbuf);
1054 static int do_dms_header(deark *c, struct dmsctx *d, i64 pos1)
1056 i64 n;
1057 i64 pos = pos1;
1058 struct de_timestamp cr_time;
1059 int retval = 0;
1061 de_dbg(c, "header at %"I64_FMT, pos1);
1062 de_dbg_indent(c, 1);
1064 // [0..3] = signature
1065 pos = pos1+8;
1066 d->info_bits = (UI)de_getu32be_p(&pos); // [8..11] = info bits
1067 de_dbg(c, "infobits: 0x%08x", d->info_bits);
1069 de_zeromem(&cr_time, sizeof(struct de_timestamp));
1070 read_unix_timestamp(c, pos, &cr_time, "creation time");
1071 pos += 4;
1073 d->first_track = de_getu16be_p(&pos); // [16..17] = firsttrack
1074 de_dbg(c, "first track: %d", (int)d->first_track);
1075 if(d->first_track >= DMS_MAX_TRACKS) goto done;
1076 if(d->first_track != 0) {
1077 de_info(c, "Note: First track is #%d, not #0. This may be a partial disk image.",
1078 (int)d->first_track);
1081 d->last_track = de_getu16be_p(&pos); // [18..19] = lasttrack
1082 de_dbg(c, "last track: %u", (int)d->last_track);
1083 if(d->last_track < d->first_track) goto done;
1084 if(d->last_track >= DMS_MAX_TRACKS) goto done;
1086 n = de_getu32be_p(&pos); // [20..23] = packed len
1087 de_dbg(c, "compressed len: %"I64_FMT, n);
1089 n = de_getu32be_p(&pos); // [24..27] = unpacked len
1090 de_dbg(c, "decompressed len: %"I64_FMT, n);
1092 // [46..47] = creating software version
1093 pos = pos1+50;
1094 n = de_getu16be_p(&pos); // [50..51] = disk type
1095 de_dbg(c, "disk type: %u", (UI)n);
1097 d->cmpr_type = (UI)de_getu16be_p(&pos); // [52..53] = compression mode
1098 de_dbg(c, "compression type: %u (%s)", d->cmpr_type,
1099 dms_get_cmprtype_name(d->cmpr_type));
1101 n = de_getu16be_p(&pos); // [54..55] = crc
1102 de_dbg(c, "crc (reported): 0x%04x", (UI)n);
1104 retval = 1;
1106 done:
1107 de_dbg_indent(c, -1);
1108 return retval;
1111 static int dms_scan_file(deark *c, struct dmsctx *d, i64 pos1)
1113 i64 pos = pos1;
1114 i64 i;
1115 u32 next_real_tracknum_expected;
1116 int retval = 0;
1118 de_dbg(c, "scanning file");
1119 de_dbg_indent(c, 1);
1121 d->num_tracks_in_file = 0;
1123 while(1) {
1124 i64 track_num_reported;
1125 i64 cmpr_len;
1126 i64 uncmpr_len;
1127 u8 track_flags;
1128 u8 cmpr_type;
1130 if(pos+DMS_TRACK_HDR_LEN > c->infile->len) break;
1132 if(dbuf_memcmp(c->infile, pos, "TR", 2)) {
1133 de_dbg(c, "[track not found at %"I64_FMT"; assuming disk image ends here]", pos);
1134 break;
1136 if(d->num_tracks_in_file >= DMS_MAX_TRACKS) {
1137 de_err(c, "Too many tracks in file");
1138 break;
1141 track_num_reported = de_getu16be(pos+2);
1142 cmpr_len = de_getu16be(pos+6);
1143 uncmpr_len = de_getu16be(pos+10);
1144 track_flags = de_getbyte(pos+12);
1145 cmpr_type = de_getbyte(pos+13);
1147 de_dbg(c, "track[%d] at %"I64_FMT", #%d, len=%"I64_FMT"/%"I64_FMT", cmpr=%u, flags=0x%02x",
1148 (int)d->num_tracks_in_file, pos, (int)track_num_reported, cmpr_len, uncmpr_len,
1149 (UI)cmpr_type, (UI)track_flags);
1151 d->tracks_by_file_order[d->num_tracks_in_file].file_pos = pos;
1152 d->tracks_by_file_order[d->num_tracks_in_file].track_num = (u32)track_num_reported;
1154 if(track_num_reported>=d->first_track && track_num_reported<=d->last_track) {
1155 d->tracks_by_track_num[track_num_reported].order_in_file = (u32)d->num_tracks_in_file;
1156 d->tracks_by_track_num[track_num_reported].in_use = 1;
1159 d->num_tracks_in_file++;
1160 pos += DMS_TRACK_HDR_LEN + cmpr_len;
1163 // Make sure all expected tracks are present, and mark the "real" tracks in
1164 // tracks_by_file_order[].
1165 // One reason for doing it this way is that there may be two tracks numbered 0,
1166 // with the second one being the real one.
1167 for(i=d->first_track; i<=d->last_track; i++) {
1168 if(!d->tracks_by_track_num[i].in_use) {
1169 // TODO: Maybe we should write a track of all zeroes instead (but how many zeroes?)
1170 de_err(c, "Could not find track #%d", (int)i);
1171 goto done;
1174 d->tracks_by_file_order[d->tracks_by_track_num[i].order_in_file].is_real = 1;
1178 next_real_tracknum_expected = (u32)d->first_track;
1179 for(i=0; i<d->num_tracks_in_file; i++) {
1180 if(d->tracks_by_file_order[i].is_real) {
1181 // I'm not going to bother supporting out-of-order tracks, at least until
1182 // I learn that such files exist.
1183 if(d->tracks_by_file_order[i].track_num != next_real_tracknum_expected) {
1184 de_err(c, "Track numbers not in order. Not supported.");
1185 goto done;
1187 next_real_tracknum_expected = d->tracks_by_file_order[i].track_num + 1;
1191 retval = 1;
1192 done:
1193 de_dbg_indent(c, -1);
1194 return retval;
1197 static void de_run_amiga_dms(deark *c, de_module_params *mparams)
1199 struct dmsctx *d = NULL;
1201 d = de_malloc(c, sizeof(struct dmsctx));
1202 if(!do_dms_header(c, d, 0)) goto done;
1203 if(!dms_scan_file(c, d, DMS_FILE_HDR_LEN)) goto done;
1204 do_dms_main(c, d);
1206 done:
1207 if(d) {
1208 destroy_saved_dcrmpr_state(c, d);
1209 de_free(c, d);
1213 static int de_identify_amiga_dms(deark *c)
1215 i64 dcmpr_size;
1217 if(dbuf_memcmp(c->infile, 0, "DMS!", 4)) return 0;
1218 dcmpr_size = de_getu32be(24);
1219 if(dcmpr_size==901120) return 100;
1220 return 85;
1223 void de_module_amiga_dms(deark *c, struct deark_module_info *mi)
1225 mi->id = "amiga_dms";
1226 mi->desc = "Amiga DMS disk image";
1227 mi->run_fn = de_run_amiga_dms;
1228 mi->identify_fn = de_identify_amiga_dms;