lha: Enabled lh1 compression by default
[deark.git] / modules / atari-dsk.c
blobe334edf6c6f1e999f0a5fec72cb20675c0963149
1 // This file is part of Deark.
2 // Copyright (C) 2016 Jason Summers
3 // See the file COPYING for terms of use.
5 #include <deark-private.h>
6 DE_DECLARE_MODULE(de_module_atari_cas);
7 DE_DECLARE_MODULE(de_module_atr);
8 DE_DECLARE_MODULE(de_module_msa);
9 DE_DECLARE_MODULE(de_module_pasti);
11 typedef struct localctx_struct {
12 i64 sector_size;
13 } lctx;
15 static void do_cas(deark *c)
17 i64 pos;
18 u8 chunk_id[4];
19 i64 chunk_len;
20 i64 chunk_extra;
22 pos = 0;
23 while(1) {
24 if(pos >= c->infile->len-8) break; // Reached end of file
26 de_read(chunk_id, pos, 4);
27 chunk_len = de_getu16le(pos+4);
28 chunk_extra = de_getu16le(pos+6);
30 de_dbg(c, "chunk at %d, data_len=%d, extra=%d", (int)pos, (int)chunk_len,
31 (int)chunk_extra);
33 pos += 8;
35 pos += chunk_len;
39 static void de_run_cas(deark *c, de_module_params *mparams)
41 do_cas(c);
42 de_err(c, "Atari CAS format is not supported");
45 static int de_identify_cas(deark *c)
47 u8 buf[16];
48 de_read(buf, 0, 16);
50 if(!de_memcmp(buf, "FUJI", 4)) {
51 // Note - Make sure Fujifilm RAF has higher confidence.
52 return 70;
54 return 0;
57 void de_module_atari_cas(deark *c, struct deark_module_info *mi)
59 mi->id = "cas";
60 mi->desc = "Atari CAS tape image";
61 mi->run_fn = de_run_cas;
62 mi->identify_fn = de_identify_cas;
63 mi->flags |= DE_MODFLAG_NONWORKING;
66 // --------------------------------------------
68 static int get_sector_offset_and_size(deark *c, lctx *d,
69 i64 sector_num, i64 *sector_offset, i64 *sector_size)
71 if(sector_num<1) return 0;
73 *sector_size = d->sector_size;
74 *sector_offset = (sector_num-1) * d->sector_size;
76 if(d->sector_size==256) {
77 // The first 3 sectors are 128 bytes
78 if(sector_num<=3) {
79 *sector_size = 128;
80 *sector_offset = (sector_num-1) * 128;
82 else {
83 *sector_offset -= 3*128;
87 return 1;
90 static void do_extract_file_contents(deark *c, lctx *d, dbuf *inf, dbuf *outf,
91 i64 starting_sector, i64 sector_count)
93 i64 sectors_extracted = 0;
94 i64 sector_pos = 0;
95 i64 sector_size = 0;
96 i64 cur_sector;
97 i64 next_sector;
98 u8 mdata[3];
99 i64 nbytes;
101 cur_sector = starting_sector;
102 while(sectors_extracted < sector_count) {
103 get_sector_offset_and_size(c, d, cur_sector, &sector_pos, &sector_size);
104 de_dbg(c, "sector %d, #%d, at %d", (int)sectors_extracted, (int)cur_sector, (int)sector_pos);
105 de_dbg_indent(c, 1);
107 dbuf_read(inf, mdata, sector_pos + sector_size-3, 3);
108 next_sector = ((mdata[0]&0x3) << 8) | mdata[1];
110 // TODO: Some documentation says the high bit of mdata[2] is a
111 // "short flag" that indicates that the other bits are valid. But I haven't
112 // found any files that use it. And it can't work with sectors > 128 bytes.
113 nbytes = (i64)mdata[2];
114 if(sector_size<=128)
115 nbytes = nbytes & 0x7f;
117 de_dbg(c, "byte count: %d, next sector: %d", (int)nbytes, (int)next_sector);
119 dbuf_copy(inf, sector_pos, nbytes, outf);
121 de_dbg_indent(c, -1);
122 sectors_extracted++;
123 cur_sector = next_sector;
124 if(next_sector<1) break;
128 static void do_directory_entry(deark *c, lctx *d, dbuf *f, i64 pos)
130 u8 flags;
131 i64 sector_count;
132 i64 starting_sector;
133 de_ucstring *fn_u = NULL;
134 de_ucstring *fn_ext = NULL;
135 de_finfo *fi = NULL;
136 dbuf *outf = NULL;
137 de_ext_encoding fn_encoding;
139 flags = dbuf_getbyte(f, pos);
140 de_dbg(c, "flags: 0x%02x", (unsigned int)flags);
141 if((flags&0x40)==0) {
142 // Unused or deleted directory entry
143 return;
146 sector_count = dbuf_getu16le(f, pos+1);
147 starting_sector = dbuf_getu16le(f, pos+3);
148 de_dbg(c, "sector start: %d, count: %d", (int)starting_sector, (int)sector_count);
150 if(starting_sector<1) goto done;
152 if(starting_sector > 720) {
153 de_err(c, "Bad starting sector: %d", (int)starting_sector);
154 goto done;
156 if(sector_count > 720) {
157 de_err(c, "Bad file size: %d blocks", (int)sector_count);
158 goto done;
161 fn_u = ucstring_create(c);
162 fn_ext = ucstring_create(c);
164 // TODO: Use correct Atari encoding.
165 fn_encoding = DE_EXTENC_MAKE(DE_ENCODING_ASCII, DE_ENCSUBTYPE_PRINTABLE);
166 dbuf_read_to_ucstring(f, pos+5, 8, fn_u, 0,fn_encoding);
167 dbuf_read_to_ucstring(f, pos+13, 3, fn_ext, 0, fn_encoding);
168 de_dbg(c, "filename: \"%s.%s\"",
169 ucstring_getpsz(fn_u), ucstring_getpsz(fn_ext));
170 ucstring_strip_trailing_spaces(fn_u);
171 ucstring_strip_trailing_spaces(fn_ext);
172 if(fn_u->len==0) {
173 ucstring_append_char(fn_u, '_');
175 if(ucstring_isnonempty(fn_ext)) {
176 ucstring_append_char(fn_u, '.');
177 ucstring_append_ucstring(fn_u, fn_ext);
180 fi = de_finfo_create(c);
181 de_finfo_set_name_from_ucstring(c, fi, fn_u, 0);
182 fi->original_filename_flag = 1;
184 outf = dbuf_create_output_file(c, NULL, fi, 0);
186 do_extract_file_contents(c, d, f, outf, starting_sector, sector_count);
188 done:
189 de_finfo_destroy(c, fi);
190 ucstring_destroy(fn_u);
191 ucstring_destroy(fn_ext);
192 dbuf_close(outf);
195 static void do_disk_image(deark *c, lctx *d, dbuf *f)
197 i64 sector_pos;
198 i64 entrypos;
199 i64 sector_size;
200 i64 sector_index;
201 i64 entry_index;
202 i64 entries_per_sector;
203 u8 flags;
205 if(d->sector_size!=128 && d->sector_size!=256) {
206 de_err(c, "Unsupported sector size: %d", (int)d->sector_size);
207 return;
209 entries_per_sector = d->sector_size / 16;
211 for(sector_index=0; sector_index<8; sector_index++) {
212 get_sector_offset_and_size(c, d, 361+sector_index, &sector_pos, &sector_size);
213 if(sector_pos + sector_size > f->len) break;
214 de_dbg(c, "directory sector %d at %d", (int)sector_index, (int)sector_pos);
215 de_dbg_indent(c, 1);
216 for(entry_index=0; entry_index<entries_per_sector; entry_index++) {
217 entrypos = sector_pos + 16*entry_index;
219 // Peek at the flags byte, just to avoid printing debugging info
220 // about nonexistent files
221 flags = dbuf_getbyte(f, entrypos);
222 if(flags==0x00) continue;
224 de_dbg(c, "directory sector %d entry %d at %d", (int)sector_index,
225 (int)entry_index, (int)entrypos);
226 de_dbg_indent(c, 1);
227 do_directory_entry(c, d, f, entrypos);
228 de_dbg_indent(c, -1);
230 de_dbg_indent(c, -1);
234 static void do_atr(deark *c, lctx *d)
236 i64 pos;
237 i64 image_size_hi, image_size_lo; // In 16-byte "paragraphs"
238 i64 image_size_bytes;
239 dbuf *diskimg = NULL;
241 pos = 0;
243 image_size_lo = de_getu16le(pos+2);
244 d->sector_size = de_getu16le(pos+4);
245 image_size_hi = de_getu16le(pos+6);
246 image_size_bytes = 16*(image_size_lo + 65536*image_size_hi);
248 de_dbg(c, "image size=%d bytes, sector size=%d", (int)image_size_bytes, (int)d->sector_size);
250 diskimg = dbuf_open_input_subfile(c->infile, 16, c->infile->len-16);
252 do_disk_image(c, d, diskimg);
254 dbuf_close(diskimg);
257 static void de_run_atr(deark *c, de_module_params *mparams)
259 lctx *d = NULL;
261 d = de_malloc(c, sizeof(lctx));
263 do_atr(c, d);
265 de_free(c, d);
268 static int de_identify_atr(deark *c)
270 u8 buf[16];
271 de_read(buf, 0, 16);
273 if(buf[0]==0x96 && buf[1]==0x02) {
274 return 60;
276 return 0;
279 void de_module_atr(deark *c, struct deark_module_info *mi)
281 mi->id = "atr";
282 mi->desc = "ATR Atari floppy disk image";
283 mi->run_fn = de_run_atr;
284 mi->identify_fn = de_identify_atr;
287 ////////////////////////////////////////////////////////
288 // MSA - Magic Shadow Archiver - Atari ST disk image
290 enum atarist_outfmt {
291 ATARIST_OUTFMT_FILES = 0,
292 ATARIST_OUTFMT_ST,
293 ATARIST_OUTFMT_UNCMSA
296 struct msactx {
297 de_encoding input_encoding;
298 enum atarist_outfmt outfmt;
299 i64 sectors_per_track;
300 i64 sides;
301 i64 first_track;
302 i64 last_track;
303 i64 num_tracks_per_side;
304 i64 num_tracks_total;
305 i64 track_size; // bytes per track per side
306 i64 disk_size;
307 i64 total_track_sides;
308 i64 total_track_sides_cmpr;
309 i64 total_cmpr_bytes;
310 i64 total_uncmpr_bytes;
313 // Decompress one track
314 static int msa_decompress_rle(deark *c, struct msactx *d, i64 pos1, i64 dlen,
315 dbuf *outf)
317 i64 endpos = pos1+dlen;
318 i64 pos = pos1;
319 i64 outcount = 0;
320 int retval = 0;
322 while(1) {
323 u8 b;
324 i64 count;
326 if(outcount >= d->track_size) {
327 retval = 1;
328 goto done; // Have enough output
331 if(pos >= endpos) {
332 goto done;
335 b = de_getbyte_p(&pos);
336 if(b != 0xe5) {
337 dbuf_writebyte(outf, b);
338 outcount++;
339 continue;
342 if(pos+3 > endpos) {
343 goto done;
345 b = de_getbyte_p(&pos);
346 count = de_getu16be_p(&pos);
347 if(outcount+count > d->track_size) {
348 count = d->track_size - outcount;
350 dbuf_write_run(outf, b, count);
351 outcount += count;
354 done:
355 if(!retval) {
356 de_err(c, "Decompression failed");
358 return retval;
361 static int do_msa_track(deark *c, struct msactx *d, i64 tk, i64 sd, i64 pos1, i64 dlen, dbuf *outf)
363 int is_compressed;
364 int retval = 0;
365 i64 outf_startsize = outf->len;
367 de_dbg2(c, "track (t=%d, s=%d) at %"I64_FMT", dlen=%"I64_FMT, (int)tk, (int)sd, pos1, dlen);
368 de_dbg_indent(c, 1);
369 if(dlen > d->track_size) {
370 de_err(c, "Invalid compressed track size");
371 goto done;
373 is_compressed = (dlen!=d->track_size);
374 de_dbg2(c, "compressed: %d", is_compressed);
376 if(is_compressed) {
377 if(!msa_decompress_rle(c, d, pos1+2, dlen, outf)) goto done;
378 d->total_track_sides_cmpr++;
380 else {
381 dbuf_copy(c->infile, pos1+2, dlen, outf);
383 d->total_cmpr_bytes += dlen;
384 d->total_track_sides++;
386 dbuf_truncate(outf, outf_startsize + d->track_size);
387 retval = 1;
389 done:
390 de_dbg_indent(c, -1);
391 return retval;
394 static int do_msa_header(deark *c, struct msactx *d, i64 pos1)
396 i64 pos;
397 int retval = 0;
399 de_dbg(c, "header at %"I64_FMT, pos1);
400 de_dbg_indent(c, 1);
401 pos = pos1+2;
402 d->sectors_per_track = de_getu16be_p(&pos);
403 de_dbg(c, "sectors/track: %d", (int)d->sectors_per_track);
404 d->sides = 1 + de_getu16be_p(&pos);
405 de_dbg(c, "sides: %d", (int)d->sides);
406 d->first_track = de_getu16be_p(&pos);
407 de_dbg(c, "first track: %d", (int)d->first_track);
408 d->last_track = de_getu16be_p(&pos);
409 de_dbg(c, "last track: %d", (int)d->last_track);
411 d->num_tracks_per_side = d->last_track - d->first_track + 1;
412 if(d->sides<1 || d->sides>2) goto done;
413 if(d->sectors_per_track<1 || d->sectors_per_track>30) goto done;
414 d->num_tracks_total = d->num_tracks_per_side * d->sides;
415 if(d->num_tracks_total<1 || d->num_tracks_total>200) goto done;
416 d->track_size = d->sectors_per_track * 512;
417 d->disk_size = d->track_size * d->num_tracks_total;
418 retval = 1;
420 done:
421 if(!retval) {
422 de_err(c, "Bad or unsupported disk layout");
424 de_dbg_indent(c, -1);
425 return retval;
428 static int do_msa_tracks(deark *c, struct msactx *d, i64 pos1, dbuf *diskbuf)
430 i64 tk, sd;
431 i64 pos = pos1;
432 int retval = 0;
434 de_dbg(c, "tracks at %"I64_FMT, pos1);
435 de_dbg_indent(c, 1);
437 for(tk=d->first_track; tk<=d->last_track; tk++) {
438 for(sd=0; sd<d->sides; sd++) {
439 i64 dlen;
440 i64 tkpos = pos;
442 if(pos+2 >= c->infile->len) {
443 de_err(c, "Unexpected end of file");
444 goto after_decompress;
447 dlen = de_getu16be_p(&pos);
448 if(!do_msa_track(c, d, tk, sd, tkpos, dlen, diskbuf)) goto done;
449 pos += dlen;
452 after_decompress:
453 retval = 1;
454 done:
455 de_dbg_indent(c, -1);
456 return retval;
459 static void msa_decode_fat(deark *c, struct msactx *d, dbuf *diskbuf)
461 de_module_params *mparams = NULL;
463 mparams = de_malloc(c, sizeof(de_module_params));
464 de_dbg(c, "decoding as FAT");
465 de_dbg_indent(c, 1);
466 mparams->in_params.codes = "A";
467 mparams->in_params.input_encoding = d->input_encoding;
468 de_run_module_by_id_on_slice(c, "fat", mparams, diskbuf, 0, diskbuf->len);
469 if(mparams->out_params.flags & 0x1) {
470 de_info(c, "Note: Use \"-opt msa:toraw\" to decompress to a raw .ST file");
472 de_free(c, mparams);
473 de_dbg_indent(c, -1);
476 static void msa_extract_to_raw(deark *c, struct msactx *d, dbuf *diskbuf)
478 dbuf *outf = NULL;
480 outf = dbuf_create_output_file(c, "st", NULL, 0);
481 dbuf_copy(diskbuf, 0, d->disk_size, outf);
482 dbuf_close(outf);
485 static void msa_extract_to_uncmsa(deark *c, struct msactx *d, dbuf *diskbuf)
487 dbuf *outf = NULL;
488 i64 i;
490 outf = dbuf_create_output_file(c, "msa", NULL, 0);
491 dbuf_copy(c->infile, 0, 10, outf);
493 for(i=0; i<d->num_tracks_total; i++) {
494 dbuf_writeu16be(outf, d->track_size);
495 dbuf_copy(diskbuf, i*d->track_size, d->track_size, outf);
498 dbuf_close(outf);
501 static void de_run_msa(deark *c, de_module_params *mparams)
503 struct msactx *d = NULL;
504 dbuf *diskbuf = NULL;
506 d = de_malloc(c, sizeof(struct msactx));
507 d->input_encoding = de_get_input_encoding(c, NULL, DE_ENCODING_ATARIST);
509 if(de_get_ext_option_bool(c, "msa:touncmsa", 0)) {
510 d->outfmt = ATARIST_OUTFMT_UNCMSA;
512 else if(de_get_ext_option_bool(c, "msa:toraw", 0)) {
513 d->outfmt = ATARIST_OUTFMT_ST;
515 else {
516 d->outfmt = ATARIST_OUTFMT_FILES;
519 if(!do_msa_header(c, d, 0)) goto done;
521 diskbuf = dbuf_create_membuf(c, d->disk_size, 0x1);
523 if(!do_msa_tracks(c, d, 10, diskbuf)) goto done;
525 d->total_uncmpr_bytes = diskbuf->len;
526 de_dbg(c, "totals: %u track-sides, %u compressed",
527 (UI)d->total_track_sides, (UI)d->total_track_sides_cmpr);
528 de_dbg(c, "totals: decompressed %"I64_FMT" bytes to %"I64_FMT,
529 d->total_cmpr_bytes, d->total_uncmpr_bytes);
531 if(d->outfmt==ATARIST_OUTFMT_ST) {
532 msa_extract_to_raw(c, d, diskbuf);
534 else if(d->outfmt==ATARIST_OUTFMT_UNCMSA) {
535 msa_extract_to_uncmsa(c, d, diskbuf);
537 else {
538 msa_decode_fat(c, d, diskbuf);
541 done:
542 de_free(c, d);
545 static int de_identify_msa(deark *c)
547 i64 sig;
548 int has_ext;
550 sig = de_getu16be(0);
551 if(sig != 0x0e0f) return 0;
552 has_ext = de_input_file_has_ext(c, "msa");
553 if(has_ext) return 100;
554 return 45;
557 static void de_help_msa(deark *c)
559 de_msg(c, "-opt msa:toraw : Extract to raw .ST format");
560 de_msg(c, "-opt msa:touncmsa : Convert to uncompressed MSA");
563 void de_module_msa(deark *c, struct deark_module_info *mi)
565 mi->id = "msa";
566 mi->desc = "MSA - Atari ST floppy disk image";
567 mi->run_fn = de_run_msa;
568 mi->identify_fn = de_identify_msa;
569 mi->help_fn = de_help_msa;
572 ////////////////////////////////////////////////////////
573 // Pasti / (.STX) - Atari ST disk image
575 #define PASTI_MAX_SECTORS_PER_TRACK 100
576 #define PASTI_MAX_TRACK_NUM 255
578 struct pasti_sector_ctx {
579 i64 data_offset_abs;
580 i64 size_in_bytes;
581 i64 n_track, n_head, n_secnum; // The sector's self-reported data
582 u8 fdcFlags;
583 u32 crc_reported;
586 struct pasti_track_ctx {
587 i64 track_num;
588 i64 side_num;
590 i64 fuzzy_count;
591 i64 sector_count;
593 #define TRK_SYNC 0x0080
594 #define TRK_IMAGE 0x0040
595 #define TRK_PROT 0x0020
596 #define TRK_SECT 0x0001
597 UI track_flags;
599 i64 track_length;
600 i64 sector_descriptors_pos;
601 i64 track_data_record_pos;
604 struct pastictx {
605 de_encoding input_encoding;
606 enum atarist_outfmt outfmt;
608 UI version;
609 UI revision;
610 i64 track_count;
612 i64 fat_bytes_per_sector;
613 i64 fat_sectors_per_track;
614 i64 fat_num_heads;
616 // Set if we can't convert the format to FAT, but maybe can continue decoding
617 // the Pasti structure.
618 int convert_errflag;
620 i64 num_nonempty_sectors_found;
621 i64 oob_sector_count;
623 dbuf *diskbuf;
626 static void pasti_save_sector_data(deark *c, struct pastictx *d,
627 struct pasti_sector_ctx *secctx, i64 trknum, i64 sidenum)
629 i64 dstpos;
630 i64 len_to_copy;
632 if(d->convert_errflag || secctx->size_in_bytes<=0) goto done;
633 de_dbg2(c, "[recording sector %d,%d,%d]", (int)trknum, (int)sidenum,
634 (int)secctx->n_secnum);
636 d->num_nonempty_sectors_found++;
637 if(trknum<0 || trknum>PASTI_MAX_TRACK_NUM ||
638 sidenum<0 || sidenum>=d->fat_num_heads ||
639 secctx->n_secnum<1 || secctx->n_secnum>d->fat_sectors_per_track)
641 de_dbg2(c, "[sector out of bounds]");
642 d->oob_sector_count++;
643 goto done;
646 if(!d->diskbuf) {
647 d->diskbuf = dbuf_create_membuf(c, 0, 0);
650 dstpos =
651 (d->fat_bytes_per_sector * d->fat_sectors_per_track * d->fat_num_heads) * trknum +
652 (d->fat_bytes_per_sector * d->fat_sectors_per_track) * sidenum +
653 (d->fat_bytes_per_sector) * (secctx->n_secnum-1);
655 if(secctx->size_in_bytes > d->fat_bytes_per_sector) {
656 de_warn(c, "Oversized sector");
657 len_to_copy = d->fat_bytes_per_sector;
659 else {
660 len_to_copy = secctx->size_in_bytes;
663 dbuf_copy_at(c->infile, secctx->data_offset_abs, len_to_copy, d->diskbuf, dstpos);
665 done:
669 static int do_pasti_sectors(deark *c, struct pastictx *d, struct pasti_track_ctx *tctx)
671 int saved_indent_level;
672 int retval = 0;
673 i64 secnum;
675 de_dbg_indent_save(c, &saved_indent_level);
677 de_dbg(c, "[%d sector descriptors at %"I64_FMT"]", (int)tctx->sector_count,
678 tctx->sector_descriptors_pos);
679 de_dbg_indent(c, 1);
681 for(secnum=0; secnum<tctx->sector_count; secnum++) {
682 i64 data_offset_rel;
683 u8 size_code;
684 i64 pos = tctx->sector_descriptors_pos + secnum*16;
685 struct pasti_sector_ctx secctx;
687 de_dbg2(c, "sector[%d] descriptor at %"I64_FMT, (int)secnum, pos);
688 de_dbg_indent(c, 1);
689 de_zeromem(&secctx, sizeof(struct pasti_sector_ctx));
690 data_offset_rel = de_getu32le_p(&pos);
691 secctx.data_offset_abs = tctx->track_data_record_pos + data_offset_rel;
692 de_dbg2(c, "dataOffset: %"I64_FMT" (+%"I64_FMT"=%"I64_FMT")", data_offset_rel,
693 tctx->track_data_record_pos, secctx.data_offset_abs);
694 pos += 2; // bitPosition
695 pos += 2; // readTime
697 secctx.n_track = (i64)de_getbyte_p(&pos);
698 secctx.n_head = (i64)de_getbyte_p(&pos);
699 secctx.n_secnum = (i64)de_getbyte_p(&pos);
700 de_dbg2(c, "track %d, side %d, sector %d", (int)secctx.n_track,
701 (int)secctx.n_head, (int)secctx.n_secnum);
702 // TODO: What should we do if the track and head are not the expected values?
703 // Who do we trust?
705 size_code = de_getbyte_p(&pos);
706 if(size_code>4) goto done;
707 secctx.size_in_bytes = ((i64)128)<<(UI)size_code;
708 de_dbg2(c, "size: 0x%02x (%"I64_FMT" bytes)", (UI)size_code, secctx.size_in_bytes);
710 secctx.crc_reported = (u32)de_getu16le_p(&pos);
711 de_dbg2(c, "sector crc (reported): 0x%04x", (UI)secctx.crc_reported);
712 secctx.fdcFlags = de_getbyte_p(&pos);
713 de_dbg2(c, "fdcFlags: 0x%02x", (UI)secctx.fdcFlags);
714 pos += 1; // reserved
716 pasti_save_sector_data(c, d, &secctx, tctx->track_num, tctx->side_num);
717 de_dbg_indent(c, -1);
720 retval = 1;
721 done:
722 de_dbg_indent_restore(c, saved_indent_level);
723 return retval;
726 // There's a chicken-and-egg problem, as we need information contained
727 // in the boot sector (especially the number of sectors per track),
728 // before we can effectively process the sectors.
729 // This function looks ahead to get the information. It duplicates some of
730 // the code in this module, and in the "fat" module.
731 static void pasti_find_and_read_boot_sector(deark *c, struct pastictx *d,
732 struct pasti_track_ctx *tctx)
734 i64 secnum;
735 i64 s1pos = 0; // sector 1 abs pos, 0 if unknown
737 for(secnum=0; secnum<tctx->sector_count; secnum++) {
738 i64 data_offset_rel;
739 i64 n_secnum;
740 i64 pos = tctx->sector_descriptors_pos + secnum*16;
742 n_secnum = (i64)de_getbyte(pos + 10);
743 if(n_secnum == 1) {
744 data_offset_rel = de_getu32le(pos);
745 s1pos = tctx->track_data_record_pos + data_offset_rel;
746 break;
750 if(s1pos==0) {
751 de_err(c, "Could not find boot sector");
752 d->convert_errflag = 1;
753 goto done;
756 de_dbg(c, "found boot sector; data at %"I64_FMT, s1pos);
757 de_dbg_indent(c, 1);
758 d->fat_bytes_per_sector = de_getu16le(s1pos+11);
759 de_dbg(c, "bytes per sector: %d", (int)d->fat_bytes_per_sector);
760 d->fat_sectors_per_track = de_getu16le(s1pos+24);
761 de_dbg(c, "sectors per track: %d", (int)d->fat_sectors_per_track);
762 d->fat_num_heads = de_getu16le(s1pos+26);
763 de_dbg(c, "number of heads: %d", (int)d->fat_num_heads);
765 de_dbg_indent(c, -1);
767 if(d->fat_bytes_per_sector<128 || d->fat_bytes_per_sector>2048 ||
768 d->fat_sectors_per_track<1 || d->fat_sectors_per_track>32 ||
769 d->fat_num_heads<1 || d->fat_num_heads>2)
771 de_err(c, "Invalid or unsupported disk geometry (%d sectors/track, "
772 "%d heads, %d bytes/sector)", (int)d->fat_sectors_per_track,
773 (int)d->fat_num_heads, (int)d->fat_bytes_per_sector);
774 d->convert_errflag = 1;
775 goto done;
778 done:
782 static int do_pasti_track(deark *c, struct pastictx *d, i64 trkidx, i64 pos1, i64 len)
784 i64 pos = pos1;
785 u8 track_number_raw;
786 struct pasti_track_ctx *tctx = NULL;
787 int retval = 0;
789 de_dbg(c, "track record[%d] at %"I64_FMT", len=%"I64_FMT, (int)trkidx, pos1, len);
790 de_dbg_indent(c, 1);
792 tctx = de_malloc(c, sizeof(struct pasti_track_ctx));
793 pos = pos1;
794 pos += 4; // record size, already read
796 tctx->fuzzy_count = de_getu32le_p(&pos);
797 de_dbg(c, "fuzzyCount: %"I64_FMT, tctx->fuzzy_count);
798 tctx->sector_count = de_getu16le_p(&pos);
799 de_dbg(c, "sectorCount: %"I64_FMT, tctx->sector_count);
800 if(tctx->sector_count > PASTI_MAX_SECTORS_PER_TRACK) {
801 goto done;
804 tctx->track_flags = (UI)de_getu16le_p(&pos);
805 de_dbg(c, "trackFlags: 0x%02x", tctx->track_flags);
807 tctx->track_length = de_getu16le_p(&pos);
808 de_dbg(c, "trackLength: %"I64_FMT, tctx->track_length);
810 track_number_raw = de_getbyte_p(&pos);
811 tctx->track_num = (i64)(track_number_raw & 0x7f);
812 tctx->side_num = (i64)(track_number_raw>>7);
813 de_dbg(c, "trackNumber: 0x%02x (track %u, side %u)", (UI)track_number_raw,
814 (UI)tctx->track_num, (UI)tctx->side_num);
816 pos += 1; // trackType, unused
818 if(tctx->track_flags & TRK_SECT) {
819 tctx->sector_descriptors_pos = pos;
820 pos += 16 * tctx->sector_count;
823 pos += tctx->fuzzy_count;
825 tctx->track_data_record_pos = pos;
827 if(!(tctx->track_flags & TRK_SECT)) {
828 // TODO: Support this
829 de_err(c, "Pasti files without sector descriptors are not supported");
830 goto done;
833 if(trkidx==0) {
834 // Special handling for the first track in the file
836 if(track_number_raw == 0x00) {
837 pasti_find_and_read_boot_sector(c, d, tctx);
839 else {
840 de_err(c, "First track in file is not track 0/side 0; can't convert this file");
841 d->convert_errflag = 1;
845 if(tctx->sector_count>0) {
846 if(!do_pasti_sectors(c, d, tctx)) goto done;
849 retval = 1;
850 done:
851 if(tctx) {
852 de_free(c, tctx);
854 de_dbg_indent(c, -1);
855 return retval;
858 static int do_pasti_tracks(deark *c, struct pastictx *d, i64 pos1)
860 i64 trkidx;
861 i64 pos = pos1;
862 int retval = 0;
864 for(trkidx=0; trkidx<d->track_count; trkidx++) {
865 i64 recsize;
867 if(pos+16 > c->infile->len) goto done;
868 recsize = de_getu32le(pos);
869 if(recsize<16) goto done;
870 if(!do_pasti_track(c, d, trkidx, pos, recsize)) goto done;
871 pos += recsize;
873 retval = 1;
874 done:
875 if(!retval) {
876 de_err(c, "Bad data or unexpected end of file");
878 return retval;
881 static int do_pasti_header(deark *c, struct pastictx *d, i64 pos1)
883 i64 pos = pos1;
884 int retval = 0;
886 de_dbg(c, "file descriptor at %"I64_FMT, pos1);
887 de_dbg_indent(c, 1);
888 pos += 4; // signature
889 d->version = (UI)de_getu16le_p(&pos);
890 de_dbg(c, "version: %u", d->version);
891 pos += 2; // tool
892 pos += 2; // reserved_1
893 d->track_count = (i64)de_getbyte_p(&pos);
894 de_dbg(c, "track count: %d", (int)d->track_count);
895 d->revision = (UI)de_getbyte_p(&pos);
896 de_dbg(c, "revision: %u", d->revision);
897 if(d->version!=3 || d->revision!=2) {
898 de_err(c, "Unsupported format version: %ur%u", d->version, d->revision);
899 goto done;
902 retval = 1;
903 done:
904 de_dbg_indent(c, -1);
905 return retval;
908 static void pasti_decode_fat(deark *c, struct pastictx *d)
910 de_module_params *mparams = NULL;
912 mparams = de_malloc(c, sizeof(de_module_params));
913 de_dbg(c, "decoding as FAT");
914 de_dbg_indent(c, 1);
915 mparams->in_params.codes = "A";
916 mparams->in_params.input_encoding = d->input_encoding;
917 de_run_module_by_id_on_slice(c, "fat", mparams, d->diskbuf, 0, d->diskbuf->len);
918 if(mparams->out_params.flags & 0x1) {
919 de_info(c, "Note: Use \"-opt pasti:toraw\" to decompress to a raw .ST file");
921 de_free(c, mparams);
922 de_dbg_indent(c, -1);
925 static void pasti_extract_to_raw(deark *c, struct pastictx *d)
927 dbuf *outf = NULL;
929 outf = dbuf_create_output_file(c, "st", NULL, 0);
930 dbuf_copy(d->diskbuf, 0, d->diskbuf->len, outf);
931 dbuf_close(outf);
934 static void de_run_pasti(deark *c, de_module_params *mparams)
936 struct pastictx *d = NULL;
938 d = de_malloc(c, sizeof(struct pastictx));
939 d->input_encoding = de_get_input_encoding(c, NULL, DE_ENCODING_ATARIST);
941 if(de_get_ext_option_bool(c, "pasti:toraw", 0)) {
942 d->outfmt = ATARIST_OUTFMT_ST;
944 else {
945 d->outfmt = ATARIST_OUTFMT_FILES;
948 if(!do_pasti_header(c, d, 0)) goto done;
950 if(!do_pasti_tracks(c, d, 16)) goto done;
951 if(!d->diskbuf) goto done;
953 if(d->oob_sector_count>0) {
954 de_warn(c, "%d of %d sectors are outside the bounds of the disk geometry, and will "
955 "be ignored.", (int)d->oob_sector_count, (int)d->num_nonempty_sectors_found);
958 if(d->num_nonempty_sectors_found<=0) goto done;
960 if(d->outfmt==ATARIST_OUTFMT_ST) {
961 pasti_extract_to_raw(c, d);
963 else {
964 pasti_decode_fat(c, d);
967 done:
968 if(d) {
969 dbuf_close(d->diskbuf);
970 de_free(c, d);
974 static int de_identify_pasti(deark *c)
976 if(!dbuf_memcmp(c->infile, 0, "RSY\0", 4))
977 return 100;
978 return 0;
981 static void de_help_pasti(deark *c)
983 de_msg(c, "-opt pasti:toraw : Extract to raw .ST format");
986 void de_module_pasti(deark *c, struct deark_module_info *mi)
988 mi->id = "pasti";
989 mi->desc = "Pasti - Atari ST floppy disk image";
990 mi->run_fn = de_run_pasti;
991 mi->identify_fn = de_identify_pasti;
992 mi->help_fn = de_help_pasti;