Minor refactoring of the IFF and box-format parsers
[deark.git] / modules / d64.c
bloba27a0d65a691b3529c4d97b5d663b2422c65981c
1 // This file is part of Deark.
2 // Copyright (C) 2016 Jason Summers
3 // See the file COPYING for terms of use.
5 // D64 (Commodore 64 disk image)
7 #include <deark-config.h>
8 #include <deark-private.h>
9 DE_DECLARE_MODULE(de_module_d64);
11 #define FTYPE_DEL 0x0
12 #define FTYPE_SEQ 0x1
13 #define FTYPE_PRG 0x2
14 #define FTYPE_USR 0x3
15 #define FTYPE_REL 0x4
17 typedef struct localctx_struct {
18 int reserved;
19 } lctx;
21 // Calculate the byte offset of a sector, given its logical track and
22 // sector numbers.
23 // Sectors range from 0 to 20
24 // Tracks range from 1 to (usually) 35
25 static i64 sector_offset(i64 track, i64 sector)
27 i64 global_sector_index;
28 i64 t;
30 global_sector_index = 0;
31 for(t=1; t<track; t++) {
32 if(t<=17) global_sector_index+=21;
33 else if(t<=24) global_sector_index+=19;
34 else if(t<=30) global_sector_index+=18;
35 else global_sector_index+=17;
37 global_sector_index += sector;
39 return 256*(global_sector_index);
42 static void do_extract_file(deark *c, lctx *d, i64 dir_entry_pos,
43 u8 file_type, i64 ftrack, i64 fsector, i64 nsectors)
45 i64 nsectors_written = 0;
46 i64 curtrack, cursector;
47 i64 nexttrack, nextsector;
48 const char *ext;
49 dbuf *f = NULL;
50 de_finfo *fi = NULL;
51 de_ucstring *fname = NULL;
52 i64 fname_len;
53 i64 i;
54 u8 z;
56 de_dbg(c, "extracting file: t=%d,s=%d,sectors=%d", (int)ftrack, (int)fsector,
57 (int)nsectors);
59 // Figure out the filename
61 // Find the length of the filename.
62 fname_len = 0;
63 for(i=15; i>=0; i--) {
64 z = de_getbyte(dir_entry_pos+5+i);
65 // TODO: Filenames are padded with 0xa0 bytes. I'm not sure if the
66 // filename length is determined by the first 0xa0 byte, or the
67 // last non-0xa0 byte. This assumes it's the last non-0xa0 byte.
68 if(z!=0xa0) {
69 fname_len = i+1;
70 break;
73 de_dbg2(c, "filename length: %d", (int)fname_len);
74 fname = ucstring_create(c);
75 dbuf_read_to_ucstring(c->infile, dir_entry_pos+5, fname_len, fname, 0, DE_ENCODING_PETSCII);
76 de_dbg(c, "filename: \"%s\"", ucstring_getpsz(fname));
78 switch(file_type) {
79 case FTYPE_SEQ: ext="seq"; break;
80 case FTYPE_PRG: ext="prg"; break;
81 case FTYPE_USR: ext="usr"; break;
82 default: ext="bin"; break;
84 ucstring_append_sz(fname, ".", DE_ENCODING_LATIN1);
85 ucstring_append_sz(fname, ext, DE_ENCODING_LATIN1);
86 ///////
88 fi = de_finfo_create(c);
89 de_finfo_set_name_from_ucstring(c, fi, fname, 0);
90 fi->original_filename_flag = 1;
92 f = dbuf_create_output_file(c, NULL, fi, 0);
94 curtrack = ftrack;
95 cursector = fsector;
96 while(1) {
97 i64 pos;
98 i64 amt_to_copy;
100 if(nsectors_written>=nsectors) break;
102 pos = sector_offset(curtrack, cursector);
104 nexttrack = (i64)de_getbyte(pos+0);
105 nextsector = (i64)de_getbyte(pos+1);
106 de_dbg2(c, "next sector: t=%d,s=%d", (int)nexttrack, (int)nextsector);
108 if(nexttrack==0 && nextsector>=1) amt_to_copy = (i64)nextsector-1;
109 else amt_to_copy = 254;
111 dbuf_copy(c->infile, pos+2, amt_to_copy, f);
112 nsectors_written++;
114 if(nexttrack<1) break;
115 curtrack = nexttrack;
116 cursector = nextsector;
119 dbuf_close(f);
120 de_finfo_destroy(c, fi);
121 ucstring_destroy(fname);
124 static void do_dir_entry(deark *c, lctx *d, i64 pos)
126 u8 file_type1, file_type;
127 i64 ftrack, fsector;
128 i64 nsectors;
129 const char *file_type_str;
130 char tmps[100];
132 de_dbg(c, "directory entry at %d", (int)pos);
133 de_dbg_indent(c, 1);
135 file_type1 = de_getbyte(pos+2);
136 file_type = file_type1 & 0x7;
137 switch(file_type) {
138 case FTYPE_DEL:
139 if((file_type1&0x80)==0) {
140 file_type_str = "scratched";
142 else {
143 file_type_str = "DEL";
145 break;
146 case FTYPE_SEQ: file_type_str = "SEQ"; break;
147 case FTYPE_PRG: file_type_str = "PRG"; break;
148 case FTYPE_USR: file_type_str = "USR"; break;
149 case FTYPE_REL: file_type_str = "REL"; break;
150 default: file_type_str = "unknown"; break;
153 de_dbg(c, "file type: 0x%02x (%s)", (unsigned int)file_type1, file_type_str);
155 if(file_type==FTYPE_REL) {
156 de_warn(c, "REL files are not supported");
157 goto done;
159 if(file_type!=FTYPE_SEQ && file_type!=FTYPE_PRG && file_type!=FTYPE_USR) {
160 goto done;
163 ftrack = (i64)de_getbyte(pos+3);
164 fsector = (i64)de_getbyte(pos+4);
165 de_dbg(c, "file starts at t=%d,s=%d", (int)ftrack, (int)fsector);
167 nsectors = de_getu16le(pos+30);
168 if(nsectors>0) {
169 de_snprintf(tmps, sizeof(tmps), "%d to %d",
170 (int)((nsectors-1)*254+1),
171 (int)(nsectors*254));
173 else {
174 de_strlcpy(tmps, "0", sizeof(tmps));
176 de_dbg(c, "number of sectors used: %d (expected file size=%s)",
177 (int)nsectors, tmps);
179 do_extract_file(c, d, pos, file_type, ftrack, fsector, nsectors);
181 done:
182 de_dbg_indent(c, -1);
185 static void do_directory_sector(deark *c, lctx *d, i64 track, i64 sector,
186 i64 *nexttrack, i64 *nextsector)
188 i64 pos;
189 i64 i;
191 pos = sector_offset(track, sector);
192 de_dbg(c, "directory sector at t=%d,s=%d pos=%d", (int)track, (int)sector,
193 (int)pos);
194 de_dbg_indent(c, 1);
196 *nexttrack = (i64)de_getbyte(pos);
197 *nextsector = (i64)de_getbyte(pos+1);
198 de_dbg(c, "next dir sector: t=%d,s=%d", (int)*nexttrack, (int)*nextsector);
200 for(i=0; i<8; i++) {
201 do_dir_entry(c, d, pos+i*32);
204 de_dbg_indent(c, -1);
207 static void do_directory(deark *c, lctx *d, i64 track, i64 sector)
209 i64 pos;
210 i64 sectorcount;
211 i64 nexttrack, nextsector;
212 i64 curtrack, cursector;
214 pos = sector_offset(track, sector);
215 de_dbg(c, "directory at t=%d,s=%d pos=%d", (int)track, (int)sector,
216 (int)pos);
218 curtrack = track;
219 cursector = sector;
220 sectorcount = 0;
221 while(1) {
222 do_directory_sector(c, d, curtrack, cursector, &nexttrack, &nextsector);
223 if(nexttrack==0) break;
224 sectorcount++;
225 if(sectorcount>1000) break;
226 curtrack = nexttrack;
227 cursector = nextsector;
231 static void de_run_d64(deark *c, de_module_params *mparams)
233 lctx *d = NULL;
235 d = de_malloc(c, sizeof(lctx));
237 do_directory(c, d, 18, 1);
239 de_free(c, d);
242 static int de_identify_d64(deark *c)
244 if(!de_input_file_has_ext(c, "d64")) return 0;
245 if(!dbuf_memcmp(c->infile, 357*256, "\x12\x01\x41\x00", 4)) {
246 return 100;
248 if(c->infile->len==683*256) return 30;
249 if(c->infile->len==683*256+683) return 30;
250 return 0;
253 void de_module_d64(deark *c, struct deark_module_info *mi)
255 mi->id = "d64";
256 mi->desc = "D64 (C64 disk image)";
257 mi->run_fn = de_run_d64;
258 mi->identify_fn = de_identify_d64;