Refactoring the iff decoder
[deark.git] / modules / t64.c
blob971b3b469a32cc9b6578275c83f187e3f8d362ca
1 // This file is part of Deark.
2 // Copyright (C) 2016 Jason Summers
3 // See the file COPYING for terms of use.
5 // T64 (Commodore 64 "tape"-like format)
7 #include <deark-config.h>
8 #include <deark-private.h>
9 DE_DECLARE_MODULE(de_module_t64);
11 typedef struct localctx_struct {
12 i64 version;
13 i64 max_dir_entries;
14 i64 used_dir_entries;
15 } lctx;
17 static void do_extract_file(deark *c, lctx *d, i64 dir_pos,
18 u8 filetype_c64s, u8 filetype)
20 i64 load_addr;
21 i64 end_addr;
22 i64 offset;
23 dbuf *f = NULL;
24 i64 payload_size; // = file_size-2
25 de_ucstring *fname = NULL;
26 i64 fname_len;
27 i64 i;
28 i64 fnpos;
29 de_finfo *fi = NULL;
31 load_addr = de_getu16le(dir_pos+2);
32 end_addr = de_getu16le(dir_pos+4);
33 offset = de_getu32le(dir_pos+8);
34 de_dbg(c, "load_addr=%d end_addr=%d offset=%d", (int)load_addr,
35 (int)end_addr, (int)offset);
37 // File name at pos+16
39 fnpos = dir_pos+16;
41 // Find the length of the (space-padded) filename.
42 fname_len = 0;
43 for(i=15; i>=0; i--) {
44 if(de_getbyte(fnpos+i)!=' ') {
45 fname_len = i+1;
46 break;
49 de_dbg2(c, "filename length: %d", (int)fname_len);
51 fname = ucstring_create(c);
52 dbuf_read_to_ucstring(c->infile, fnpos, fname_len, fname, 0, DE_ENCODING_PETSCII);
53 de_dbg(c, "filename: \"%s\"", ucstring_getpsz(fname));
55 ucstring_append_sz(fname, ".prg", DE_ENCODING_LATIN1);
57 fi = de_finfo_create(c);
58 de_finfo_set_name_from_ucstring(c, fi, fname, 0);
59 fi->original_filename_flag = 1;
61 payload_size = end_addr - load_addr;
62 if(payload_size < 0) {
63 // TODO: Try to support files that don't have end_addr set properly.
64 de_err(c, "This type of T64 file is not supported.");
65 goto done;
68 f = dbuf_create_output_file(c, NULL, fi, 0);
69 dbuf_copy(c->infile, dir_pos+2, 2, f);
70 dbuf_copy(c->infile, offset, payload_size, f);
72 done:
73 dbuf_close(f);
74 de_finfo_destroy(c, fi);
75 ucstring_destroy(fname);
78 static void do_dir_entry(deark *c, lctx *d, i64 entry_num, i64 pos)
80 u8 filetype_c64s;
81 u8 filetype;
83 filetype_c64s = de_getbyte(pos);
84 if(filetype_c64s==0) {
85 de_dbg2(c, "unused entry #%d at %d", (int)entry_num, (int)pos);
86 return;
88 de_dbg(c, "entry #%d at %d", (int)entry_num, (int)pos);
90 de_dbg_indent(c, 1);
92 filetype = de_getbyte(pos+1);
93 de_dbg(c, "c64s filetype=%d, filetype=0x%02x", (int)filetype_c64s, (int)filetype);
95 if(filetype==0x00) {
96 de_err(c, "Unsupported file type (0x%02x)", (int)filetype);
98 else {
99 do_extract_file(c, d, pos, filetype_c64s, filetype);
102 de_dbg_indent(c, -1);
105 static void de_run_t64(deark *c, de_module_params *mparams)
107 lctx *d = NULL;
108 i64 pos;
109 i64 i;
111 d = de_malloc(c, sizeof(lctx));
113 pos = 32;
114 d->version = de_getu16le(pos);
115 de_dbg(c, "version: 0x%04x", (int)d->version);
116 if(d->version!=0x100 && d->version!=0x101) {
117 de_warn(c, "Unexpected version number. This might not be a T64 file.");
120 d->max_dir_entries = de_getu16le(pos+2);
121 d->used_dir_entries = de_getu16le(pos+4);
122 de_dbg(c, "max dir entries = %d, files = %d", (int)d->max_dir_entries, (int)d->used_dir_entries);
124 pos += 32;
125 for(i=0; i<d->max_dir_entries; i++) {
126 do_dir_entry(c, d, i, pos+32*i);
129 de_free(c, d);
132 static int de_identify_t64(deark *c)
134 if(!dbuf_memcmp(c->infile, 0, "C64", 3)) return 80;
135 return 0;
138 void de_module_t64(deark *c, struct deark_module_info *mi)
140 mi->id = "t64";
141 mi->desc = "T64 (C64 tape format)";
142 mi->run_fn = de_run_t64;
143 mi->identify_fn = de_identify_t64;