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
);
17 typedef struct localctx_struct
{
21 // Calculate the byte offset of a sector, given its logical track and
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
;
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
;
51 de_ucstring
*fname
= NULL
;
56 de_dbg(c
, "extracting file: t=%d,s=%d,sectors=%d", (int)ftrack
, (int)fsector
,
59 // Figure out the filename
61 // Find the length of the filename.
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.
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
));
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
);
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);
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
);
114 if(nexttrack
<1) break;
115 curtrack
= nexttrack
;
116 cursector
= nextsector
;
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
;
129 const char *file_type_str
;
132 de_dbg(c
, "directory entry at %d", (int)pos
);
135 file_type1
= de_getbyte(pos
+2);
136 file_type
= file_type1
& 0x7;
139 if((file_type1
&0x80)==0) {
140 file_type_str
= "scratched";
143 file_type_str
= "DEL";
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");
159 if(file_type
!=FTYPE_SEQ
&& file_type
!=FTYPE_PRG
&& file_type
!=FTYPE_USR
) {
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);
169 de_snprintf(tmps
, sizeof(tmps
), "%d to %d",
170 (int)((nsectors
-1)*254+1),
171 (int)(nsectors
*254));
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
);
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
)
191 pos
= sector_offset(track
, sector
);
192 de_dbg(c
, "directory sector at t=%d,s=%d pos=%d", (int)track
, (int)sector
,
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
);
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
)
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
,
222 do_directory_sector(c
, d
, curtrack
, cursector
, &nexttrack
, &nextsector
);
223 if(nexttrack
==0) break;
225 if(sectorcount
>1000) break;
226 curtrack
= nexttrack
;
227 cursector
= nextsector
;
231 static void de_run_d64(deark
*c
, de_module_params
*mparams
)
235 d
= de_malloc(c
, sizeof(lctx
));
237 do_directory(c
, d
, 18, 1);
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)) {
248 if(c
->infile
->len
==683*256) return 30;
249 if(c
->infile
->len
==683*256+683) return 30;
253 void de_module_d64(deark
*c
, struct deark_module_info
*mi
)
256 mi
->desc
= "D64 (C64 disk image)";
257 mi
->run_fn
= de_run_d64
;
258 mi
->identify_fn
= de_identify_d64
;