fat: Better handling of OS/2 extended attributes
[deark.git] / modules / fat.c
blob2bff8b5724c46501f4ec4da95ee3725fdb632869
1 // This file is part of Deark.
2 // Copyright (C) 2020 Jason Summers
3 // See the file COPYING for terms of use.
5 // FAT disk image
7 #include <deark-config.h>
8 #include <deark-private.h>
9 DE_DECLARE_MODULE(de_module_fat);
10 DE_DECLARE_MODULE(de_module_loaddskf);
11 DE_DECLARE_MODULE(de_module_ea_data);
13 #define MAX_NESTING_LEVEL 16
15 struct member_data {
16 u8 fn_base[8];
17 u8 fn_ext[3];
18 u8 is_subdir;
19 u8 is_special;
20 UI attribs;
21 UI ea_handle;
22 i64 fn_base_len, fn_ext_len;
23 i64 filesize;
24 i64 first_cluster;
25 de_ucstring *short_fn;
26 de_ucstring *long_fn;
27 struct de_timestamp mod_time;
30 struct dirctx {
31 u8 lfn_valid;
32 u8 first_seq_num;
33 u8 prev_seq_num;
34 u8 name_cksum;
35 i64 dir_entry_count;
36 i64 pending_lfn_bytesused;
37 #define LFN_CHARS_PER_FRAGMENT 13
38 #define LFN_MAX_FRAGMENTS 20
39 u8 pending_lfn[LFN_CHARS_PER_FRAGMENT*2*LFN_MAX_FRAGMENTS];
42 typedef struct localctx_struct {
43 de_encoding input_encoding;
44 u8 opt_check_root_dir;
45 u8 prescan_root_dir;
47 // TODO: Decide how to handle different variants of FAT.
48 #define FAT_SUBFMT_UNKNOWN 0
49 #define FAT_SUBFMT_PC 1
50 #define FAT_SUBFMT_ATARIST 2
51 int subfmt_req;
52 int subfmt;
53 #define FAT_PLATFORM_UNKNOWN 0
54 #define FAT_PLATFORM_PC 1
55 #define FAT_PLATFORM_ATARIST 2
56 int platform;
58 u8 num_fat_bits; // 12, 16, or 32. 0 if unknown.
59 u8 has_atarist_checksum;
60 i64 bytes_per_sector;
61 i64 sectors_per_cluster;
62 i64 bytes_per_cluster;
63 i64 num_sectors;
64 i64 data_region_sector;
65 i64 data_region_pos;
66 i64 num_data_region_clusters;
67 i64 num_rsvd_sectors;
68 i64 num_fats;
69 i64 num_sectors_per_fat;
70 i64 max_root_dir_entries16;
71 i64 root_dir_sector;
72 i64 num_cluster_identifiers;
73 struct de_strarray *curpath;
75 i64 num_fat_entries;
76 u32 *fat_nextcluster; // array[num_fat_entries]
77 u8 *cluster_used_flags; // array[num_fat_entries]
78 u8 *cluster_used_flags_saved; // array[num_fat_entries] (or NULL)
79 dbuf *ea_data; // NULL if not available
80 } lctx;
82 static void ucstring_append_hexbytes(de_ucstring *s, const u8 *buf, i64 buflen)
84 i64 i;
86 for(i = 0; i<buflen; i++) {
87 if(i>0) ucstring_append_char(s, ' ');
88 ucstring_append_char(s, (de_rune)de_get_hexchar((int)(buf[i]/16)));
89 ucstring_append_char(s, (de_rune)de_get_hexchar((int)(buf[i]%16)));
93 static void dbg_hexbytes_oneline_mem(deark *c, const u8 *buf, i64 buflen, const char *label)
95 de_ucstring *s = NULL;
97 if(buflen<0) buflen = 0;
98 if(buflen>64) buflen = 64;
99 s = ucstring_create(c);
100 ucstring_append_hexbytes(s, buf, buflen);
101 de_dbg(c, "%s: %s", label, ucstring_getpsz_d(s));
102 ucstring_destroy(s);
105 static void dbg_hexbytes_oneline(deark *c, dbuf *f, i64 pos, i64 len, const char *label)
107 u8 buf[64];
109 if(len<0) len = 0;
110 if(len>(i64)sizeof(buf)) len = (i64)sizeof(buf);
111 dbuf_read(f, buf, pos, len);
112 dbg_hexbytes_oneline_mem(c, buf, len, label);
115 static void fat_save_cluster_use_flags(deark *c, lctx *d)
117 if(!d->cluster_used_flags) return;
118 if(!d->cluster_used_flags_saved) {
119 d->cluster_used_flags_saved = de_malloc(c, d->num_fat_entries);
121 de_memcpy(d->cluster_used_flags_saved, d->cluster_used_flags,
122 (size_t)d->num_fat_entries);
125 static void fat_restore_cluster_use_flags(deark *c, lctx *d)
127 if(!d->cluster_used_flags_saved || !d->cluster_used_flags) return;
128 de_memcpy(d->cluster_used_flags, d->cluster_used_flags_saved,
129 (size_t)d->num_fat_entries);
132 static i64 sectornum_to_offset(deark *c, lctx *d, i64 secnum)
134 return secnum * d->bytes_per_sector;
137 static int is_good_clusternum(lctx *d, i64 cnum)
139 if(cnum<2) return 0;
140 if(cnum >= d->num_cluster_identifiers) return 0;
141 return 1;
144 static i64 clusternum_to_offset(deark *c, lctx *d, i64 cnum)
146 return d->data_region_pos + (cnum-2) * d->bytes_per_cluster;
149 static void dbg_timestamp(deark *c, struct de_timestamp *ts, const char *name)
151 char timestamp_buf[64];
153 de_timestamp_to_string(ts, timestamp_buf, sizeof(timestamp_buf), 0);
154 de_dbg(c, "%s: %s", name, timestamp_buf);
157 static i64 get_unpadded_len(const u8 *s, i64 len1)
159 i64 i;
160 i64 len = len1;
162 // Stop at NUL, I guess.
163 for(i=0; i<len1; i++) {
164 if(s[i]==0x00) {
165 len = i;
166 break;
170 for(i=len; i>0; i--) {
171 if(s[i-1]!=' ') {
172 return i;
175 return 0;
178 static int extract_file_lowlevel(deark *c, lctx *d, struct member_data *md, dbuf *outf)
180 int retval = 0;
181 i64 cur_cluster;
182 i64 nbytes_remaining;
184 cur_cluster = md->first_cluster;
185 if(md->is_subdir) {
186 nbytes_remaining = 0;
188 else {
189 nbytes_remaining = md->filesize;
192 while(1) {
193 i64 dpos;
194 i64 nbytes_to_copy;
196 if(nbytes_remaining <= 0) break;
197 if(!is_good_clusternum(d, cur_cluster)) break;
198 if(d->cluster_used_flags[cur_cluster]) break;
199 d->cluster_used_flags[cur_cluster] = 1;
200 if(c->debug_level>=3) de_dbg3(c, "cluster: %d", (int)cur_cluster);
201 dpos = clusternum_to_offset(c, d, cur_cluster);
202 nbytes_to_copy = de_min_int(d->bytes_per_cluster, nbytes_remaining);
203 dbuf_copy(c->infile, dpos, nbytes_to_copy, outf);
204 nbytes_remaining -= nbytes_to_copy;
205 cur_cluster = (i64)d->fat_nextcluster[cur_cluster];
208 if(nbytes_remaining>0) {
209 goto done;
212 retval = 1;
213 done:
214 return retval;
217 static void do_extract_file(deark *c, lctx *d, struct member_data *md)
219 dbuf *outf = NULL;
220 de_finfo *fi = NULL;
221 de_ucstring *fullfn = NULL;
223 if(!md->is_subdir) {
224 if(md->filesize > d->num_data_region_clusters * d->bytes_per_cluster) {
225 de_err(c, "%s: Bad file size", ucstring_getpsz_d(md->short_fn));
226 goto done;
230 fi = de_finfo_create(c);
231 fullfn = ucstring_create(c);
232 de_strarray_make_path(d->curpath, fullfn, DE_MPFLAG_NOTRAILINGSLASH);
233 de_finfo_set_name_from_ucstring(c, fi, fullfn, DE_SNFLAG_FULLPATH);
234 fi->original_filename_flag = 1;
235 if(md->is_subdir) {
236 fi->is_directory = 1;
238 if(md->mod_time.is_valid) {
239 fi->timestamp[DE_TIMESTAMPIDX_MODIFY] = md->mod_time;
242 outf = dbuf_create_output_file(c, NULL, fi, 0);
244 if(!extract_file_lowlevel(c, d, md, outf)) {
245 de_err(c, "%s: File extraction failed", ucstring_getpsz_d(md->short_fn));
246 goto done;
249 done:
250 dbuf_close(outf);
251 ucstring_destroy(fullfn);
252 de_finfo_destroy(c, fi);
255 static void do_subdir(deark *c, lctx *d, struct member_data *md, int nesting_level);
257 static void do_vfat_entry(deark *c, lctx *d, struct dirctx *dctx, i64 pos1, u8 seq_num_raw)
259 u8 seq_num;
260 u8 fn_cksum;
261 int is_first_entry = 0;
262 i64 startpos_in_lfn;
264 if(seq_num_raw==0xe5) {
265 de_dbg(c, "[deleted VFAT entry]");
266 dctx->lfn_valid = 0;
267 goto done;
270 de_dbg(c, "seq number: 0x%02x", (UI)seq_num_raw);
272 seq_num = seq_num_raw & 0xbf;
274 if(seq_num<1 || seq_num>LFN_MAX_FRAGMENTS) {
275 de_warn(c, "Bad VFAT sequence number (%u)", (UI)seq_num);
276 dctx->lfn_valid = 0;
277 goto done;
280 if(seq_num_raw & 0x40) {
281 is_first_entry = 1;
282 de_zeromem(dctx->pending_lfn, sizeof(dctx->pending_lfn));
283 dctx->first_seq_num = seq_num;
284 dctx->lfn_valid = 1;
286 else {
287 if(!dctx->lfn_valid || (seq_num+1 != dctx->prev_seq_num)) {
288 de_dbg(c, "[stray VFAT entry]");
289 dctx->lfn_valid = 0;
290 goto done;
293 dctx->prev_seq_num = seq_num;
295 startpos_in_lfn = LFN_CHARS_PER_FRAGMENT*2*((i64)seq_num-1);
297 de_read(&dctx->pending_lfn[startpos_in_lfn+ 0], pos1+ 1, 10); // 5 chars
298 fn_cksum = de_getbyte(pos1+13);
299 de_read(&dctx->pending_lfn[startpos_in_lfn+10], pos1+14, 12); // 6 more chars
300 de_read(&dctx->pending_lfn[startpos_in_lfn+22], pos1+28, 4); // 2 more chars
301 de_dbg(c, "filename checksum (reported): 0x%02x", (UI)fn_cksum);
302 if(!is_first_entry) {
303 if(fn_cksum != dctx->name_cksum) {
304 de_dbg(c, "[inconsistent VFAT checksums]");
305 dctx->lfn_valid = 0;
308 dctx->name_cksum = fn_cksum;
310 done:
314 static void vfat_cksum_update(const u8 *buf, size_t buflen, u8 *cksum)
316 size_t i;
318 for(i=0; i<buflen; i++) {
319 *cksum = (((*cksum) & 1) << 7) + ((*cksum) >> 1) + buf[i];
323 // If the long file name seems valid, sets it in md->long_fn for later use.
324 static void handle_vfat_lfn(deark *c, lctx *d, struct dirctx *dctx,
325 struct member_data *md)
327 u8 cksum_calc = 0;
328 i64 max_len_in_ucs2_chars;
329 i64 len_in_ucs2_chars = 0;
330 i64 i;
332 if(!dctx->lfn_valid) goto done;
333 if(dctx->prev_seq_num != 1) goto done;
334 if(md->long_fn) goto done;
336 vfat_cksum_update(md->fn_base, 8, &cksum_calc);
337 vfat_cksum_update(md->fn_ext, 3, &cksum_calc);
338 de_dbg(c, "filename checksum (calculated): 0x%02x", (UI)cksum_calc);
339 if(cksum_calc != dctx->name_cksum) goto done;
341 max_len_in_ucs2_chars = LFN_CHARS_PER_FRAGMENT * (i64)dctx->first_seq_num;
342 if(max_len_in_ucs2_chars > (i64)(sizeof(dctx->pending_lfn)/2)) goto done;
343 for(i=0; i<max_len_in_ucs2_chars; i++) {
344 if(dctx->pending_lfn[i*2]==0x00 && dctx->pending_lfn[i*2+1]==0x00) break;
345 if(dctx->pending_lfn[i*2]==0xff && dctx->pending_lfn[i*2+1]==0xff) break;
346 len_in_ucs2_chars++;
349 md->long_fn = ucstring_create(c);
350 ucstring_append_bytes(md->long_fn, dctx->pending_lfn, len_in_ucs2_chars*2,
351 0, DE_ENCODING_UTF16LE);
352 de_dbg(c, "long filename: \"%s\"", ucstring_getpsz_d(md->long_fn));
354 done:
358 // Reads from md->fn_base* and md->fn_ext*, writes to md->short_fn
359 static void decode_short_filename(deark *c, lctx *d, struct member_data *md)
361 if(md->fn_base_len>0) {
362 ucstring_append_bytes(md->short_fn, md->fn_base, md->fn_base_len, 0, d->input_encoding);
364 else {
365 ucstring_append_char(md->short_fn, '_');
367 if(md->fn_ext_len>0) {
368 ucstring_append_char(md->short_fn, '.');
369 ucstring_append_bytes(md->short_fn, md->fn_ext, md->fn_ext_len, 0, d->input_encoding);
373 static void decode_volume_label_name(deark *c, lctx *d, struct member_data *md)
375 if(md->fn_ext_len>0) {
376 ucstring_append_bytes(md->short_fn, md->fn_base, 8, 0, d->input_encoding);
377 ucstring_append_bytes(md->short_fn, md->fn_ext, md->fn_ext_len, 0, d->input_encoding);
379 else {
380 ucstring_append_bytes(md->short_fn, md->fn_base, md->fn_base_len, 0, d->input_encoding);
384 // md is that of the file whose EA data is being requested.
385 // Uses md->ea_handle.
386 static void do_fat_eadata_item(deark *c, lctx *d, struct member_data *md)
388 de_module_params *mparams = NULL;
390 if(!d->ea_data) goto done;
391 if(md->ea_handle==0) goto done;
392 mparams = de_malloc(c, sizeof(de_module_params));
393 mparams->in_params.input_encoding = d->input_encoding;
394 mparams->in_params.flags = 0x1;
395 mparams->in_params.uint1 = (u32)md->ea_handle;
396 de_dbg(c, "reading OS/2 extended attributes");
397 de_dbg_indent(c, 1);
398 // TODO: Better filenames for icons that may be extracted.
399 de_run_module_by_id_on_slice(c, "ea_data", mparams, d->ea_data, 0, d->ea_data->len);
400 de_dbg_indent(c, -1);
402 done:
403 de_free(c, mparams);
406 // md is that of the "EA DATA" file itself.
407 static void do_fat_eadata(deark *c, lctx *d, struct member_data *md)
409 int ret;
411 if(d->ea_data) goto done;
412 d->ea_data = dbuf_create_membuf(c, 0, 0);
413 dbuf_set_length_limit(d->ea_data, c->infile->len);
414 ret = extract_file_lowlevel(c, d, md, d->ea_data);
415 if(!ret) {
416 dbuf_close(d->ea_data);
417 d->ea_data = NULL;
418 goto done;
420 de_dbg(c, "[read EA data, len=%"I64_FMT"]", d->ea_data->len);
421 done:
425 // Returns 0 if this is the end-of-directory marker.
426 static int do_dir_entry(deark *c, lctx *d, struct dirctx *dctx,
427 i64 pos1, int nesting_level, int scanmode)
429 u8 firstbyte;
430 i64 ddate, dtime;
431 int retval = 0;
432 int is_deleted = 0;
433 int is_volume_label = 0;
434 int need_curpath_pop = 0;
435 de_ucstring *descr = NULL;
436 struct member_data *md = NULL;
438 md = de_malloc(c, sizeof(struct member_data));
440 de_dbg(c, "dir entry at %"I64_FMT, pos1);
441 de_dbg_indent(c, 1);
443 de_read(md->fn_base, pos1+0, 8);
444 de_read(md->fn_ext, pos1+8, 3);
445 firstbyte = md->fn_base[0];
447 if(firstbyte==0x00) {
448 de_dbg(c, "[end of dir marker]");
449 goto done;
451 retval = 1;
453 md->attribs = (UI)de_getbyte(pos1+11);
454 descr = ucstring_create(c);
455 de_describe_dos_attribs(c, md->attribs, descr, 0x1);
456 de_dbg(c, "attribs: 0x%02x (%s)", md->attribs, ucstring_getpsz_d(descr));
457 if((md->attribs & 0x3f)==0x0f) {
458 do_vfat_entry(c, d, dctx, pos1, firstbyte);
459 goto done;
462 if((md->attribs & 0x18) == 0x00) {
463 ; // Normal file
465 else if((md->attribs & 0x18) == 0x08) {
466 is_volume_label = 1;
467 md->is_special = 1;
469 else if((md->attribs & 0x18) == 0x10) {
470 md->is_subdir = 1;
472 else {
473 de_warn(c, "Invalid directory entry");
474 md->is_special = 1;
475 dctx->lfn_valid = 0;
476 goto done;
479 if(dctx->lfn_valid) {
480 handle_vfat_lfn(c, d, dctx, md);
481 dctx->lfn_valid = 0;
484 if(firstbyte==0xe5) {
485 de_dbg(c, "[deleted]");
486 is_deleted = 1;
487 md->fn_base[0] = '?';
489 else if(firstbyte==0x05) {
490 md->fn_base[0] = 0xe5;
493 md->fn_base_len = get_unpadded_len(md->fn_base, 8);
494 md->fn_ext_len = get_unpadded_len(md->fn_ext, 3);
496 if(md->is_subdir && md->fn_base_len>=1 && md->fn_base[0]=='.') {
497 // special "." and ".." dirs
498 md->is_special = 1;
501 md->short_fn = ucstring_create(c);
502 if(is_volume_label) {
503 decode_volume_label_name(c, d, md);
505 else {
506 decode_short_filename(c, d, md);
509 de_dbg(c, "filename: \"%s\"", ucstring_getpsz_d(md->short_fn));
511 if(ucstring_isnonempty(md->long_fn)) {
512 de_strarray_push(d->curpath, md->long_fn);
514 else {
515 de_strarray_push(d->curpath, md->short_fn);
517 need_curpath_pop = 1;
519 if(!scanmode && d->num_fat_bits<32) {
520 md->ea_handle = (UI)de_getu16le(pos1+20);
521 if(md->ea_handle) {
522 de_dbg(c, "EA handle (if OS/2): %u", md->ea_handle);
526 dtime = de_getu16le(pos1+22);
527 ddate = de_getu16le(pos1+24);
528 de_dos_datetime_to_timestamp(&md->mod_time, ddate, dtime);
529 dbg_timestamp(c, &md->mod_time, "mod time");
531 // TODO: This is wrong for FAT32.
532 md->first_cluster = de_getu16le(pos1+26);
533 de_dbg(c, "first cluster: %"I64_FMT, md->first_cluster);
535 md->filesize = de_getu32le(pos1+28);
536 de_dbg(c, "file size: %"I64_FMT, md->filesize);
538 // (Done reading dir entry)
540 if(is_deleted) goto done;
542 if(scanmode) {
543 if(!md->is_subdir && !md->is_special && (md->attribs&0x04) &&
544 md->fn_base_len==7 && md->fn_ext_len==3 &&
545 !de_memcmp(md->fn_base, "EA DATA", 7) &&
546 !de_memcmp(md->fn_ext, " SF", 3) )
548 do_fat_eadata(c, d, md);
549 goto done;
551 de_dbg2(c, "[scan mode - not extracting]");
552 goto done;
555 if(md->ea_handle!=0 && d->ea_data) {
556 do_fat_eadata_item(c, d, md);
559 if(!md->is_subdir && !md->is_special) {
560 do_extract_file(c, d, md);
562 else if(md->is_subdir && !md->is_special) {
563 do_extract_file(c, d, md);
564 do_subdir(c, d, md, nesting_level+1);
567 done:
568 ucstring_destroy(descr);
569 if(md) {
570 ucstring_destroy(md->short_fn);
571 ucstring_destroy(md->long_fn);
573 if(need_curpath_pop) {
574 de_strarray_pop(d->curpath);
576 de_dbg_indent(c, -1);
577 return retval;
580 // Process a contiguous block of directory entries
581 // Returns 0 if an end-of-dir marker was found.
582 static int do_dir_entries(deark *c, lctx *d, struct dirctx *dctx,
583 i64 pos1, i64 len, int nesting_level, int scanmode)
585 i64 num_entries;
586 i64 i;
587 int retval = 0;
589 num_entries = len/32;
590 de_dbg(c, "num entries: %"I64_FMT, num_entries);
592 for(i=0; i<num_entries; i++) {
593 if(!do_dir_entry(c, d, dctx, pos1+32*i, nesting_level, scanmode)) {
594 goto done;
596 dctx->dir_entry_count++;
599 retval = 1;
600 done:
601 return retval;
604 static void destroy_dirctx(deark *c, struct dirctx *dctx)
606 if(!dctx) return;
607 de_free(c, dctx);
610 static void do_subdir(deark *c, lctx *d, struct member_data *md, int nesting_level)
612 int saved_indent_level;
613 i64 cur_cluster_num;
614 i64 cur_cluster_pos;
615 struct dirctx *dctx = NULL;
617 de_dbg_indent_save(c, &saved_indent_level);
619 if(nesting_level >= MAX_NESTING_LEVEL) {
620 de_err(c, "Directories nested too deeply");
621 goto done;
624 dctx = de_malloc(c, sizeof(struct dirctx));
626 cur_cluster_num = md->first_cluster;
627 if(!is_good_clusternum(d, cur_cluster_num)) {
628 de_err(c, "Bad subdirectory entry");
629 goto done;
631 cur_cluster_pos = clusternum_to_offset(c, d, cur_cluster_num);
632 de_dbg(c, "subdir starting at %"I64_FMT, cur_cluster_pos);
633 de_dbg_indent(c, 1);
635 while(1) {
636 if(!is_good_clusternum(d, cur_cluster_num)) {
637 break;
639 cur_cluster_pos = clusternum_to_offset(c, d, cur_cluster_num);
640 de_dbg(c, "[subdir cluster %"I64_FMT" at %"I64_FMT"]", cur_cluster_num, cur_cluster_pos);
642 if(d->cluster_used_flags[cur_cluster_num]) {
643 goto done;
645 d->cluster_used_flags[cur_cluster_num] = 1;
647 if(!do_dir_entries(c, d, dctx, cur_cluster_pos, d->bytes_per_cluster, nesting_level, 0)) {
648 break;
651 cur_cluster_num = d->fat_nextcluster[cur_cluster_num];
654 done:
655 destroy_dirctx(c, dctx);
656 de_dbg_indent_restore(c, saved_indent_level);
659 static void do_root_dir(deark *c, lctx *d)
661 i64 pos1;
662 struct dirctx *dctx = NULL;
664 dctx = de_malloc(c, sizeof(struct dirctx));
665 pos1 = sectornum_to_offset(c, d, d->root_dir_sector);
666 de_dbg(c, "dir at %"I64_FMT, pos1);
667 de_dbg_indent(c, 1);
668 if(pos1<d->bytes_per_sector) goto done;
669 if(d->prescan_root_dir) {
670 de_dbg(c, "[scanning root dir]");
671 // This feature causes us to intentionally read some clusters more than once,
672 // so we have to work around our protections against doing that.
673 fat_save_cluster_use_flags(c, d);
674 de_dbg_indent(c, 1);
675 (void)do_dir_entries(c, d, dctx, pos1, d->max_root_dir_entries16 * 32, 0, 1);
676 de_dbg_indent(c, -1);
677 fat_restore_cluster_use_flags(c, d);
678 de_dbg(c, "[done scanning root dir]");
680 (void)do_dir_entries(c, d, dctx, pos1, d->max_root_dir_entries16 * 32, 0, 0);
681 done:
682 destroy_dirctx(c, dctx);
683 de_dbg_indent(c, -1);
686 static int root_dir_seems_valid(deark *c, lctx *d)
688 i64 pos1;
689 i64 max_entries_to_check;
690 i64 i;
691 i64 entrycount = 0;
692 i64 errcount = 0;
694 if(d->num_fat_bits==32) return 1;
696 if(d->max_root_dir_entries16<=0) return 0;
697 pos1 = sectornum_to_offset(c, d, d->root_dir_sector);
698 if(pos1 + d->max_root_dir_entries16 * 32 > c->infile->len) {
699 return 0;
702 max_entries_to_check = de_max_int(d->max_root_dir_entries16, 10);
703 for(i=0; i<max_entries_to_check; i++) {
704 i64 entrypos;
705 u8 firstbyte;
706 u8 attribs;
708 entrypos = pos1 + 32*i;
709 firstbyte = de_getbyte(entrypos);
710 if(firstbyte==0x00) break;
711 if(firstbyte==0xe5) continue; // Don't validate deleted entries
712 entrycount++;
713 attribs = de_getbyte(entrypos+11);
714 if(attribs & 0xc0) {
715 errcount++;
717 else if((attribs & 0x3f) == 0x0f) {
718 ; // LFN; OK
720 else if((attribs & 0x18)==0x18) {
721 errcount++; // dir + vol.label not valid
724 // TODO: It's really lame to only validate the attribs field, when there's
725 // so much more we could be doing. But it's a hard problem. We don't want
726 // to be too sensitive to minor errors.
729 if(errcount>1 || (errcount==1 && entrycount<=1)) {
730 return 0;
732 return 1;
735 static void do_atarist_boot_checksum(deark *c, lctx *d, i64 pos1)
737 i64 i;
738 UI ck = 0;
740 for(i=0; i<256; i++) {
741 ck += (UI)de_getu16be(pos1+i*2);
742 ck &= 0xffff;
745 de_dbg(c, "Atari ST checksum: 0x%04x", ck);
746 if(ck==0x1234) {
747 d->has_atarist_checksum = 1;
751 static void do_oem_name(deark *c, lctx *d, i64 pos, i64 len)
753 struct de_stringreaderdata *srd;
754 i64 i;
756 srd = dbuf_read_string(c->infile, pos, len, len, 0, DE_ENCODING_ASCII);
758 // Require printable ASCII.
759 for(i=0; i<len; i++) {
760 if(srd->sz[i]<32 || srd->sz[i]>126) {
761 goto done;
765 de_dbg(c, "OEM name: \"%s\"", ucstring_getpsz_d(srd->str));
767 done:
768 de_destroy_stringreaderdata(c, srd);
771 static int do_boot_sector(deark *c, lctx *d, i64 pos1)
773 i64 pos;
774 i64 num_data_region_sectors;
775 i64 num_root_dir_sectors;
776 i64 num_sectors_per_fat16;
777 i64 num_sectors_per_fat32 = 0;
778 i64 num_sectors16;
779 i64 num_sectors32 = 0;
780 i64 num_sectors_per_track;
781 i64 num_heads;
782 i64 jmpinstrlen;
783 u8 b;
784 u8 cksum_sig[2];
785 int retval = 0;
787 de_dbg(c, "boot sector at %"I64_FMT, pos1);
788 de_dbg_indent(c, 1);
790 // BIOS parameter block
791 jmpinstrlen = (d->subfmt==FAT_SUBFMT_ATARIST)?2:3;
792 dbg_hexbytes_oneline(c, c->infile, pos1, jmpinstrlen, "jump instr");
794 if(d->subfmt==FAT_SUBFMT_ATARIST) {
795 do_oem_name(c, d, pos1+2, 6);
796 dbg_hexbytes_oneline(c, c->infile, pos1+8, 3, "serial num");
798 else {
799 do_oem_name(c, d, pos1+3, 8);
802 pos = pos1+11;
803 d->bytes_per_sector = de_getu16le_p(&pos);
804 de_dbg(c, "bytes per sector: %d", (int)d->bytes_per_sector);
805 d->sectors_per_cluster = (i64)de_getbyte_p(&pos);
806 de_dbg(c, "sectors per cluster: %d", (int)d->sectors_per_cluster);
807 d->num_rsvd_sectors = de_getu16le_p(&pos);
809 de_dbg(c, "reserved sectors: %d", (int)d->num_rsvd_sectors);
810 if(d->num_rsvd_sectors==0) {
811 // This happens on some Atari ST disks. Don't know why.
812 d->num_rsvd_sectors = 1;
815 d->num_fats = (i64)de_getbyte_p(&pos);
816 de_dbg(c, "number of FATs: %d", (int)d->num_fats);
818 // This is expected to be 0 for FAT32.
819 d->max_root_dir_entries16 = de_getu16le_p(&pos);
820 de_dbg(c, "max number of root dir entries (if FAT12/16): %d", (int)d->max_root_dir_entries16);
822 num_sectors16 = de_getu16le_p(&pos);
823 de_dbg(c, "number of sectors (old 16-bit field): %d", (int)num_sectors16);
824 b = de_getbyte_p(&pos);
825 de_dbg(c, "media descriptor: 0x%02x", (UI)b);
826 num_sectors_per_fat16 = de_getu16le_p(&pos);
827 de_dbg(c, "sectors per FAT (if FAT12/16): %d", (int)num_sectors_per_fat16);
829 num_sectors_per_track = de_getu16le_p(&pos);
830 de_dbg(c, "sectors per track: %d", (int)num_sectors_per_track);
831 num_heads = de_getu16le_p(&pos);
832 de_dbg(c, "number of heads: %d", (int)num_heads);
834 pos = pos1+0x1fe;
835 de_read(cksum_sig, pos, 2);
836 dbg_hexbytes_oneline_mem(c, cksum_sig, 2, "boot sector signature");
838 do_atarist_boot_checksum(c, d, pos1);
839 if(d->has_atarist_checksum) {
840 d->platform = FAT_PLATFORM_ATARIST;
841 de_dbg(c, "[This is probably a bootable Atari ST disk.]");
843 else if(cksum_sig[0]==0x55 && cksum_sig[1]==0xaa) {
844 d->platform = FAT_PLATFORM_PC;
845 de_dbg(c, "[Disk has PC-compatible boot code.]");
848 if(num_sectors16==0) {
849 num_sectors32 = de_getu32le(pos1+32);
850 de_dbg(c, "num sectors (new 32-bit field): %"I64_FMT, num_sectors32);
853 if(num_sectors_per_fat16==0) {
854 num_sectors_per_fat32 = de_getu32le(pos1+36);
855 de_dbg(c, "sectors per FAT (if FAT32): %u", (UI)num_sectors_per_fat32);
858 if(num_sectors_per_fat16==0) {
859 d->num_sectors_per_fat = num_sectors_per_fat32;
861 else {
862 d->num_sectors_per_fat = num_sectors_per_fat16;
865 if(num_sectors16==0) {
866 d->num_sectors = num_sectors32;
868 else {
869 d->num_sectors = num_sectors16;
872 if(d->sectors_per_cluster<1) goto done;
873 if(d->bytes_per_sector<32) goto done;
874 d->bytes_per_cluster = d->bytes_per_sector * d->sectors_per_cluster;
875 d->root_dir_sector = d->num_rsvd_sectors + d->num_sectors_per_fat * d->num_fats;
876 de_dbg(c, "root dir pos (calculated): %"I64_FMT" (sector %"I64_FMT")",
877 sectornum_to_offset(c, d, d->root_dir_sector), d->root_dir_sector);
879 // num_root_dir_sectors is expected to be 0 for FAT32.
880 num_root_dir_sectors = (d->max_root_dir_entries16*32 + d->bytes_per_sector - 1)/d->bytes_per_sector;
882 num_data_region_sectors = d->num_sectors - (d->root_dir_sector + num_root_dir_sectors);
883 if(num_data_region_sectors<0) goto done;
884 d->num_data_region_clusters = num_data_region_sectors / d->sectors_per_cluster;
885 de_dbg(c, "num clusters (calculated): %"I64_FMT, d->num_data_region_clusters);
887 d->data_region_sector = d->root_dir_sector + num_root_dir_sectors;
888 d->data_region_pos = d->data_region_sector * d->bytes_per_sector;
889 de_dbg(c, "data region pos (calculated): %"I64_FMT" (sector %"I64_FMT")", d->data_region_pos,
890 d->data_region_sector);
892 // (The first cluster is numbered "2".)
893 d->num_cluster_identifiers = d->num_data_region_clusters + 2;
895 if(d->num_data_region_clusters < 4085) {
896 d->num_fat_bits = 12;
898 else if(d->num_data_region_clusters < 65525) {
899 d->num_fat_bits = 16;
901 else {
902 d->num_fat_bits = 32;
905 de_dbg(c, "bits per cluster id (calculated): %u", (UI)d->num_fat_bits);
907 retval = 1;
909 done:
910 if(!retval) {
911 de_err(c, "Invalid or unsupported boot sector");
913 de_dbg_indent(c, -1);
914 return retval;
917 static int do_read_fat(deark *c, lctx *d)
919 i64 pos1;
920 i64 pos;
921 i64 fat_idx_to_read = 0;
922 int retval = 0;
923 i64 i;
925 pos1 = sectornum_to_offset(c, d, d->num_rsvd_sectors + fat_idx_to_read*d->num_sectors_per_fat);
926 de_dbg(c, "FAT#%d at %"I64_FMT, (int)fat_idx_to_read, pos1);
927 de_dbg_indent(c, 1);
929 if(d->num_cluster_identifiers > (i64)(DE_MAX_SANE_OBJECT_SIZE/sizeof(u32))) goto done;
930 d->num_fat_entries = d->num_cluster_identifiers;
931 d->fat_nextcluster = de_mallocarray(c, d->num_fat_entries, sizeof(u32));
932 d->cluster_used_flags = de_malloc(c, d->num_fat_entries);
934 pos = pos1;
935 if(d->num_fat_bits==12) {
936 for(i=0; i<d->num_fat_entries+1; i+=2) {
937 UI val;
939 val = (UI)dbuf_getint_ext(c->infile, pos, 3, 1, 0);
940 pos += 3;
941 if(i < d->num_fat_entries) {
942 d->fat_nextcluster[i] = (u32)(val & 0xfff);
944 if(i+1 < d->num_fat_entries) {
945 d->fat_nextcluster[i+1] = (u32)(val >> 12);
949 else if(d->num_fat_bits==16) {
950 for(i=0; i<d->num_fat_entries; i++) {
951 d->fat_nextcluster[i] = (u32)de_getu16le_p(&pos);
954 else {
955 de_err(c, "This type of FAT is not supported");
956 goto done;
959 if(c->debug_level>=3) {
960 for(i=0; i<d->num_fat_entries; i++) {
961 de_dbg3(c, "fat[%"I64_FMT"]: %"I64_FMT, i, (i64)d->fat_nextcluster[i]);
965 retval = 1;
966 done:
967 de_dbg_indent(c, -1);
968 return retval;
971 static void de_run_fat(deark *c, de_module_params *mparams)
973 lctx *d = NULL;
974 const char *s;
975 int got_root_dir = 0;
976 de_encoding default_encoding = DE_ENCODING_CP437_G;
978 if(mparams) {
979 // out_params.flags:
980 // 0x1 = No valid FAT directory structure found
981 mparams->out_params.flags = 0;
984 d = de_malloc(c, sizeof(lctx));
986 d->prescan_root_dir = (u8)de_get_ext_option_bool(c, "fat:scanroot", 1);
987 d->opt_check_root_dir = (u8)de_get_ext_option_bool(c, "fat:checkroot", 1);
988 s = de_get_ext_option(c, "fat:subfmt");
989 if(s) {
990 if(!de_strcmp(s, "pc")) {
991 d->subfmt_req = FAT_SUBFMT_PC;
993 else if(!de_strcmp(s, "atarist")) {
994 d->subfmt_req = FAT_SUBFMT_ATARIST;
997 d->subfmt = d->subfmt_req;
998 if(d->subfmt==FAT_SUBFMT_ATARIST) {
999 default_encoding = DE_ENCODING_ATARIST;
1002 d->input_encoding = de_get_input_encoding(c, mparams, default_encoding);
1004 // TODO: Detect MBR?
1005 if(!do_boot_sector(c, d, 0)) goto done;
1006 if(d->num_fat_bits==0) goto done;
1008 switch(d->platform) {
1009 case FAT_PLATFORM_PC:
1010 de_declare_fmtf(c, "FAT%d - PC", d->num_fat_bits);
1011 break;
1012 case FAT_PLATFORM_ATARIST:
1013 de_declare_fmtf(c, "FAT%d - Atari ST", d->num_fat_bits);
1014 break;
1015 default:
1016 de_declare_fmtf(c, "FAT%d - Unknown platform", d->num_fat_bits);
1017 break;
1020 if(!do_read_fat(c, d)) goto done;
1022 if(d->opt_check_root_dir) {
1023 if(!root_dir_seems_valid(c, d)) {
1024 de_warn(c, "This file does not appear to contain a valid FAT "
1025 "directory structure. (\"-opt fat:checkroot=0\" to try anyway)");
1026 goto done;
1030 d->curpath = de_strarray_create(c, MAX_NESTING_LEVEL+10);
1031 got_root_dir = 1;
1032 do_root_dir(c, d);
1034 done:
1035 if(!got_root_dir) {
1036 // Inform the parent module that we failed to do anything.
1037 if(mparams) {
1038 mparams->out_params.flags |= 0x1;
1042 if(d) {
1043 de_free(c, d->fat_nextcluster);
1044 de_free(c, d->cluster_used_flags);
1045 de_free(c, d->cluster_used_flags_saved);
1046 if(d->curpath) de_strarray_destroy(d->curpath);
1047 if(d->ea_data) dbuf_close(d->ea_data);
1048 de_free(c, d);
1052 static int de_identify_fat(deark *c)
1054 i64 bytes_per_sector;
1055 i64 max_root_dir_entries;
1056 i64 num_rsvd_sectors;
1057 int confidence = 0;
1058 int has_pc_sig;
1059 int has_ext;
1060 u8 sectors_per_cluster;
1061 u8 num_fats;
1062 u8 media_descr;
1063 u8 b[32];
1065 // TODO: This needs a lot of work.
1066 // It's good enough for most FAT12 floppy disk images.
1068 de_read(b, 0, sizeof(b));
1069 bytes_per_sector = de_getu16le_direct(&b[11]);
1070 sectors_per_cluster = b[13];
1071 num_rsvd_sectors = de_getu16le_direct(&b[14]);
1072 num_fats = b[16];
1073 max_root_dir_entries = de_getu16le_direct(&b[17]);
1074 media_descr = b[21];
1076 if(bytes_per_sector!=512) return 0;
1077 switch(sectors_per_cluster) {
1078 case 1: case 2: case 4: case 8:
1079 case 16: case 32: case 64: case 128:
1080 break;
1081 default:
1082 return 0;
1084 if(num_fats!=1 && num_fats!=2) return 0;
1085 if(media_descr<0xe5 && media_descr!=0) return 0; // Media descriptor
1087 confidence = 1;
1088 if(b[0]==0xeb && b[2]==0x90) confidence += 2;
1089 else if(b[0]==0xe9) confidence += 1;
1090 else if(b[0]==0x60) confidence += 1;
1091 has_pc_sig = (de_getu16be(510)==0x55aa);
1092 if(has_pc_sig) confidence += 2;
1093 if(num_fats==2) confidence += 1;
1094 if(media_descr>=0xe5) confidence += 1;
1095 if(num_rsvd_sectors==1) confidence += 1;
1096 if(max_root_dir_entries==112 || max_root_dir_entries==224) confidence += 2;
1098 has_ext = de_input_file_has_ext(c, "ima") ||
1099 de_input_file_has_ext(c, "img") ||
1100 de_input_file_has_ext(c, "st");
1102 if(confidence>=6) return (has_ext?100:80);
1103 else if(confidence>=4) return (has_ext?60:9);
1104 else return 0;
1107 static void de_help_fat(deark *c)
1109 de_msg(c, "-opt fat:checkroot=0 : Read the directory structure, even if it "
1110 "seems invalid");
1111 de_msg(c, "-opt fat:scanroot=0 : Do not scan the root directory to look for "
1112 "special files");
1115 void de_module_fat(deark *c, struct deark_module_info *mi)
1117 mi->id = "fat";
1118 mi->desc = "FAT disk image";
1119 mi->run_fn = de_run_fat;
1120 mi->identify_fn = de_identify_fat;
1121 mi->help_fn = de_help_fat;
1124 ///////////////////////// LoadDskF/SaveDskF format (OS/2-centric)
1126 // We barely support this format, but if it's uncompressed, we'll try to skip
1127 // past the header, and pretend it's FAT.
1129 struct skf_ctx {
1130 int new_fmt;
1131 i64 hdr_size;
1134 static void loaddskf_decode_as_fat(deark *c, struct skf_ctx *d)
1136 i64 dlen = c->infile->len - d->hdr_size;
1138 de_dbg(c, "decoding as FAT, pos=%"I64_FMT", len=%"I64_FMT, d->hdr_size, dlen);
1139 if(dlen<=0) goto done;
1141 de_dbg_indent(c, 1);
1142 de_run_module_by_id_on_slice(c, "fat", NULL, c->infile, d->hdr_size, dlen);
1143 de_dbg_indent(c, -1);
1144 done:
1148 static int loaddskf_read_header(deark *c, struct skf_ctx *d)
1150 int retval = 0;
1152 d->hdr_size = de_getu16le(38);
1153 de_dbg(c, "header size: %"I64_FMT, d->hdr_size);
1154 if((UI)de_getu16be(d->hdr_size + 510) != 0x55aa) {
1155 goto done;
1157 retval = 1;
1159 done:
1160 if(!retval) {
1161 de_err(c, "Failed to parse LoadDskF file");
1163 return retval;
1166 static void de_run_loaddskf(deark *c, de_module_params *mparams)
1168 struct skf_ctx *d = NULL;
1169 UI sig;
1171 d = de_malloc(c, sizeof(struct skf_ctx));
1172 sig = (UI)de_getu16be(0);
1173 switch(sig) {
1174 case 0xaa58:
1175 break;
1176 case 0xaa59:
1177 d->new_fmt = 1;
1178 break;
1179 case 0xaa5a:
1180 de_err(c, "Compressed LoadDskF files are not supported");
1181 goto done;
1182 default:
1183 de_err(c, "Not a LoadDskF file");
1184 goto done;
1187 if(!loaddskf_read_header(c, d)) goto done;
1188 loaddskf_decode_as_fat(c, d);
1190 done:
1191 de_free(c, d);
1194 static int de_identify_loaddskf(deark *c)
1196 UI sig;
1198 sig = (UI)de_getu16be(0);
1199 if(sig==0xaa58 || sig==0xaa59 || sig==0xaa5a) {
1200 if((UI)de_getu16be(2)==0xf000) {
1201 return 100;
1203 return 9;
1205 return 0;
1208 void de_module_loaddskf(deark *c, struct deark_module_info *mi)
1210 mi->id = "loaddskf";
1211 mi->desc = "LoadDskF/SaveDskF disk image";
1212 mi->run_fn = de_run_loaddskf;
1213 mi->identify_fn = de_identify_loaddskf;
1216 //---------------------- "EA DATA. SF"
1218 struct easector_ctx {
1219 i64 ea_data_len;
1222 struct eadata_ctx {
1223 de_encoding input_encoding;
1224 UI createflags_for_icons;
1225 i64 bytes_per_cluster;
1228 static int eadata_is_ea_sector_at_offset(deark *c, struct eadata_ctx *d, i64 pos, int strictmode)
1230 u8 b;
1232 if((UI)de_getu16be(pos)!=0x4541) return 0;
1233 if(strictmode) {
1234 if((UI)de_getu32be(pos+4)!=0) return 0;
1235 b = de_getbyte(pos+8);
1236 if(b<32) return 0;
1237 if((UI)de_getu32be(pos+22)!=0) return 0;
1239 return 1;
1242 static const char *eadata_get_data_type_name(UI t)
1244 const char *name = NULL;
1245 switch(t) {
1246 case 0xfffe: name ="binary"; break;
1247 case 0xfffd: name ="text"; break;
1248 case 0xfff9: name ="icon"; break;
1251 return name?name:"?";
1254 static int eadata_extract_icon(deark *c, struct eadata_ctx *d, struct easector_ctx *md,
1255 i64 pos1, i64 nbytes_avail)
1257 i64 ipos, ilen;
1258 int retval = 0;
1260 ilen = de_getu16le(pos1);
1261 de_dbg(c, "icon len: %"I64_FMT, ilen);
1262 ipos = pos1 + 2;
1263 if(ipos + ilen > pos1 + nbytes_avail) goto done;
1265 dbuf_create_file_from_slice(c->infile, ipos, ilen, "os2.ico", NULL, d->createflags_for_icons);
1266 retval = 1;
1267 done:
1268 return retval;
1271 // Sets md->ea_data_len.
1272 static void eadata_do_ea_data(deark *c, struct eadata_ctx *d, struct easector_ctx *md,
1273 i64 pos1)
1275 i64 pos = pos1;
1276 i64 endpos;
1277 int saved_indent_level;
1278 de_ucstring *s = NULL;
1280 de_dbg_indent_save(c, &saved_indent_level);
1281 de_dbg(c, "EA data at %"I64_FMT, pos1);
1282 de_dbg_indent(c, 1);
1284 md->ea_data_len = de_getu16le_p(&pos); // TODO: Is this actually a 4-byte field?
1285 de_dbg(c, "data len: %"I64_FMT, md->ea_data_len);
1287 pos += 2; // ?
1288 endpos = pos1 + md->ea_data_len;
1289 s = ucstring_create(c);
1291 while(pos < endpos-4) {
1292 i64 namelen;
1293 i64 attr_dpos;
1294 i64 attr_dlen;
1295 UI attr_dtype;
1296 int handled = 0;
1298 de_dbg(c, "attribute at %"I64_FMT, pos);
1299 de_dbg_indent(c, 1);
1300 pos++; // ?
1301 namelen = (i64)de_getbyte_p(&pos);
1303 attr_dlen = (i64)de_getu16le_p(&pos);
1304 ucstring_empty(s);
1305 dbuf_read_to_ucstring(c->infile, pos, namelen, s, 0, DE_ENCODING_ASCII);
1306 de_dbg(c, "name: \"%s\"", ucstring_getpsz_d(s));
1307 pos += namelen + 1;
1308 attr_dpos = pos;
1309 de_dbg(c, "outer data len: %"I64_FMT, attr_dlen);
1310 attr_dtype = (UI)de_getu16le_p(&pos);
1311 de_dbg(c, "data type: 0x%04x (%s)", attr_dtype, eadata_get_data_type_name(attr_dtype));
1313 if(attr_dtype==0xfff9) {
1314 handled = eadata_extract_icon(c, d, md, attr_dpos+2, attr_dlen-2);
1317 if(!handled) {
1318 de_dbg_hexdump(c, c->infile, attr_dpos+2, attr_dlen-2, 256, NULL, 0x1);
1321 pos = attr_dpos + attr_dlen;
1322 de_dbg_indent(c, -1);
1325 ucstring_destroy(s);
1326 de_dbg_indent_restore(c, saved_indent_level);
1329 static void eadata_do_ea_sector_by_offset(deark *c, struct eadata_ctx *d, i64 pos1,
1330 i64 *pbytes_consumed1)
1332 i64 n;
1333 i64 pos;
1334 de_ucstring *fn = NULL;
1335 struct easector_ctx *md = NULL;
1336 int saved_indent_level;
1338 de_dbg_indent_save(c, &saved_indent_level);
1339 md = de_malloc(c, sizeof(struct easector_ctx));
1341 if(!eadata_is_ea_sector_at_offset(c, d, pos1, 0)) {
1342 de_err(c, "EA sector not found at %"I64_FMT, pos1);
1343 goto done;
1346 de_dbg(c, "EA sector at %"I64_FMT, pos1);
1347 de_dbg_indent(c, 1);
1348 pos = pos1+2;
1349 n = de_getu16le_p(&pos);
1350 de_dbg(c, "sector number (consistency check): %u", (UI)n);
1352 pos += 4;
1354 fn = ucstring_create(c);
1355 dbuf_read_to_ucstring(c->infile, pos, 12, fn, DE_CONVFLAG_STOP_AT_NUL, d->input_encoding);
1356 de_dbg(c, "file name: \"%s\"", ucstring_getpsz_d(fn));
1357 pos += 12;
1359 pos += 2;
1360 pos += 4;
1362 eadata_do_ea_data(c, d, md, pos);
1363 pos += md->ea_data_len;
1365 if(pbytes_consumed1) {
1366 *pbytes_consumed1 = pos - pos1;
1369 done:
1370 ucstring_destroy(fn);
1371 de_free(c, md);
1372 de_dbg_indent_restore(c, saved_indent_level);
1375 static int eadata_id_to_offset(deark *c, struct eadata_ctx *d, UI id, i64 *poffset)
1377 int retval = 0;
1378 UI a_idx;
1379 UI a_val;
1380 UI b_val;
1381 i64 cluster_num;
1383 *poffset = 0;
1385 a_idx = id>>7;
1386 if(a_idx>=240) goto done;
1387 a_val = (UI)de_getu16le(32+2*(i64)a_idx);
1388 b_val = (UI)de_getu16le(512+2*(i64)id);
1389 if(b_val==0xffff) goto done;
1391 cluster_num = (i64)b_val + (i64)a_val;
1392 *poffset = d->bytes_per_cluster * cluster_num;
1394 if(eadata_is_ea_sector_at_offset(c, d, *poffset, 0)) {
1395 retval = 1;
1398 done:
1399 return retval;
1402 static void eadata_scan_file(deark *c, struct eadata_ctx *d)
1404 i64 pos = 1024;
1406 while(pos < c->infile->len) {
1407 if(eadata_is_ea_sector_at_offset(c, d, pos, 1)) {
1408 i64 bytes_consumed;
1410 eadata_do_ea_sector_by_offset(c, d, pos, &bytes_consumed);
1412 if(bytes_consumed<1) bytes_consumed = 1;
1413 pos = de_pad_to_n(pos+bytes_consumed, 512);
1415 else {
1416 pos += 512;
1421 static void de_run_eadata(deark *c, de_module_params *mparams)
1423 int ret;
1424 UI ea_id = 0;
1425 i64 pos;
1426 const char *s;
1428 de_declare_fmt(c, "OS/2 extended attributes data");
1430 struct eadata_ctx *d = de_malloc(c, sizeof(struct eadata_ctx));
1432 if(mparams && (mparams->in_params.flags & 0x1)) {
1433 // We're being used by another module, to handle a specific ea_id.
1434 ea_id = (UI)mparams->in_params.uint1;
1435 if(ea_id==0) goto done;
1436 d->createflags_for_icons = DE_CREATEFLAG_IS_AUX;
1438 else {
1439 s = de_get_ext_option(c, "ea_data:handle");
1440 if(s) {
1441 ea_id = (UI)de_atoi(s);
1445 d->input_encoding = de_get_input_encoding(c, mparams, DE_ENCODING_CP437);
1446 d->bytes_per_cluster = 512;
1448 if(ea_id==0) {
1449 eadata_scan_file(c, d);
1451 else {
1452 ret = eadata_id_to_offset(c, d, ea_id, &pos);
1453 if(!ret) goto done;
1454 eadata_do_ea_sector_by_offset(c, d, pos, NULL);
1457 done:
1458 de_free(c, d);
1461 static int de_identify_eadata(deark *c)
1463 if(de_getu16be(0)!=0x4544) return 0;
1464 if(de_input_file_has_ext(c, " sf")) return 100;
1465 if(dbuf_is_all_zeroes(c->infile, 2, 30)) {
1466 return 20;
1468 return 0;
1471 static void de_help_eadata(deark *c)
1473 de_msg(c, "-opt ea_data:handle=<n> : Decode only EA handle/pointer <n>");
1476 void de_module_ea_data(deark *c, struct deark_module_info *mi)
1478 mi->id = "ea_data";
1479 mi->desc = "EA DATA (OS/2 extended attributes)";
1480 mi->run_fn = de_run_eadata;
1481 mi->identify_fn = de_identify_eadata;
1482 mi->help_fn = de_help_eadata;