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