1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <string.h>
4
5 #include "util.h"
6 #include "log.h"
7 #include "filename.h"
8
9 #include <1541img/filedata.h>
10 #include <1541img/hostfilereader.h>
11 #include <1541img/d64.h>
12 #include <1541img/d64reader.h>
13 #include <1541img/cbmdosvfs.h>
14 #include <1541img/cbmdosfile.h>
15 #include <1541img/cbmdosvfsreader.h>
16
17 #include <1541img/zcfileset.h>
18
19 struct ZcFileSet
20 {
21 ZcType type;
22 int count;
23 char *name;
24 FileData *files[];
25 };
26
ZcFileSet_create(ZcType type,const char * name)27 SOEXPORT ZcFileSet *ZcFileSet_create(ZcType type, const char *name)
28 {
29 int count;
30 switch (type)
31 {
32 case ZT_4PACK:
33 count = 4;
34 break;
35 case ZT_5PACK:
36 count = 5;
37 break;
38 case ZT_6PACK:
39 count = 6;
40 break;
41 default:
42 logmsg(L_ERROR, "ZcFileSet: invalid type.");
43 return 0;
44 }
45
46 ZcFileSet *self = xmalloc(sizeof *self + count * sizeof *self->files);
47 self->type = type;
48 self->count = count;
49 self->name = copystr(name);
50 for (int i = 0; i < count; ++i) self->files[i] = FileData_create();
51 return self;
52 }
53
fromFileData(const char * name,FileData ** files,int logerrors)54 static ZcFileSet *fromFileData(
55 const char *name, FileData **files, int logerrors)
56 {
57 ZcFileSet *self = 0;
58
59 if (files[0] && files[1] && files[2] && files[3])
60 {
61 if (files[4])
62 {
63 logmsg(L_INFO, "ZcFileSet: 5-file disk-packed zipcode "
64 "read successfully.");
65 self = xmalloc(sizeof *self + 5 * sizeof *self->files);
66 self->type = ZT_5PACK;
67 self->count = 5;
68 memcpy(self->files, files, 5 * sizeof *self->files);
69 }
70 else
71 {
72 logmsg(L_INFO, "ZcFileSet: 4-file disk-packed zipcode "
73 "read successfully.");
74 self = xmalloc(sizeof *self + 4 * sizeof *self->files);
75 self->type = ZT_4PACK;
76 self->count = 4;
77 memcpy(self->files, files, 4 * sizeof *self->files);
78 }
79 self->name = copystr(name);
80 }
81 else
82 {
83 if (logerrors)
84 {
85 logmsg(L_ERROR, "ZcFileSet: reading failed (missing or "
86 "unreadable files).");
87 }
88 for (int i = 0; i < 5; ++i) FileData_destroy(files[i]);
89 }
90
91 return self;
92 }
93
fromD64(const char * filename)94 static ZcFileSet *fromD64(const char *filename)
95 {
96 FILE *d64file = fopen_internal(filename, "rb");
97 if (!d64file)
98 {
99 logfmt(L_ERROR,
100 "ZcFileSet: error opening `%s' for reading.", filename);
101 return 0;
102 }
103 FileData *data = readHostFile(d64file);
104 if (!data)
105 {
106 fclose(d64file);
107 logfmt(L_ERROR, "ZcFileSet: error reading from `%s'.", filename);
108 return 0;
109 }
110 fclose(d64file);
111 ZcFileSet *self = ZcFileSet_fromFileData(data);
112 FileData_destroy(data);
113 return self;
114 }
115
fromVfsInternal(const CbmdosVfs * vfs,int logerrors)116 static ZcFileSet *fromVfsInternal(const CbmdosVfs *vfs, int logerrors)
117 {
118 logfmt(L_INFO, "ZcFileSet: checking disk `%s,%s'", CbmdosVfs_name(vfs, 0),
119 CbmdosVfs_id(vfs, 0));
120
121 const char *compare = 0;
122 FileData *files[5] = { 0 };
123 for (unsigned pos = 0; pos < CbmdosVfs_fileCount(vfs); ++pos)
124 {
125 const CbmdosFile *dosfile = CbmdosVfs_rfile(vfs, pos);
126 if (CbmdosFile_type(dosfile) != CFT_PRG) continue;
127 uint8_t namelen;
128 const char *name = CbmdosFile_name(dosfile, &namelen);
129 if (namelen < 3) continue;
130 if (name[1] != '!' || name[2] == '!') continue;
131 if (name[0] < '1' || name[0] > '5') continue;
132 if (compare)
133 {
134 if (strcmp(name+2, compare)) continue;
135 }
136 else compare = name+2;
137 int partidx = name[0] - '1';
138 if (files[partidx])
139 {
140 logfmt(logerrors ? L_WARNING : L_INFO,
141 "ZcFileSet: skipping duplicate `%s.PRG'", name);
142 continue;
143 }
144 files[partidx] = FileData_clone(CbmdosFile_rdata(dosfile));
145 logfmt(L_INFO, "ZcFileSet: found `%s.PRG' (%lu bytes)", name,
146 (unsigned long)FileData_size(files[partidx]));
147 }
148
149 return fromFileData(compare, files, logerrors);
150 }
151
ZcFileSet_fromVfs(const CbmdosVfs * vfs)152 SOEXPORT ZcFileSet *ZcFileSet_fromVfs(const CbmdosVfs *vfs)
153 {
154 return fromVfsInternal(vfs, 1);
155 }
156
ZcFileSet_tryFromVfs(const CbmdosVfs * vfs)157 SOEXPORT ZcFileSet *ZcFileSet_tryFromVfs(const CbmdosVfs *vfs)
158 {
159 return fromVfsInternal(vfs, 0);
160 }
161
fromFileDataInternal(const FileData * file,int logerrors)162 static ZcFileSet *fromFileDataInternal(const FileData *file, int logerrors)
163 {
164 D64 *d64 = readD64FromFileData(file);
165 if (!d64) return 0;
166 CbmdosVfs *vfs = CbmdosVfs_create();
167 int rc = readCbmdosVfs(vfs, d64, 0);
168 D64_destroy(d64);
169 if (rc < 0)
170 {
171 CbmdosVfs_destroy(vfs);
172 return 0;
173 }
174 ZcFileSet *self = fromVfsInternal(vfs, logerrors);
175 CbmdosVfs_destroy(vfs);
176 return self;
177 }
178
ZcFileSet_fromFileData(const FileData * file)179 SOEXPORT ZcFileSet *ZcFileSet_fromFileData(const FileData *file)
180 {
181 return fromFileDataInternal(file, 1);
182 }
183
ZcFileSet_tryFromFileData(const FileData * file)184 SOEXPORT ZcFileSet *ZcFileSet_tryFromFileData(const FileData *file)
185 {
186 return fromFileDataInternal(file, 0);
187 }
188
ZcFileSet_fromFile(const char * filename)189 SOEXPORT ZcFileSet *ZcFileSet_fromFile(const char *filename)
190 {
191 ZcFileSet *self = 0;
192 Filename *fn = Filename_create();
193 Filename_setFull(fn, filename);
194 char *ext = upperstr(Filename_ext(fn));
195 if ((!ext || strcmp(ext, "D64"))
196 && Filename_base(fn)[1] == '!')
197 {
198 char *base = copystr(Filename_base(fn));
199 if (base[0] >= '1' && base[0] <= '5'
200 && base[1] == '!' && base[2] != '!')
201 {
202 logmsg(L_INFO, "ZcFileSet: 4/5-file disk-packed zipcode "
203 "detected, looking for all member files...");
204 FileData *files[5] = { 0 };
205 for (base[0] = '1'; base[0] <= '5'; ++base[0])
206 {
207 Filename *pn = Filename_clone(fn);
208 Filename_setBase(pn, base);
209 const char *ffn = Filename_full(pn);
210 logfmt(L_INFO, "ZcFileSet: trying to read `%s'.", ffn);
211 FILE *p = fopen_internal(ffn, "rb");
212 if (p)
213 {
214 files[base[0]-'1'] = readHostFile(p);
215 fclose(p);
216 }
217 }
218 self = fromFileData(base+2, files, 1);
219 }
220 else
221 {
222 logmsg(L_WARNING, "ZcFileSet: no known ZipCode structure found.");
223 }
224 free(base);
225 }
226 else if (!strcmp(ext, "D64"))
227 {
228 self = fromD64(filename);
229 }
230 else
231 {
232 logmsg(L_WARNING, "ZcFileSet: no known ZipCode structure found.");
233 }
234 free(ext);
235 return self;
236 }
237
ZcFileSet_type(const ZcFileSet * self)238 SOEXPORT ZcType ZcFileSet_type(const ZcFileSet *self)
239 {
240 return self->type;
241 }
242
ZcFileSet_count(const ZcFileSet * self)243 SOEXPORT int ZcFileSet_count(const ZcFileSet *self)
244 {
245 return self->count;
246 }
247
ZcFileSet_name(const ZcFileSet * self)248 SOEXPORT const char *ZcFileSet_name(const ZcFileSet *self)
249 {
250 return self->name;
251 }
252
checkIndex(const ZcFileSet * self,int index)253 static int checkIndex(const ZcFileSet *self, int index)
254 {
255 if (index < 0 || index >= self->count)
256 {
257 logfmt(L_ERROR, "ZcFileSet: non-existing member file %d requested.",
258 index);
259 return -1;
260 }
261 return 0;
262 }
263
ZcFileSet_fileData(ZcFileSet * self,int index)264 SOEXPORT FileData *ZcFileSet_fileData(ZcFileSet *self, int index)
265 {
266 if (checkIndex(self, index) < 0) return 0;
267 return self->files[index];
268 }
269
ZcFileSet_rfileData(const ZcFileSet * self,int index)270 SOEXPORT const FileData *ZcFileSet_rfileData(const ZcFileSet *self, int index)
271 {
272 if (checkIndex(self, index) < 0) return 0;
273 return self->files[index];
274 }
275
ZcFileSet_save(const ZcFileSet * self,const char * filename)276 SOEXPORT int ZcFileSet_save(const ZcFileSet *self, const char *filename)
277 {
278 Filename *fn = Filename_create();
279 Filename_setFull(fn, filename);
280 const char *basename = self->name;
281 const char *fnbase = Filename_base(fn);
282 if (fnbase)
283 {
284 if (fnbase[0] && fnbase[1] == '!') basename = fnbase+2;
285 else basename = fnbase;
286 }
287
288 char *nextname = xmalloc(strlen(basename) + 3);
289 nextname[1] = '!';
290 strcpy(nextname+2, basename);
291
292 const char *ext = Filename_ext(fn);
293 if (!ext) Filename_setExt(fn, "prg");
294
295 for (nextname[0] = '1';
296 nextname[0] < (self->type == ZT_5PACK ? '6' : '5');
297 ++nextname[0])
298 {
299 Filename_setBase(fn, nextname);
300 FILE *f = fopen_internal(Filename_full(fn), "wb");
301 if (!f)
302 {
303 free(nextname);
304 logfmt(L_ERROR, "ZcFileSet_save: error opening `%s' for writing.",
305 Filename_full(fn));
306 Filename_destroy(fn);
307 return -1;
308 }
309 const FileData *fd = self->files[nextname[0]-'1'];
310 if (!fwrite(FileData_rcontent(fd), FileData_size(fd), 1, f))
311 {
312 fclose(f);
313 free(nextname);
314 logfmt(L_ERROR, "ZcFileSet_save: error writing to `%s'.",
315 Filename_full(fn));
316 Filename_destroy(fn);
317 return -1;
318 }
319 logfmt(L_INFO, "ZcFileSet_save: saved `%s'.", Filename_full(fn));
320 fclose(f);
321 }
322 free(nextname);
323 Filename_destroy(fn);
324 return 0;
325 }
326
ZcFileSet_saveVfs(const ZcFileSet * self,CbmdosVfs * vfs)327 SOEXPORT int ZcFileSet_saveVfs(const ZcFileSet *self, CbmdosVfs *vfs)
328 {
329 char name[16];
330 uint8_t baselen = strlen(self->name) > 14 ? 14 : strlen(self->name);
331 memcpy(name+2, self->name, baselen);
332 name[1] = '!';
333 for (name[0] = '1'; name[0] < (self->type == ZT_5PACK ? '6' : '5');
334 ++name [0])
335 {
336 CbmdosFile *file = CbmdosFile_create();
337 CbmdosFile_setName(file, name, baselen+2);
338 FileData *cfd = CbmdosFile_data(file);
339 const FileData *fd = self->files[name[0]-'1'];
340 if (FileData_append(cfd, FileData_rcontent(fd), FileData_size(fd)) < 0)
341 {
342 return -1;
343 }
344 if (CbmdosVfs_append(vfs, file))
345 {
346 return -1;
347 }
348 }
349 return 0;
350 }
351
ZcFileSet_destroy(ZcFileSet * self)352 SOEXPORT void ZcFileSet_destroy(ZcFileSet *self)
353 {
354 if (!self) return;
355 free(self->name);
356 for (int i = 0; i < self->count; ++i) FileData_destroy(self->files[i]);
357 free(self);
358 }
359
360