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.
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
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
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
56 u32 crc_cmprdata_reported
;
57 u32 crc_header_reported
;
62 struct dms_tracks_by_file_order_entry
{
68 struct dms_tracks_by_track_num_entry
{
73 struct dmsheavy_cmpr_state
;
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
;
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
)
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
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
{
132 UI heavy_prev_offset
;
133 struct de_lz77buffer
*ringbuf
;
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.
142 struct de_dfilter_in_params
*dcmpri
;
143 struct de_dfilter_out_params
*dcmpro
;
144 struct de_dfilter_results
*dres
;
150 // bitrd.eof_flag: Always set if err_flag is set.
151 struct de_bitreader bitrd
;
156 struct dmslzh_params
{
157 UI cmpr_type
; // 5=heavy1, 6=heavy2
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
);
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
) {
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
)) {
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;
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
);
210 de_dfilter_set_errorf(cctx
->c
, cctx
->dres
, cctx
->modname
,
211 "Huffman decoding error");
212 lzh_set_err_flag(cctx
);
221 static int dmsheavy_read_tree(struct lzh_ctx
*cctx
, struct lzh_tree_wrapper
*htw
,
222 UI ncodes_nbits
, UI symlen_nbits
)
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
);
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
);
247 while(curr_idx
< ncodes
) {
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
);
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
;
262 lzh_set_err_flag(cctx
);
267 static void dmsheavy_discard_tree(deark
*c
, struct lzh_tree_wrapper
*htw
)
270 fmtutil_huffman_destroy_decoder(c
, htw
->ht
);
275 static void decompress_dms_heavy(struct lzh_ctx
*cctx
, struct dmslzh_params
*lzhp
,
276 struct dmsheavy_cmpr_state
*hvst
)
281 int saved_indent_level
;
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");
292 if(lzhp
->cmpr_type
==DMSCMPR_HEAVY1
) {
294 cctx
->heavy_np
= 14; // for heavy1
298 cctx
->heavy_np
= 15; // for heavy2
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");
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");
325 ret
= dmsheavy_read_tree(cctx
, &hvst
->literals_tree
, 9, 5);
326 de_dbg_indent(c
, -1);
329 de_dbg2(c
, "p tree");
331 ret
= dmsheavy_read_tree(cctx
, &hvst
->offsets_tree
, 5, 4);
332 de_dbg_indent(c
, -1);
336 de_bitreader_describe_curpos(&cctx
->bitrd
, pos_descr
, sizeof(pos_descr
));
337 de_dbg2(c
, "cmpr data codes at %s", pos_descr
);
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
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
;
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
);
386 de_dbg_indent_restore(c
, saved_indent_level
);
389 static void destroy_heavy_state(deark
*c
, struct dmsheavy_cmpr_state
*hvst
)
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";
408 cctx
->dcmpri
= dcmpri
;
409 cctx
->dcmpro
= dcmpro
;
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
;
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
;
433 // A default error message
434 de_dfilter_set_errorf(c
, dres
, cctx
->modname
, "LZH decoding error");
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;
446 if(hvst
) destroy_heavy_state(c
, hvst
);
450 /////// RLE compression ///////
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
461 DMSRLE_STATE_NEUTRAL
= 0,
464 DMSRLE_STATE_90_FF_N3
,
465 DMSRLE_STATE_90_FF_N3_N4
469 enum dmsrle_state state
;
473 static void dmsrle_codec_addbuf(struct de_dfilter_ctx
*dfctx
,
474 const u8
*buf
, i64 buf_len
)
477 struct dmsrle_ctx
*rctx
= (struct dmsrle_ctx
*)dfctx
->codec_private
;
481 for(i
=0; i
<buf_len
; i
++) {
487 switch(rctx
->state
) {
488 case DMSRLE_STATE_NEUTRAL
:
490 rctx
->state
= DMSRLE_STATE_90
;
493 dbuf_writebyte(dfctx
->dcmpro
->f
, n
);
496 case DMSRLE_STATE_90
:
498 dbuf_writebyte(dfctx
->dcmpro
->f
, 0x90);
499 rctx
->state
= DMSRLE_STATE_NEUTRAL
;
503 rctx
->state
= DMSRLE_STATE_90_N2
;
506 case DMSRLE_STATE_90_N2
:
509 rctx
->state
= DMSRLE_STATE_90_FF_N3
;
512 count
= (i64
)rctx
->n2
;
513 dbuf_write_run(dfctx
->dcmpro
->f
, n
, count
);
514 rctx
->state
= DMSRLE_STATE_NEUTRAL
;
517 case DMSRLE_STATE_90_FF_N3
:
519 rctx
->state
= DMSRLE_STATE_90_FF_N3_N4
;
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
;
532 static void dmsrle_codec_destroy(struct de_dfilter_ctx
*dfctx
)
534 struct dmsrle_ctx
*rctx
= (struct dmsrle_ctx
*)dfctx
->codec_private
;
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
;
568 struct de_dfilter_out_params
*dcmpro
;
570 struct de_bitreader bitrd
;
573 static void destroy_medium_state(deark
*c
, struct dmsmedium_cmpr_state
*mdst
)
576 de_lz77buffer_destroy(c
, mdst
->ringbuf
);
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
) {
619 static void do_mediumlz77_internal(struct medium_ctx
*mctx
, struct dmsmedium_cmpr_state
*mdst
)
624 if(mctx
->bitrd
.eof_flag
) break;
625 if(medium_have_enough_output(mctx
)) break;
627 n
= (UI
)de_bitreader_getbits(&mctx
->bitrd
, 1);
631 b
= (u8
)de_bitreader_getbits(&mctx
->bitrd
, 8);
632 de_lz77buffer_add_literal_byte(mdst
->ringbuf
, (u8
)b
);
643 // TODO: This seems overly complicated. Is there a simpler way to
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
)) {
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
));
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
;
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
;
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
));
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
;
739 tlp
.intermed_expected_len
= tri
->intermediate_len
;
740 tlp
.intermed_len_known
= 1;
741 de_dfilter_decompress_two_layer(c
, &tlp
);
744 d
->saved_medium_state
= mdparams
.medium_state
;
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
;
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
;
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
);
790 dmslzh_codectype1(c
, dcmpri
, dcmpro
, dres
, (void*)&lzhparams
);
794 d
->saved_heavy_state
= lzhparams
.heavy_state
;
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
,
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");
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
;
834 dcmpro
.len_known
= 1;
835 dcmpro
.expected_len
= tri
->uncmpr_len
;
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
);
853 de_err(c
, "[%s] Unsupported compression method: %u (%s)",
854 tri
->shortname
, tri
->cmpr_type
,
855 dms_get_cmprtype_name(tri
->cmpr_type
));
860 de_err(c
, "[%s] Decompression failed: %s", tri
->shortname
,
861 de_dfilter_get_errmsg(c
, &dres
));
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
);
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
);
882 if(tri
->is_real
&& !(tri
->track_flags
& 0x1)) {
883 destroy_saved_dcrmpr_state(c
, d
);
888 static int dms_checksum_cbfn(struct de_bufferedreadctx
*brctx
, const u8
*buf
,
891 u32
*cksum
= (u32
*)brctx
->userdata
;
894 for(i
=0; i
<buf_len
; i
++) {
895 *cksum
+= (u32
)buf
[i
];
900 // outf is presumed to be membuf containing one track, and nothing else.
901 static u32
dms_calc_checksum(deark
*c
, dbuf
*outf
)
905 dbuf_buffered_read(outf
, 0, outf
->len
, dms_checksum_cbfn
, (void*)&cksum
);
910 static void get_trackflags_descr(deark
*c
, de_ucstring
*s
, UI tflags1
, UI cmpr
)
914 if(cmpr
==5 || cmpr
==6) {
916 ucstring_append_flags_item(s
, "w/RLE");
920 ucstring_append_flags_item(s
, "track has Huffman tree defs");
925 ucstring_append_flags_item(s
, "persist decompr. state");
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
)
938 struct dms_track_info
*tri
= NULL
;
939 de_ucstring
*descr
= NULL
;
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
);
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
);
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
);
995 ucstring_destroy(descr
);
997 de_dbg_indent_restore(c
, saved_indent_level
);
1001 static void write_extra_track(deark
*c
, struct dmsctx
*d
, i64 track_idx
, dbuf
*trackbuf
)
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
)
1017 int real_track_failure_flag
= 0;
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
++) {
1027 if(real_track_failure_flag
&& d
->tracks_by_file_order
[i
].is_real
) {
1031 dbuf_truncate(trackbuf
, 0);
1033 ret_dcmpr
= dms_read_and_decompress_track(c
, d
, i
, trackbuf
);
1036 if(d
->tracks_by_file_order
[i
].is_real
) {
1037 real_track_failure_flag
= 1;
1042 if(d
->tracks_by_file_order
[i
].is_real
) {
1043 dbuf_copy(trackbuf
, 0, trackbuf
->len
, outf
);
1046 write_extra_track(c
, d
, i
, trackbuf
);
1051 dbuf_close(trackbuf
);
1054 static int do_dms_header(deark
*c
, struct dmsctx
*d
, i64 pos1
)
1058 struct de_timestamp cr_time
;
1061 de_dbg(c
, "header at %"I64_FMT
, pos1
);
1062 de_dbg_indent(c
, 1);
1064 // [0..3] = signature
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");
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
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
);
1107 de_dbg_indent(c
, -1);
1111 static int dms_scan_file(deark
*c
, struct dmsctx
*d
, i64 pos1
)
1115 u32 next_real_tracknum_expected
;
1118 de_dbg(c
, "scanning file");
1119 de_dbg_indent(c
, 1);
1121 d
->num_tracks_in_file
= 0;
1124 i64 track_num_reported
;
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
);
1136 if(d
->num_tracks_in_file
>= DMS_MAX_TRACKS
) {
1137 de_err(c
, "Too many tracks in file");
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
);
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.");
1187 next_real_tracknum_expected
= d
->tracks_by_file_order
[i
].track_num
+ 1;
1193 de_dbg_indent(c
, -1);
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
;
1208 destroy_saved_dcrmpr_state(c
, d
);
1213 static int de_identify_amiga_dms(deark
*c
)
1217 if(dbuf_memcmp(c
->infile
, 0, "DMS!", 4)) return 0;
1218 dcmpr_size
= de_getu32be(24);
1219 if(dcmpr_size
==901120) return 100;
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
;