iccprofile: Decode 'dict' data type
[deark.git] / modules / amiga-dsk.c
bloba13b6e2652bdb47b1ec3d775c3cf2f20071fb33f
1 // This file is part of Deark.
2 // Copyright (C) 2020 Jason Summers
3 // See the file COPYING for terms of use.
5 // Amiga disk image (ADF)
7 #include <deark-private.h>
8 DE_DECLARE_MODULE(de_module_amiga_adf);
10 #define ADF_T_HEADER 2
11 #define ADF_T_DATA 8
12 #define ADF_T_LIST 16
14 #define ADF_ST_ROOT 1
15 #define ADF_ST_USERDIR 2
16 #define ADF_ST_FILE (-3)
18 #define MAX_ADF_BLOCKS 3520
19 #define MAX_NESTING_LEVEL 16
21 struct block_ptrs_tbl {
22 i64 high_seq;
23 i64 blocks_tbl_capacity;
24 i64 *blocks_tbl; // array[blocks_tbl_capacity]
27 struct member_data {
28 i64 header_blknum;
29 i64 header_pos;
31 u8 is_dir;
32 i64 fsize;
33 i64 first_data_block;
34 i64 first_ext_block;
35 int sec_type;
36 de_ucstring *fn;
37 struct de_timestamp mod_time;
39 dbuf *outf;
40 i64 nbytes_written;
41 i64 next_block_to_read;
42 de_finfo *fi;
43 struct block_ptrs_tbl tmpbpt; // reused for each extension block
46 typedef struct localctx_struct {
47 u8 is_ffs;
48 u8 intnl_mode;
49 u8 dirc_mode;
50 i64 bsize;
51 i64 root_block;
52 i64 num_blocks;
53 int nesting_level;
54 u8 bootblock_flags;
55 u8 *block_used_flags; // array[num_blocks]
56 struct de_strarray *curpath;
57 } lctx;
59 static i64 blocknum_to_offset(lctx *d, i64 blknum)
61 return d->bsize * blknum;
64 static void on_adf_error(deark *c, lctx *d, int code)
66 de_err(c, "ADF decode error (%d)", code);
69 // Remember which blocks have been processed, to prevent infinite loops.
70 // In failure, reports an error and returns 0.
71 static int claim_block(deark *c, lctx *d, i64 blknum)
73 if(blknum<0 || blknum>=d->num_blocks) {
74 de_err(c, "Bad block number: %"I64_FMT, blknum);
75 return 0;
77 if(!d->block_used_flags) {
78 d->block_used_flags = de_malloc(c, d->num_blocks);
80 if(d->block_used_flags[blknum]) {
81 de_err(c, "Attempt to reuse block #%"I64_FMT, blknum);
82 return 0;
84 d->block_used_flags[blknum] = 1;
85 return 1;
88 // Reads a file data block.
89 // On success, returns nonzero and sets md->next_block_to_read.
90 static int do_file_ofs_data_block(deark *c, lctx *d, struct member_data *md,
91 i64 seq_num_expected, i64 blknum)
93 i64 pos1, pos;
94 i64 data_size;
95 i64 dpos;
96 i64 n;
97 int blocktype;
98 int retval = 0;
99 int dbg = (c->debug_level>=2);
100 int saved_indent_level;
102 de_dbg_indent_save(c, &saved_indent_level);
103 pos1 = blocknum_to_offset(d, blknum);
104 if(dbg) de_dbg(c, "data block: blk#%"I64_FMT" (%"I64_FMT"), seq=%d", blknum, pos1,
105 (int)seq_num_expected);
106 de_dbg_indent(c, 1);
108 if(!claim_block(c, d, blknum)) {
109 goto done;
112 pos = pos1;
113 blocktype = (int)de_geti32be_p(&pos);
114 if(dbg) de_dbg(c, "block type: %d", blocktype);
115 if(blocktype!=ADF_T_DATA) {
116 de_err(c, "%s: Bad block type in data block %d (%d, expected %d)",
117 ucstring_getpsz_d(md->fn), (int)seq_num_expected,
118 blocktype, (int)ADF_T_DATA);
119 goto done;
122 n = de_getu32be_p(&pos);
123 if(dbg) de_dbg(c, "header_key: %u", (UI)n);
124 if(n!=md->header_blknum) {
125 on_adf_error(c, d, 12);
126 goto done;
129 n = de_getu32be_p(&pos);
130 if(dbg) de_dbg(c, "seq_num: %u", (UI)n);
131 if(n!=seq_num_expected) {
132 on_adf_error(c, d, 13);
133 goto done;
135 data_size = de_getu32be_p(&pos);
136 if(dbg) de_dbg(c, "data size: %"I64_FMT, data_size);
137 if(data_size > d->bsize-24) {
138 de_err(c, "%s: Bad data size in data block %d (%"I64_FMT", max=%"I64_FMT")",
139 ucstring_getpsz_d(md->fn), (int)seq_num_expected,
140 data_size, (i64)(d->bsize-24));
141 goto done;
143 if(md->nbytes_written + data_size > md->fsize) {
144 on_adf_error(c, d, 15);
145 goto done;
148 md->next_block_to_read = de_getu32be_p(&pos);
149 if(dbg) de_dbg(c, "next data block: %"I64_FMT, md->next_block_to_read);
151 dpos = pos1 + 24;
152 dbuf_copy(c->infile, dpos, data_size, md->outf);
153 md->nbytes_written += data_size;
155 retval = 1;
157 done:
158 de_dbg_indent_restore(c, saved_indent_level);
159 return retval;
162 static void read_ofs_timestamp(deark *c, i64 pos1, struct de_timestamp *ts,
163 const char *name)
165 i64 pos = pos1;
166 i64 days, mins, ticks;
167 i64 ut;
168 char timestamp_buf[64];
170 ts->is_valid = 0;
171 days = de_getu32be_p(&pos);
172 mins = de_getu32be_p(&pos);
173 ticks = de_getu32be_p(&pos);
174 ut = (6*365 + 2*366) * 86400; // 1970-01-01 to 1978-01-01
175 ut += days * 86400;
176 ut += mins * 60;
177 ut += ticks / 50;
179 if(days!=0) {
180 de_unix_time_to_timestamp(ut, ts, 0);
181 de_timestamp_set_subsec(ts, (double)(ticks%50) / 50.0);
182 de_timestamp_to_string(ts, timestamp_buf, sizeof(timestamp_buf), 0);
184 else {
185 de_strlcpy(timestamp_buf, "none", sizeof(timestamp_buf));
188 de_dbg(c, "%s: [%"I64_FMT",%"I64_FMT",%"I64_FMT"] (%s)",
189 name, days, mins, ticks, timestamp_buf);
192 static void read_file_ofs_style(deark *c, lctx *d, struct member_data *md)
194 i64 seq_num;
196 md->next_block_to_read = md->first_data_block;
197 seq_num = 1;
198 while(1) {
199 int ret;
201 if(md->next_block_to_read==0) break;
203 ret = do_file_ofs_data_block(c, d, md, seq_num, md->next_block_to_read);
204 if(!ret) goto done;
205 seq_num++;
207 done:
211 static void read_blocks_table(deark *c, lctx *d, i64 pos, struct block_ptrs_tbl *bpt)
213 i64 k;
215 for(k=0; k<bpt->blocks_tbl_capacity; k++) {
216 bpt->blocks_tbl[k] = de_getu32be(pos + 4*k);
217 if(c->debug_level>=2 && bpt->blocks_tbl[k]!=0) {
218 de_dbg2(c, "blktbl[%d]: %u", (int)k, (UI)bpt->blocks_tbl[k]);
223 static int read_file_segment_from_blocks_tbl(deark *c, lctx *d, struct member_data *md)
225 i64 nbytes_left_to_copy;
226 i64 k;
227 int retval = 0;
229 nbytes_left_to_copy = md->fsize - md->outf->len;
231 for(k=0; k<md->tmpbpt.high_seq; k++) {
232 i64 blknum;
233 i64 blkpos;
234 i64 nbytes_to_copy;
236 if(nbytes_left_to_copy<1) break;
238 blknum = md->tmpbpt.blocks_tbl[md->tmpbpt.blocks_tbl_capacity-1-k];
239 if(!claim_block(c, d, blknum)) {
240 goto done;
243 blkpos = blocknum_to_offset(d, blknum);
244 nbytes_to_copy = d->bsize;
245 if(!d->is_ffs) {
246 // TODO: If we allow this, it might be better to call
247 // do_file_ofs_data_block(), somehow.
248 blkpos += 24;
249 nbytes_to_copy -= 24;
252 if(nbytes_to_copy > nbytes_left_to_copy) {
253 nbytes_to_copy = nbytes_left_to_copy;
255 dbuf_copy(c->infile, blkpos, nbytes_to_copy, md->outf);
256 nbytes_left_to_copy -= nbytes_to_copy;
258 retval = 1;
260 done:
261 return retval;
264 static int read_file_segment_from_extension_block(deark *c, lctx *d, struct member_data *md,
265 i64 blknum, i64 *pnextblock)
267 u64 pos1;
268 int blocktype;
269 int retval = 0;
270 int saved_indent_level;
272 de_dbg_indent_save(c, &saved_indent_level);
274 pos1 = blocknum_to_offset(d, blknum);
275 de_dbg(c, "file ext. block #%"I64_FMT, blknum);
276 de_dbg_indent(c, 1);
278 if(!claim_block(c, d, blknum)) {
279 goto done;
282 blocktype = (int)de_geti32be(pos1);
283 de_dbg(c, "block type: %d", blocktype);
284 if(blocktype!=ADF_T_LIST) {
285 de_err(c, "%s: Bad extension block type in (%d, expected %d)",
286 ucstring_getpsz_d(md->fn), blocktype, (int)ADF_T_LIST);
287 goto done;
290 md->tmpbpt.high_seq = de_getu32be(pos1+8);
291 de_dbg(c, "high_seq: %u", (UI)md->tmpbpt.high_seq);
292 read_blocks_table(c, d, pos1+24, &md->tmpbpt);
294 if(!read_file_segment_from_blocks_tbl(c, d, md)) {
295 goto done;
298 *pnextblock = de_getu32be(pos1+d->bsize-8);
299 de_dbg(c, "next ext. block: %"I64_FMT, (i64)(*pnextblock));
301 retval = 1;
303 done:
304 de_dbg_indent_restore(c, saved_indent_level);
305 return retval;
308 static void read_file_using_blocks_table(deark *c, lctx *d, struct member_data *md)
310 i64 cur_ext_header_blk;
312 if(md->tmpbpt.high_seq > md->tmpbpt.blocks_tbl_capacity) {
313 on_adf_error(c, d, 30);
314 goto done;
317 // Process the blocks table stored in the main file header
318 if(!read_file_segment_from_blocks_tbl(c, d, md)) goto done;
320 // Process the chain of extended header blocks
321 if(md->first_ext_block) {
322 cur_ext_header_blk = md->first_ext_block;
323 while(1) {
324 i64 next_ext_header_blk = 0;
326 if(cur_ext_header_blk == 0) break;
327 if(md->outf->len >= md->fsize) break;
329 if(!read_file_segment_from_extension_block(c, d, md, cur_ext_header_blk,
330 &next_ext_header_blk))
332 goto done;
334 cur_ext_header_blk = next_ext_header_blk;
338 if(md->outf->len < md->fsize) {
339 on_adf_error(c, d, 26);
340 goto done;
343 done:
347 static void read_protection_flags(deark *c, lctx *d, struct member_data *md, i64 pos)
349 UI n;
351 n = (UI)de_getu32be(pos);
352 de_dbg(c, "protection flags: 0x%08x", n);
353 if(md->fi && !md->is_dir) {
354 // Some disks use the 0x2 bit to mean "non executable", but I don't think
355 // there's a good way to tell *which* disks.
356 if((n & 0x0000ff0f)!=0) { // If these flags seem to be used...
357 if(n & 0x00000002) {
358 md->fi->mode_flags |= DE_MODEFLAG_NONEXE;
360 else {
361 md->fi->mode_flags |= DE_MODEFLAG_EXE;
367 static void do_file(deark *c, lctx *d, struct member_data *md)
369 i64 pos1, pos;
370 i64 fnlen;
371 i64 header_key;
372 i64 blocks_tbl_pos;
373 i64 n;
374 int need_curpath_pop = 0;
375 de_ucstring *fullfn = NULL;
376 int saved_indent_level;
378 de_dbg_indent_save(c, &saved_indent_level);
380 if(md->sec_type!=ADF_ST_FILE) goto done;
381 pos1 = md->header_pos;
382 de_dbg(c, "file, header at blk#%"I64_FMT" (%"I64_FMT")", md->header_blknum, pos1);
383 de_dbg_indent(c, 1);
385 pos = pos1 + 4;
386 header_key = de_getu32be_p(&pos);
387 de_dbg(c, "header_key: %u", (UI)header_key);
388 md->tmpbpt.high_seq = de_getu32be_p(&pos);
389 de_dbg(c, "high_seq: %u", (UI)md->tmpbpt.high_seq);
390 pos += 4; // data_size - unused
391 md->first_data_block = de_getu32be_p(&pos);
392 de_dbg(c, "first data block: %"I64_FMT, md->first_data_block);
394 if(header_key!=md->header_blknum) {
395 de_err(c, "Bad self-pointer (%"I64_FMT") in block #%"I64_FMT, header_key,
396 md->header_blknum);
397 goto done;
400 blocks_tbl_pos = md->header_pos + 24;
401 md->tmpbpt.blocks_tbl_capacity = (d->bsize/4) - 56;
402 md->tmpbpt.blocks_tbl = de_mallocarray(c, md->tmpbpt.blocks_tbl_capacity,
403 sizeof(md->tmpbpt.blocks_tbl[0]));
405 read_blocks_table(c, d, blocks_tbl_pos, &md->tmpbpt);
407 read_protection_flags(c, d, md, pos1+d->bsize-192);
409 pos = pos1+d->bsize-188;
410 md->fsize = de_getu32be_p(&pos);
411 de_dbg(c, "file size: %"I64_FMT, md->fsize);
413 pos = pos1+d->bsize-92;
414 read_ofs_timestamp(c, pos, &md->mod_time, "mod time");
416 pos = pos1+d->bsize-80;
417 fnlen = (i64)de_getbyte_p(&pos);
418 if(fnlen>30) fnlen=30;
419 md->fn = ucstring_create(c);
420 dbuf_read_to_ucstring(c->infile, pos, fnlen, md->fn, 0, DE_ENCODING_LATIN1);
421 de_dbg(c, "filename: \"%s\"", ucstring_getpsz_d(md->fn));
422 de_strarray_push(d->curpath, md->fn);
423 need_curpath_pop = 1;
425 pos = pos1+d->bsize-12;
426 n = de_getu32be_p(&pos);
427 de_dbg(c, "parent dir: %"I64_FMT, n);
428 md->first_ext_block = de_getu32be_p(&pos);
429 de_dbg(c, "first ext. block: %"I64_FMT, md->first_ext_block);
431 if(md->sec_type!=ADF_ST_FILE) {
432 de_dbg(c, "[not a supported file type]");
433 goto done;
436 md->fi->original_filename_flag = 1;
437 fullfn = ucstring_create(c);
438 de_strarray_make_path(d->curpath, fullfn, DE_MPFLAG_NOTRAILINGSLASH);
439 de_finfo_set_name_from_ucstring(c, md->fi, fullfn, DE_SNFLAG_FULLPATH);
441 if(md->mod_time.is_valid) {
442 md->fi->timestamp[DE_TIMESTAMPIDX_MODIFY] = md->mod_time;
445 md->outf = dbuf_create_output_file(c, NULL, md->fi, 0x0);
447 if(d->is_ffs) {
448 read_file_using_blocks_table(c, d, md);
450 else {
451 read_file_ofs_style(c, d, md);
454 done:
455 if(need_curpath_pop) {
456 de_strarray_pop(d->curpath);
458 ucstring_destroy(fullfn);
459 de_dbg_indent_restore(c, saved_indent_level);
462 static int do_header_block(deark *c, lctx *d, i64 blknum);
464 static void do_file_list(deark *c, lctx *d, i64 blknum)
466 i64 pos1;
467 i64 next_in_chain;
468 int saved_indent_level;
470 de_dbg_indent_save(c, &saved_indent_level);
472 pos1 = blocknum_to_offset(d, blknum);
473 de_dbg(c, "file list starting at blk#%"I64_FMT, blknum);
474 de_dbg_indent(c, 1);
476 while(1) {
477 if(blknum<1) break;
478 if(!do_header_block(c, d, blknum)) goto done;
479 next_in_chain = de_getu32be(pos1+d->bsize-16);
480 de_dbg(c, "next: %"I64_FMT, next_in_chain);
481 blknum = next_in_chain;
482 pos1 = blocknum_to_offset(d, blknum);
485 done:
486 de_dbg_indent_restore(c, saved_indent_level);
489 static void do_hashtable(deark *c, lctx *d, i64 pos1, i64 ht_size_in_longs)
491 i64 k;
492 i64 pos = pos1;
493 int saved_indent_level;
494 i64 used_count = 0;
496 de_dbg_indent_save(c, &saved_indent_level);
498 de_dbg(c, "hashtable at %"I64_FMT, pos1);
499 de_dbg_indent(c, 1);
501 for(k=0; k<ht_size_in_longs; k++) {
502 i64 n;
503 n = de_getu32be_p(&pos);
504 if(n>0 || c->debug_level>=2) {
505 de_dbg(c, "ht[%u]: %u", (UI)k, (UI)n);
507 if(n>0) {
508 used_count++;
509 de_dbg_indent(c, 1);
510 do_file_list(c, d, n);
511 de_dbg_indent(c, -1);
514 de_dbg(c, "hash buckets in use: %d of %d", (int)used_count, (int)ht_size_in_longs);
516 de_dbg_indent_restore(c, saved_indent_level);
519 // ST_ROOT or ST_USERDIR
520 static void do_directory(deark *c, lctx *d, struct member_data *md)
522 i64 pos1;
523 i64 ht_size_in_longs;
524 int saved_indent_level;
525 int need_curpath_pop = 0;
526 de_ucstring *fullfn = NULL;
527 struct de_timestamp tmpts;
529 de_dbg_indent_save(c, &saved_indent_level);
531 md->is_dir = 1;
532 pos1 = md->header_pos;
533 de_dbg(c, "directory header block: #%"I64_FMT" (%"I64_FMT")", md->header_blknum, pos1);
534 de_dbg_indent(c, 1);
535 if(md->sec_type!=ADF_ST_ROOT && md->sec_type!=ADF_ST_USERDIR) {
536 goto done;
539 if(md->sec_type==ADF_ST_ROOT) {
540 ht_size_in_longs = de_getu32be(pos1+12);
541 de_dbg(c, "hashtable size: %"I64_FMT" longwords", ht_size_in_longs);
542 if(ht_size_in_longs>128) {
543 on_adf_error(c, d, 20);
544 goto done;
547 else {
548 ht_size_in_longs = (d->bsize/4) - 56;
551 read_protection_flags(c, d, md, pos1+d->bsize-192);
553 read_ofs_timestamp(c, pos1+d->bsize-92, &md->mod_time, "dir mod time");
555 if(md->sec_type==ADF_ST_USERDIR) {
556 i64 fnlen;
558 fnlen = (i64)de_getbyte(pos1+d->bsize-80);
559 if(fnlen>30) fnlen=30;
560 md->fn = ucstring_create(c);
561 dbuf_read_to_ucstring(c->infile, pos1+d->bsize-79, fnlen, md->fn, 0, DE_ENCODING_LATIN1);
562 de_dbg(c, "dirname: \"%s\"", ucstring_getpsz_d(md->fn));
563 de_strarray_push(d->curpath, md->fn);
564 need_curpath_pop = 1;
567 if(md->sec_type==ADF_ST_ROOT) {
568 read_ofs_timestamp(c, pos1+d->bsize-40, &tmpts, "disk mod time");
569 read_ofs_timestamp(c, pos1+d->bsize-28, &tmpts, "filesystem create time");
572 // "extract"
573 md->fi->is_directory = 1;
574 if(md->sec_type==ADF_ST_ROOT) {
575 md->fi->is_root_dir = 1;
577 if(md->sec_type==ADF_ST_USERDIR) {
578 if(md->fn) {
579 md->fi->original_filename_flag = 1;
580 fullfn = ucstring_create(c);
581 de_strarray_make_path(d->curpath, fullfn, DE_MPFLAG_NOTRAILINGSLASH);
582 de_finfo_set_name_from_ucstring(c, md->fi, fullfn, DE_SNFLAG_FULLPATH);
586 if(md->mod_time.is_valid) {
587 md->fi->timestamp[DE_TIMESTAMPIDX_MODIFY] = md->mod_time;
590 md->outf = dbuf_create_output_file(c, NULL, md->fi, 0x0);
591 dbuf_close(md->outf);
592 md->outf = NULL;
594 // Now recurse into the files and subdirs in this directory.
595 do_hashtable(c, d, pos1+24, ht_size_in_longs);
597 done:
598 if(need_curpath_pop) {
599 de_strarray_pop(d->curpath);
601 ucstring_destroy(fullfn);
602 de_dbg_indent_restore(c, saved_indent_level);
605 // For block type 2 (ST_HEADER).
606 // Returns 1 unless the block isn't a header block.
607 static int do_header_block(deark *c, lctx *d, i64 blknum)
609 i64 pos1;
610 int blocktype;
611 int retval = 0;
612 int saved_indent_level;
613 struct member_data *md = NULL;
615 de_dbg_indent_save(c, &saved_indent_level);
617 d->nesting_level++;
618 if(d->nesting_level>MAX_NESTING_LEVEL) goto done;
620 pos1 = blocknum_to_offset(d, blknum);
622 if(!claim_block(c, d, blknum)) {
623 goto done;
626 blocktype = (int)de_geti32be(pos1);
627 if(blocktype==ADF_T_HEADER) {
628 retval = 1;
631 md = de_malloc(c, sizeof(struct member_data));
632 md->header_blknum = blknum;
633 md->header_pos = blocknum_to_offset(d, md->header_blknum);
634 md->fi = de_finfo_create(c);
636 de_dbg(c, "header block: #%"I64_FMT" (%"I64_FMT")", blknum, pos1);
637 de_dbg_indent(c, 1);
639 de_dbg(c, "block type: %d", blocktype);
640 if(blocktype!=ADF_T_HEADER) {
641 de_err(c, "Expected header block #%"I64_FMT" (at %"I64_FMT") not found", blknum, pos1);
642 goto done;
644 md->sec_type = (UI)de_getu32be(pos1+d->bsize-4);
645 de_dbg(c, "block secondary type: %d", md->sec_type);
647 if(md->sec_type==ADF_ST_ROOT || md->sec_type==ADF_ST_USERDIR) {
648 do_directory(c, d, md);
650 else if(md->sec_type==ADF_ST_FILE) {
651 do_file(c, d, md);
653 else {
654 de_warn(c, "Unsupported file type: %d", md->sec_type);
655 goto done;
658 done:
659 if(md) {
660 if(md->outf) {
661 dbuf_close(md->outf);
663 de_finfo_destroy(c, md->fi);
664 ucstring_destroy(md->fn);
665 de_free(c, md->tmpbpt.blocks_tbl);
666 de_free(c, md);
668 de_dbg_indent_restore(c, saved_indent_level);
669 d->nesting_level--;
670 return retval;
673 // If true, sets d->root_block
674 static int test_root_block(deark *c, lctx *d, i64 blknum)
676 i64 pos;
678 pos = blocknum_to_offset(d, blknum);
679 if(de_getu32be(pos) != ADF_T_HEADER) return 0;
680 if(de_getu32be(pos+d->bsize-4) != ADF_ST_ROOT) return 0;
681 d->root_block = blknum;
682 return 1;
685 // If found, sets d->root_block
686 static int find_root_block(deark *c, lctx *d, i64 root_block_reported)
688 if(c->infile->len >= 1802240) {
689 if(test_root_block(c, d, 880*2)) return 1;
691 else {
692 if(test_root_block(c, d, 880)) return 1;
695 if(test_root_block(c, d, root_block_reported)) return 1;
697 if((c->infile->len >= (901120+d->bsize)) && (c->infile->len < 1802240)) {
698 if(test_root_block(c, d, 880*2)) return 1;
701 return 0;
704 static void de_run_amiga_adf(deark *c, de_module_params *mparams)
706 lctx *d = NULL;
707 i64 root_block_reported;
708 int saved_indent_level;
709 de_ucstring *flags_descr;
711 de_dbg_indent_save(c, &saved_indent_level);
713 d = de_malloc(c, sizeof(lctx));
714 d->bsize = 512;
716 de_dbg(c, "header at %d", 0);
717 de_dbg_indent(c, 1);
719 d->bootblock_flags = (de_getbyte(3) & 0x07);
720 flags_descr = ucstring_create(c);
722 if(d->bootblock_flags & 0x1) {
723 d->is_ffs = 1;
725 if(d->bootblock_flags & 0x2) {
726 d->intnl_mode = 1;
728 if(d->bootblock_flags & 0x4) {
729 d->intnl_mode = 1;
730 d->dirc_mode = 1;
732 ucstring_append_flags_item(flags_descr, d->is_ffs?"FFS":"OFS");
733 if(d->intnl_mode) {
734 ucstring_append_flags_item(flags_descr, "international mode");
736 if(d->dirc_mode) {
737 ucstring_append_flags_item(flags_descr, "dircache mode");
739 de_dbg(c, "flags: 0x%02x (%s)", (UI)d->bootblock_flags, ucstring_getpsz_d(flags_descr));
740 ucstring_destroy(flags_descr);
742 de_declare_fmtf(c, "Amiga ADF, %s", d->is_ffs?"FFS":"OFS");
744 root_block_reported = de_getu32be(8);
745 de_dbg(c, "root block (reported): %"I64_FMT, root_block_reported);
747 d->num_blocks = de_pad_to_n(c->infile->len, 512) / 512;
748 if(d->num_blocks > MAX_ADF_BLOCKS) {
749 d->num_blocks = MAX_ADF_BLOCKS;
751 de_dbg_indent(c, -1);
753 if(d->dirc_mode || d->intnl_mode) {
754 de_warn(c, "This type of ADF file might not be supported correctly");
757 if(!find_root_block(c, d, root_block_reported)) {
758 de_err(c, "Root block not found");
759 goto done;
762 d->curpath = de_strarray_create(c, MAX_NESTING_LEVEL+10);
764 if(!do_header_block(c, d, d->root_block)) goto done;
766 done:
767 if(d) {
768 de_free(c, d->block_used_flags);
769 de_strarray_destroy(d->curpath);
770 de_free(c, d);
772 de_dbg_indent_restore(c, saved_indent_level);
775 static int de_identify_amiga_adf(deark *c)
777 int has_size;
778 int has_ext;
780 if(dbuf_memcmp(c->infile, 0, "DOS", 3)) return 0;
781 if(de_getbyte(3)>0x05) return 0;
782 has_ext = de_input_file_has_ext(c, "adf");
783 has_size = (c->infile->len==901120 || c->infile->len==1802240);
784 if(has_ext && has_size) return 100;
785 if(has_size) return 90;
786 if(has_ext) return 60;
787 return 20;
790 void de_module_amiga_adf(deark *c, struct deark_module_info *mi)
792 mi->id = "amiga_adf";
793 mi->desc = "Amiga disk image";
794 mi->run_fn = de_run_amiga_adf;
795 mi->identify_fn = de_identify_amiga_adf;