fnt: Improved error handling, etc.
[deark.git] / modules / atari-dsk.c
blob82de7897ed170384d41329c2e0f0c4a40e98353c
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_atr_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_atr_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 dbuf_flush(outf);
391 de_dbg_indent(c, -1);
392 return retval;
395 static int do_msa_header(deark *c, struct msactx *d, i64 pos1)
397 i64 pos;
398 int retval = 0;
400 de_dbg(c, "header at %"I64_FMT, pos1);
401 de_dbg_indent(c, 1);
402 pos = pos1+2;
403 d->sectors_per_track = de_getu16be_p(&pos);
404 de_dbg(c, "sectors/track: %d", (int)d->sectors_per_track);
405 d->sides = 1 + de_getu16be_p(&pos);
406 de_dbg(c, "sides: %d", (int)d->sides);
407 d->first_track = de_getu16be_p(&pos);
408 de_dbg(c, "first track: %d", (int)d->first_track);
409 d->last_track = de_getu16be_p(&pos);
410 de_dbg(c, "last track: %d", (int)d->last_track);
412 d->num_tracks_per_side = d->last_track - d->first_track + 1;
413 if(d->sides<1 || d->sides>2) goto done;
414 if(d->sectors_per_track<1 || d->sectors_per_track>30) goto done;
415 d->num_tracks_total = d->num_tracks_per_side * d->sides;
416 if(d->num_tracks_total<1 || d->num_tracks_total>200) goto done;
417 d->track_size = d->sectors_per_track * 512;
418 d->disk_size = d->track_size * d->num_tracks_total;
419 retval = 1;
421 done:
422 if(!retval) {
423 de_err(c, "Bad or unsupported disk layout");
425 de_dbg_indent(c, -1);
426 return retval;
429 static int do_msa_tracks(deark *c, struct msactx *d, i64 pos1, dbuf *diskbuf)
431 i64 tk, sd;
432 i64 pos = pos1;
433 int retval = 0;
435 de_dbg(c, "tracks at %"I64_FMT, pos1);
436 de_dbg_indent(c, 1);
438 for(tk=d->first_track; tk<=d->last_track; tk++) {
439 for(sd=0; sd<d->sides; sd++) {
440 i64 dlen;
441 i64 tkpos = pos;
443 if(pos+2 >= c->infile->len) {
444 de_err(c, "Unexpected end of file");
445 goto after_decompress;
448 dlen = de_getu16be_p(&pos);
449 if(!do_msa_track(c, d, tk, sd, tkpos, dlen, diskbuf)) goto done;
450 pos += dlen;
453 after_decompress:
454 retval = 1;
455 done:
456 de_dbg_indent(c, -1);
457 return retval;
460 static void msa_decode_fat(deark *c, struct msactx *d, dbuf *diskbuf)
462 de_module_params *mparams = NULL;
464 mparams = de_malloc(c, sizeof(de_module_params));
465 de_dbg(c, "decoding as FAT");
466 de_dbg_indent(c, 1);
467 mparams->in_params.codes = "A";
468 mparams->in_params.input_encoding = d->input_encoding;
469 de_run_module_by_id_on_slice(c, "fat", mparams, diskbuf, 0, diskbuf->len);
470 if(mparams->out_params.flags & 0x1) {
471 de_info(c, "Note: Use \"-opt msa:toraw\" to decompress to a raw .ST file");
473 de_free(c, mparams);
474 de_dbg_indent(c, -1);
477 static void msa_extract_to_raw(deark *c, struct msactx *d, dbuf *diskbuf)
479 dbuf *outf = NULL;
481 outf = dbuf_create_output_file(c, "st", NULL, 0);
482 dbuf_copy(diskbuf, 0, d->disk_size, outf);
483 dbuf_close(outf);
486 static void msa_extract_to_uncmsa(deark *c, struct msactx *d, dbuf *diskbuf)
488 dbuf *outf = NULL;
489 i64 i;
491 outf = dbuf_create_output_file(c, "msa", NULL, 0);
492 dbuf_copy(c->infile, 0, 10, outf);
494 for(i=0; i<d->num_tracks_total; i++) {
495 dbuf_writeu16be(outf, d->track_size);
496 dbuf_copy(diskbuf, i*d->track_size, d->track_size, outf);
499 dbuf_close(outf);
502 static void de_run_msa(deark *c, de_module_params *mparams)
504 struct msactx *d = NULL;
505 dbuf *diskbuf = NULL;
507 d = de_malloc(c, sizeof(struct msactx));
508 d->input_encoding = de_get_input_encoding(c, NULL, DE_ENCODING_ATARIST);
510 if(de_get_ext_option_bool(c, "msa:touncmsa", 0)) {
511 d->outfmt = ATARIST_OUTFMT_UNCMSA;
513 else if(de_get_ext_option_bool(c, "msa:toraw", 0)) {
514 d->outfmt = ATARIST_OUTFMT_ST;
516 else {
517 d->outfmt = ATARIST_OUTFMT_FILES;
520 if(!do_msa_header(c, d, 0)) goto done;
522 diskbuf = dbuf_create_membuf(c, d->disk_size, 0x1);
523 dbuf_enable_wbuffer(diskbuf);
525 if(!do_msa_tracks(c, d, 10, diskbuf)) goto done;
527 d->total_uncmpr_bytes = diskbuf->len;
528 de_dbg(c, "totals: %u track-sides, %u compressed",
529 (UI)d->total_track_sides, (UI)d->total_track_sides_cmpr);
530 de_dbg(c, "totals: decompressed %"I64_FMT" bytes to %"I64_FMT,
531 d->total_cmpr_bytes, d->total_uncmpr_bytes);
533 if(d->outfmt==ATARIST_OUTFMT_ST) {
534 msa_extract_to_raw(c, d, diskbuf);
536 else if(d->outfmt==ATARIST_OUTFMT_UNCMSA) {
537 msa_extract_to_uncmsa(c, d, diskbuf);
539 else {
540 msa_decode_fat(c, d, diskbuf);
543 done:
544 de_free(c, d);
547 static int de_identify_msa(deark *c)
549 i64 sig;
550 int has_ext;
552 sig = de_getu16be(0);
553 if(sig != 0x0e0f) return 0;
554 has_ext = de_input_file_has_ext(c, "msa");
555 if(has_ext) return 100;
556 return 45;
559 static void de_help_msa(deark *c)
561 de_msg(c, "-opt msa:toraw : Extract to raw .ST format");
562 de_msg(c, "-opt msa:touncmsa : Convert to uncompressed MSA");
565 void de_module_msa(deark *c, struct deark_module_info *mi)
567 mi->id = "msa";
568 mi->desc = "MSA - Atari ST floppy disk image";
569 mi->run_fn = de_run_msa;
570 mi->identify_fn = de_identify_msa;
571 mi->help_fn = de_help_msa;
574 ////////////////////////////////////////////////////////
575 // Pasti / (.STX) - Atari ST disk image
577 #define PASTI_MAX_SECTORS_PER_TRACK 100
578 #define PASTI_MAX_TRACK_NUM 255
580 struct pasti_sector_ctx {
581 i64 data_offset_abs;
582 i64 size_in_bytes;
583 i64 n_track, n_head, n_secnum; // The sector's self-reported data
584 u8 fdcFlags;
585 u32 crc_reported;
588 struct pasti_track_ctx {
589 i64 track_num;
590 i64 side_num;
592 i64 fuzzy_count;
593 i64 sector_count;
595 #define TRK_SYNC 0x0080
596 #define TRK_IMAGE 0x0040
597 #define TRK_PROT 0x0020
598 #define TRK_SECT 0x0001
599 UI track_flags;
601 i64 track_length;
602 i64 sector_descriptors_pos;
603 i64 track_data_record_pos;
606 struct pastictx {
607 de_encoding input_encoding;
608 enum atarist_outfmt outfmt;
610 UI version;
611 UI revision;
612 i64 track_count;
614 i64 fat_bytes_per_sector;
615 i64 fat_sectors_per_track;
616 i64 fat_num_heads;
618 // Set if we can't convert the format to FAT, but maybe can continue decoding
619 // the Pasti structure.
620 int convert_errflag;
622 i64 num_nonempty_sectors_found;
623 i64 oob_sector_count;
625 dbuf *diskbuf;
628 static void pasti_save_sector_data(deark *c, struct pastictx *d,
629 struct pasti_sector_ctx *secctx, i64 trknum, i64 sidenum)
631 i64 dstpos;
632 i64 len_to_copy;
634 if(d->convert_errflag || secctx->size_in_bytes<=0) goto done;
635 de_dbg2(c, "[recording sector %d,%d,%d]", (int)trknum, (int)sidenum,
636 (int)secctx->n_secnum);
638 d->num_nonempty_sectors_found++;
639 if(trknum<0 || trknum>PASTI_MAX_TRACK_NUM ||
640 sidenum<0 || sidenum>=d->fat_num_heads ||
641 secctx->n_secnum<1 || secctx->n_secnum>d->fat_sectors_per_track)
643 de_dbg2(c, "[sector out of bounds]");
644 d->oob_sector_count++;
645 goto done;
648 if(!d->diskbuf) {
649 d->diskbuf = dbuf_create_membuf(c, 0, 0);
652 dstpos =
653 (d->fat_bytes_per_sector * d->fat_sectors_per_track * d->fat_num_heads) * trknum +
654 (d->fat_bytes_per_sector * d->fat_sectors_per_track) * sidenum +
655 (d->fat_bytes_per_sector) * (secctx->n_secnum-1);
657 if(secctx->size_in_bytes > d->fat_bytes_per_sector) {
658 de_warn(c, "Oversized sector");
659 len_to_copy = d->fat_bytes_per_sector;
661 else {
662 len_to_copy = secctx->size_in_bytes;
665 dbuf_copy_at(c->infile, secctx->data_offset_abs, len_to_copy, d->diskbuf, dstpos);
667 done:
671 static int do_pasti_sectors(deark *c, struct pastictx *d, struct pasti_track_ctx *tctx)
673 int saved_indent_level;
674 int retval = 0;
675 i64 secnum;
677 de_dbg_indent_save(c, &saved_indent_level);
679 de_dbg(c, "[%d sector descriptors at %"I64_FMT"]", (int)tctx->sector_count,
680 tctx->sector_descriptors_pos);
681 de_dbg_indent(c, 1);
683 for(secnum=0; secnum<tctx->sector_count; secnum++) {
684 i64 data_offset_rel;
685 u8 size_code;
686 i64 pos = tctx->sector_descriptors_pos + secnum*16;
687 struct pasti_sector_ctx secctx;
689 de_dbg2(c, "sector[%d] descriptor at %"I64_FMT, (int)secnum, pos);
690 de_dbg_indent(c, 1);
691 de_zeromem(&secctx, sizeof(struct pasti_sector_ctx));
692 data_offset_rel = de_getu32le_p(&pos);
693 secctx.data_offset_abs = tctx->track_data_record_pos + data_offset_rel;
694 de_dbg2(c, "dataOffset: %"I64_FMT" (+%"I64_FMT"=%"I64_FMT")", data_offset_rel,
695 tctx->track_data_record_pos, secctx.data_offset_abs);
696 pos += 2; // bitPosition
697 pos += 2; // readTime
699 secctx.n_track = (i64)de_getbyte_p(&pos);
700 secctx.n_head = (i64)de_getbyte_p(&pos);
701 secctx.n_secnum = (i64)de_getbyte_p(&pos);
702 de_dbg2(c, "track %d, side %d, sector %d", (int)secctx.n_track,
703 (int)secctx.n_head, (int)secctx.n_secnum);
704 // TODO: What should we do if the track and head are not the expected values?
705 // Who do we trust?
707 size_code = de_getbyte_p(&pos);
708 if(size_code>4) goto done;
709 secctx.size_in_bytes = ((i64)128)<<(UI)size_code;
710 de_dbg2(c, "size: 0x%02x (%"I64_FMT" bytes)", (UI)size_code, secctx.size_in_bytes);
712 secctx.crc_reported = (u32)de_getu16le_p(&pos);
713 de_dbg2(c, "sector crc (reported): 0x%04x", (UI)secctx.crc_reported);
714 secctx.fdcFlags = de_getbyte_p(&pos);
715 de_dbg2(c, "fdcFlags: 0x%02x", (UI)secctx.fdcFlags);
716 pos += 1; // reserved
718 pasti_save_sector_data(c, d, &secctx, tctx->track_num, tctx->side_num);
719 de_dbg_indent(c, -1);
722 retval = 1;
723 done:
724 de_dbg_indent_restore(c, saved_indent_level);
725 return retval;
728 // There's a chicken-and-egg problem, as we need information contained
729 // in the boot sector (especially the number of sectors per track),
730 // before we can effectively process the sectors.
731 // This function looks ahead to get the information. It duplicates some of
732 // the code in this module, and in the "fat" module.
733 static void pasti_find_and_read_boot_sector(deark *c, struct pastictx *d,
734 struct pasti_track_ctx *tctx)
736 i64 secnum;
737 i64 s1pos = 0; // sector 1 abs pos, 0 if unknown
739 for(secnum=0; secnum<tctx->sector_count; secnum++) {
740 i64 data_offset_rel;
741 i64 n_secnum;
742 i64 pos = tctx->sector_descriptors_pos + secnum*16;
744 n_secnum = (i64)de_getbyte(pos + 10);
745 if(n_secnum == 1) {
746 data_offset_rel = de_getu32le(pos);
747 s1pos = tctx->track_data_record_pos + data_offset_rel;
748 break;
752 if(s1pos==0) {
753 de_err(c, "Could not find boot sector");
754 d->convert_errflag = 1;
755 goto done;
758 de_dbg(c, "found boot sector; data at %"I64_FMT, s1pos);
759 de_dbg_indent(c, 1);
760 d->fat_bytes_per_sector = de_getu16le(s1pos+11);
761 de_dbg(c, "bytes per sector: %d", (int)d->fat_bytes_per_sector);
762 d->fat_sectors_per_track = de_getu16le(s1pos+24);
763 de_dbg(c, "sectors per track: %d", (int)d->fat_sectors_per_track);
764 d->fat_num_heads = de_getu16le(s1pos+26);
765 de_dbg(c, "number of heads: %d", (int)d->fat_num_heads);
767 de_dbg_indent(c, -1);
769 if(d->fat_bytes_per_sector<128 || d->fat_bytes_per_sector>2048 ||
770 d->fat_sectors_per_track<1 || d->fat_sectors_per_track>32 ||
771 d->fat_num_heads<1 || d->fat_num_heads>2)
773 de_err(c, "Invalid or unsupported disk geometry (%d sectors/track, "
774 "%d heads, %d bytes/sector)", (int)d->fat_sectors_per_track,
775 (int)d->fat_num_heads, (int)d->fat_bytes_per_sector);
776 d->convert_errflag = 1;
777 goto done;
780 done:
784 static int do_pasti_track(deark *c, struct pastictx *d, i64 trkidx, i64 pos1, i64 len)
786 i64 pos = pos1;
787 u8 track_number_raw;
788 struct pasti_track_ctx *tctx = NULL;
789 int retval = 0;
791 de_dbg(c, "track record[%d] at %"I64_FMT", len=%"I64_FMT, (int)trkidx, pos1, len);
792 de_dbg_indent(c, 1);
794 tctx = de_malloc(c, sizeof(struct pasti_track_ctx));
795 pos = pos1;
796 pos += 4; // record size, already read
798 tctx->fuzzy_count = de_getu32le_p(&pos);
799 de_dbg(c, "fuzzyCount: %"I64_FMT, tctx->fuzzy_count);
800 tctx->sector_count = de_getu16le_p(&pos);
801 de_dbg(c, "sectorCount: %"I64_FMT, tctx->sector_count);
802 if(tctx->sector_count > PASTI_MAX_SECTORS_PER_TRACK) {
803 goto done;
806 tctx->track_flags = (UI)de_getu16le_p(&pos);
807 de_dbg(c, "trackFlags: 0x%02x", tctx->track_flags);
809 tctx->track_length = de_getu16le_p(&pos);
810 de_dbg(c, "trackLength: %"I64_FMT, tctx->track_length);
812 track_number_raw = de_getbyte_p(&pos);
813 tctx->track_num = (i64)(track_number_raw & 0x7f);
814 tctx->side_num = (i64)(track_number_raw>>7);
815 de_dbg(c, "trackNumber: 0x%02x (track %u, side %u)", (UI)track_number_raw,
816 (UI)tctx->track_num, (UI)tctx->side_num);
818 pos += 1; // trackType, unused
820 if(tctx->track_flags & TRK_SECT) {
821 tctx->sector_descriptors_pos = pos;
822 pos += 16 * tctx->sector_count;
825 pos += tctx->fuzzy_count;
827 tctx->track_data_record_pos = pos;
829 if(!(tctx->track_flags & TRK_SECT)) {
830 // TODO: Support this
831 de_err(c, "Pasti files without sector descriptors are not supported");
832 goto done;
835 if(trkidx==0) {
836 // Special handling for the first track in the file
838 if(track_number_raw == 0x00) {
839 pasti_find_and_read_boot_sector(c, d, tctx);
841 else {
842 de_err(c, "First track in file is not track 0/side 0; can't convert this file");
843 d->convert_errflag = 1;
847 if(tctx->sector_count>0) {
848 if(!do_pasti_sectors(c, d, tctx)) goto done;
851 retval = 1;
852 done:
853 if(tctx) {
854 de_free(c, tctx);
856 de_dbg_indent(c, -1);
857 return retval;
860 static int do_pasti_tracks(deark *c, struct pastictx *d, i64 pos1)
862 i64 trkidx;
863 i64 pos = pos1;
864 int retval = 0;
866 for(trkidx=0; trkidx<d->track_count; trkidx++) {
867 i64 recsize;
869 if(pos+16 > c->infile->len) goto done;
870 recsize = de_getu32le(pos);
871 if(recsize<16) goto done;
872 if(!do_pasti_track(c, d, trkidx, pos, recsize)) goto done;
873 pos += recsize;
875 retval = 1;
876 done:
877 if(!retval) {
878 de_err(c, "Bad data or unexpected end of file");
880 return retval;
883 static int do_pasti_header(deark *c, struct pastictx *d, i64 pos1)
885 i64 pos = pos1;
886 int retval = 0;
888 de_dbg(c, "file descriptor at %"I64_FMT, pos1);
889 de_dbg_indent(c, 1);
890 pos += 4; // signature
891 d->version = (UI)de_getu16le_p(&pos);
892 de_dbg(c, "version: %u", d->version);
893 pos += 2; // tool
894 pos += 2; // reserved_1
895 d->track_count = (i64)de_getbyte_p(&pos);
896 de_dbg(c, "track count: %d", (int)d->track_count);
897 d->revision = (UI)de_getbyte_p(&pos);
898 de_dbg(c, "revision: %u", d->revision);
899 if(d->version!=3 || d->revision!=2) {
900 de_err(c, "Unsupported format version: %ur%u", d->version, d->revision);
901 goto done;
904 retval = 1;
905 done:
906 de_dbg_indent(c, -1);
907 return retval;
910 static void pasti_decode_fat(deark *c, struct pastictx *d)
912 de_module_params *mparams = NULL;
914 mparams = de_malloc(c, sizeof(de_module_params));
915 de_dbg(c, "decoding as FAT");
916 de_dbg_indent(c, 1);
917 mparams->in_params.codes = "A";
918 mparams->in_params.input_encoding = d->input_encoding;
919 de_run_module_by_id_on_slice(c, "fat", mparams, d->diskbuf, 0, d->diskbuf->len);
920 if(mparams->out_params.flags & 0x1) {
921 de_info(c, "Note: Use \"-opt pasti:toraw\" to decompress to a raw .ST file");
923 de_free(c, mparams);
924 de_dbg_indent(c, -1);
927 static void pasti_extract_to_raw(deark *c, struct pastictx *d)
929 dbuf *outf = NULL;
931 outf = dbuf_create_output_file(c, "st", NULL, 0);
932 dbuf_copy(d->diskbuf, 0, d->diskbuf->len, outf);
933 dbuf_close(outf);
936 static void de_run_pasti(deark *c, de_module_params *mparams)
938 struct pastictx *d = NULL;
940 d = de_malloc(c, sizeof(struct pastictx));
941 d->input_encoding = de_get_input_encoding(c, NULL, DE_ENCODING_ATARIST);
943 if(de_get_ext_option_bool(c, "pasti:toraw", 0)) {
944 d->outfmt = ATARIST_OUTFMT_ST;
946 else {
947 d->outfmt = ATARIST_OUTFMT_FILES;
950 if(!do_pasti_header(c, d, 0)) goto done;
952 if(!do_pasti_tracks(c, d, 16)) goto done;
953 if(!d->diskbuf) goto done;
955 if(d->oob_sector_count>0) {
956 de_warn(c, "%d of %d sectors are outside the bounds of the disk geometry, and will "
957 "be ignored.", (int)d->oob_sector_count, (int)d->num_nonempty_sectors_found);
960 if(d->num_nonempty_sectors_found<=0) goto done;
962 if(d->outfmt==ATARIST_OUTFMT_ST) {
963 pasti_extract_to_raw(c, d);
965 else {
966 pasti_decode_fat(c, d);
969 done:
970 if(d) {
971 dbuf_close(d->diskbuf);
972 de_free(c, d);
976 static int de_identify_pasti(deark *c)
978 if(!dbuf_memcmp(c->infile, 0, "RSY\0", 4))
979 return 100;
980 return 0;
983 static void de_help_pasti(deark *c)
985 de_msg(c, "-opt pasti:toraw : Extract to raw .ST format");
988 void de_module_pasti(deark *c, struct deark_module_info *mi)
990 mi->id = "pasti";
991 mi->desc = "Pasti - Atari ST floppy disk image";
992 mi->run_fn = de_run_pasti;
993 mi->identify_fn = de_identify_pasti;
994 mi->help_fn = de_help_pasti;