1 #include <stdlib.h>
2 #include <string.h>
3
4 #include "util.h"
5 #include "log.h"
6 #include <1541img/event.h>
7 #include <1541img/filedata.h>
8 #include <1541img/hostfilereader.h>
9 #include <1541img/hostfilewriter.h>
10 #include <1541img/petscii.h>
11
12 #include <1541img/cbmdosfile.h>
13
14 static const char *exts[] =
15 {
16 "DEL",
17 "SEQ",
18 "PRG",
19 "USR",
20 "REL"
21 };
22
CbmdosFileType_name(CbmdosFileType type)23 SOEXPORT const char *CbmdosFileType_name(CbmdosFileType type)
24 {
25 if (type < CFT_DEL || type > CFT_REL) return 0;
26 return exts[type];
27 }
28
29 struct CbmdosFile
30 {
31 CbmdosFileType type;
32 int invalidType;
33 int locked;
34 int closed;
35 int autoMapToLc;
36 char *name;
37 FileData *data;
38 Event *changedEvent;
39 uint8_t nameLength;
40 uint8_t recordLength;
41 uint16_t forcedBlocks;
42 };
43
fileDataHandler(void * receiver,int id,const void * sender,const void * args)44 static void fileDataHandler(void *receiver, int id, const void *sender,
45 const void *args)
46 {
47 (void)id;
48 (void)args;
49 (void)sender;
50
51 CbmdosFileEventArgs ea = { CFE_DATACHANGED };
52 CbmdosFile *self = receiver;
53 Event_raise(self->changedEvent, &ea);
54 }
55
CbmdosFile_create(void)56 SOEXPORT CbmdosFile *CbmdosFile_create(void)
57 {
58 CbmdosFile *self = xmalloc(sizeof *self);
59 memset(self, 0, sizeof *self);
60 self->data = FileData_create();
61 self->changedEvent = Event_create(0, self);
62 self->type = CFT_PRG;
63 self->invalidType = -1;
64 self->closed = 1;
65 self->forcedBlocks = 0xffff;
66 self->recordLength = 254;
67 Event_register(FileData_changedEvent(self->data), self, fileDataHandler);
68 return self;
69 }
70
CbmdosFile_clone(const CbmdosFile * other)71 SOEXPORT CbmdosFile *CbmdosFile_clone(const CbmdosFile *other)
72 {
73 CbmdosFile *self = xmalloc(sizeof *self);
74 self->type = other->type;
75 self->invalidType = other->invalidType;
76 self->locked = other->locked;
77 self->closed = other->closed;
78 self->autoMapToLc = 0;
79 self->name = 0;
80 self->data = FileData_clone(other->data);
81 self->changedEvent = Event_create(0, self);
82 self->nameLength = 0;
83 self->recordLength = other->recordLength;
84 self->forcedBlocks = other->forcedBlocks;
85 uint8_t len;
86 const char *name = CbmdosFile_name(other, &len);
87 CbmdosFile_setName(self, name, len);
88 self->autoMapToLc = other->autoMapToLc;
89 Event_register(FileData_changedEvent(self->data), self, fileDataHandler);
90 return self;
91 }
92
CbmdosFile_type(const CbmdosFile * self)93 SOEXPORT CbmdosFileType CbmdosFile_type(const CbmdosFile *self)
94 {
95 return self->type;
96 }
97
CbmdosFile_invalidType(const CbmdosFile * self)98 SOEXPORT int CbmdosFile_invalidType(const CbmdosFile *self)
99 {
100 return self->invalidType;
101 }
102
CbmdosFile_setType(CbmdosFile * self,CbmdosFileType type)103 SOEXPORT int CbmdosFile_setType(CbmdosFile *self, CbmdosFileType type)
104 {
105 int rc = 0;
106 int invalidType = -1;
107 type &= 0xf;
108 if (type < CFT_DEL || type > CFT_REL)
109 {
110 logmsg(L_WARNING, "CbmdosFile_setType: invalid type.");
111 invalidType = type;
112 type = CFT_DEL;
113 rc = -1;
114 }
115 if (type == self->type && invalidType == self->invalidType) return rc;
116 if ((self->invalidType < 0 && self->type == CFT_DEL) ||
117 (invalidType < 0 && type == CFT_DEL))
118 {
119 CbmdosFile_setData(self, FileData_create());
120 }
121 self->type = type;
122 self->invalidType = invalidType;
123 CbmdosFileEventArgs args = { CFE_TYPECHANGED };
124 Event_raise(self->changedEvent, &args);
125 return rc;
126 }
127
CbmdosFile_name(const CbmdosFile * self,uint8_t * length)128 SOEXPORT const char *CbmdosFile_name(const CbmdosFile *self, uint8_t *length)
129 {
130 if (length)
131 {
132 *length = self->nameLength;
133 }
134 return self->name;
135 }
136
CbmdosFile_setName(CbmdosFile * self,const char * name,uint8_t length)137 SOEXPORT void CbmdosFile_setName(
138 CbmdosFile *self, const char *name, uint8_t length)
139 {
140 if (length > 16)
141 {
142 logmsg(L_WARNING, "CbmdosFile_setName: truncating long name.");
143 length = 16;
144 }
145 free(self->name);
146 if (name)
147 {
148 self->name = xmalloc(length+1);
149 memcpy(self->name, name, length);
150 self->name[length] = 0;
151 }
152 else
153 {
154 self->name = 0;
155 if (length)
156 {
157 logmsg(L_WARNING, "CbmdosFile_setName: length > 0 given for empty "
158 "name.");
159 length = 0;
160 }
161 }
162 self->nameLength = length;
163 if (self->autoMapToLc)
164 {
165 petscii_mapUpperGfxToLower(self->name, self->nameLength);
166 }
167 CbmdosFileEventArgs args = { CFE_NAMECHANGED };
168 Event_raise(self->changedEvent, &args);
169 }
170
CbmdosFile_mapUpperGfxToLower(CbmdosFile * self)171 SOEXPORT void CbmdosFile_mapUpperGfxToLower(CbmdosFile *self)
172 {
173 petscii_mapUpperGfxToLower(self->name, self->nameLength);
174 CbmdosFileEventArgs args = { CFE_NAMECHANGED };
175 Event_raise(self->changedEvent, &args);
176 }
177
CbmdosFile_rdata(const CbmdosFile * self)178 SOEXPORT const FileData *CbmdosFile_rdata(const CbmdosFile *self)
179 {
180 return self->data;
181 }
182
CbmdosFile_data(CbmdosFile * self)183 SOEXPORT FileData *CbmdosFile_data(CbmdosFile *self)
184 {
185 return self->data;
186 }
187
CbmdosFile_setData(CbmdosFile * self,FileData * data)188 SOEXPORT void CbmdosFile_setData(CbmdosFile *self, FileData *data)
189 {
190 Event_unregister(FileData_changedEvent(self->data), self, fileDataHandler);
191 FileData_destroy(self->data);
192 self->data = data;
193 Event_register(FileData_changedEvent(self->data), self, fileDataHandler);
194 CbmdosFileEventArgs ea = { CFE_DATACHANGED };
195 Event_raise(self->changedEvent, &ea);
196 }
197
CbmdosFile_exportRaw(const CbmdosFile * self,FILE * file)198 SOEXPORT int CbmdosFile_exportRaw(const CbmdosFile *self, FILE *file)
199 {
200 return writeHostFile(self->data, file);
201 }
202
CbmdosFile_exportPC64(const CbmdosFile * self,FILE * file)203 SOEXPORT int CbmdosFile_exportPC64(const CbmdosFile *self, FILE *file)
204 {
205 uint8_t header[26] = "C64File";
206 memcpy(header+8, self->name, self->nameLength);
207 if (self->type == CFT_REL) header[25] = self->recordLength;
208 if (fwrite(header, sizeof header, 1, file) != 1) return -1;
209 return writeHostFile(self->data, file);
210 }
211
CbmdosFile_import(CbmdosFile * self,FILE * file)212 SOEXPORT int CbmdosFile_import(CbmdosFile *self, FILE *file)
213 {
214 FileData *data = readHostFile(file);
215 if (!data) return -1;
216 const uint8_t *content = FileData_rcontent(data);
217 if (FileData_size(data) > 26 && !content[24]
218 && !memcmp(content, "C64File", sizeof "C64File"))
219 {
220 FileData *raw = FileData_create();
221 if (!raw)
222 {
223 FileData_destroy(data);
224 return -1;
225 }
226 if (FileData_append(raw, content+26, FileData_size(data)-26) < 0)
227 {
228 FileData_destroy(raw);
229 FileData_destroy(data);
230 return -1;
231 }
232 CbmdosFile_setData(self, raw);
233 CbmdosFile_setName(self, (const char *)content+8,
234 strlen((const char *)content+8));
235 CbmdosFile_setRecordLength(self, content[25] ? content[25] : 254);
236 FileData_destroy(data);
237 }
238 else
239 {
240 CbmdosFile_setData(self, data);
241 }
242 return 0;
243 }
244
CbmdosFile_recordLength(const CbmdosFile * self)245 SOEXPORT uint8_t CbmdosFile_recordLength(const CbmdosFile *self)
246 {
247 return self->recordLength;
248 }
249
CbmdosFile_setRecordLength(CbmdosFile * self,uint8_t recordLength)250 SOEXPORT int CbmdosFile_setRecordLength(CbmdosFile *self, uint8_t recordLength)
251 {
252 if (recordLength > 254)
253 {
254 logmsg(L_WARNING, "CbmdosFile_setRecordLength: invalid length, "
255 "max is 254.");
256 return -1;
257 }
258 if (recordLength < 1)
259 {
260 logmsg(L_WARNING, "CbmdosFile_setRecordLength: invalid length, "
261 "min is 1.");
262 return -1;
263 }
264 self->recordLength = recordLength;
265 CbmdosFileEventArgs ea = { CFE_RECORDLENGTHCHANGED };
266 Event_raise(self->changedEvent, &ea);
267 return 0;
268 }
269
CbmdosFile_realBlocks(const CbmdosFile * self)270 SOEXPORT uint16_t CbmdosFile_realBlocks(const CbmdosFile *self)
271 {
272 if ((self->invalidType < 0 && self->type == CFT_DEL) || !self->data)
273 {
274 return 0;
275 }
276 size_t size = FileData_size(self->data);
277 uint16_t blocks = size / 254;
278 if (size % 254) ++blocks;
279 return blocks;
280 }
281
CbmdosFile_blocks(const CbmdosFile * self)282 SOEXPORT uint16_t CbmdosFile_blocks(const CbmdosFile *self)
283 {
284 if (self->forcedBlocks != 0xffff) return self->forcedBlocks;
285 return CbmdosFile_realBlocks(self);
286 }
287
CbmdosFile_forcedBlocks(const CbmdosFile * self)288 SOEXPORT uint16_t CbmdosFile_forcedBlocks(const CbmdosFile *self)
289 {
290 return self->forcedBlocks;
291 }
292
CbmdosFile_setForcedBlocks(CbmdosFile * self,uint16_t forcedBlocks)293 SOEXPORT void CbmdosFile_setForcedBlocks(
294 CbmdosFile *self, uint16_t forcedBlocks)
295 {
296 self->forcedBlocks = forcedBlocks;
297 CbmdosFileEventArgs args = { CFE_FORCEDBLOCKSCHANGED };
298 Event_raise(self->changedEvent, &args);
299 }
300
CbmdosFile_locked(const CbmdosFile * self)301 SOEXPORT int CbmdosFile_locked(const CbmdosFile *self)
302 {
303 return self->locked;
304 }
305
CbmdosFile_setLocked(CbmdosFile * self,int locked)306 SOEXPORT void CbmdosFile_setLocked(CbmdosFile *self, int locked)
307 {
308 self->locked = !!locked;
309 CbmdosFileEventArgs args = { CFE_LOCKEDCHANGED };
310 Event_raise(self->changedEvent, &args);
311 }
312
CbmdosFile_closed(const CbmdosFile * self)313 SOEXPORT int CbmdosFile_closed(const CbmdosFile *self)
314 {
315 return self->closed;
316 }
317
CbmdosFile_setClosed(CbmdosFile * self,int closed)318 SOEXPORT void CbmdosFile_setClosed(CbmdosFile *self, int closed)
319 {
320 self->closed = !!closed;
321 CbmdosFileEventArgs args = { CFE_CLOSEDCHANGED };
322 Event_raise(self->changedEvent, &args);
323 }
324
CbmdosFile_autoMapToLc(const CbmdosFile * self)325 SOEXPORT int CbmdosFile_autoMapToLc(const CbmdosFile *self)
326 {
327 return self->autoMapToLc;
328 }
329
CbmdosFile_setAutoMapToLc(CbmdosFile * self,int autoMapToLc)330 SOEXPORT void CbmdosFile_setAutoMapToLc(CbmdosFile *self, int autoMapToLc)
331 {
332 self->autoMapToLc = !!autoMapToLc;
333 }
334
CbmdosFile_getDirLine(const CbmdosFile * self,uint8_t * line)335 SOEXPORT void CbmdosFile_getDirLine(const CbmdosFile *self, uint8_t *line)
336 {
337 int blocklen = sprintf((char *)line, "%u", CbmdosFile_blocks(self));
338 memset(line + blocklen, 0xa0, 28 - blocklen);
339 memcpy(line + 6, self->name, self->nameLength);
340 if (self->invalidType < 0)
341 {
342 memcpy(line + 24, CbmdosFileType_name(self->type), 3);
343 }
344 else
345 {
346 memcpy(line + 24, "? ", 3);
347 }
348 line[5] = 0x22;
349 uint8_t endidx = 6;
350 while (line[endidx] != 0xa0) ++endidx;
351 line[endidx] = 0x22;
352 if (!self->closed) line[23] = 0x2a;
353 if (self->locked) line[27] = 0x3c;
354 }
355
CbmdosFile_changedEvent(CbmdosFile * self)356 SOEXPORT Event *CbmdosFile_changedEvent(CbmdosFile *self)
357 {
358 return self->changedEvent;
359 }
360
CbmdosFile_destroy(CbmdosFile * self)361 SOEXPORT void CbmdosFile_destroy(CbmdosFile *self)
362 {
363 if (!self) return;
364 free(self->name);
365 Event_destroy(self->changedEvent);
366 FileData_destroy(self->data);
367 }
368
369