File Coverage

diskimage.c
Criterion Covered Total %
statement 364 722 50.4
branch 124 266 46.6
condition n/a
subroutine n/a
pod n/a
total 488 988 49.3


line stmt bran cond sub pod time code
1             #include
2             #include
3             #include
4             #include "diskimage.h"
5              
6              
7             typedef struct errormessage {
8             signed int number;
9             char *string;
10             } ErrorMessage;
11              
12              
13             ErrorMessage error_msg[] = {
14             /* non-errors */
15             { 0, "ok" },
16             { 1, "files scratched" },
17             { 2, "partition selected" },
18             /* errors */
19             { 20, "read error (block header not found)" },
20             { 21, "read error (drive not ready)" },
21             { 22, "read error (data block not found)" },
22             { 23, "read error (crc error in data block)" },
23             { 24, "read error (byte sector header)" },
24             { 25, "write error (write-verify error)" },
25             { 26, "write protect on" },
26             { 27, "read error (crc error in header)" },
27             { 30, "syntax error (general syntax)" },
28             { 31, "syntax error (invalid command)" },
29             { 32, "syntax error (long line)" },
30             { 33, "syntax error (invalid file name)" },
31             { 34, "syntax error (no file given)" },
32             { 39, "syntax error (invalid command)" },
33             { 50, "record not present" },
34             { 51, "overflow in record" },
35             { 52, "file too large" },
36             { 60, "write file open" },
37             { 61, "file not open" },
38             { 62, "file not found" },
39             { 63, "file exists" },
40             { 64, "file type mismatch" },
41             { 65, "no block" },
42             { 66, "illegal track and sector" },
43             { 67, "illegal system t or s" },
44             { 70, "no channel" },
45             { 71, "directory error" },
46             { 72, "disk full" },
47             { 73, "dos mismatch" },
48             { 74, "drive not ready" },
49             { 75, "format error" },
50             { 76, "controller error" },
51             { 77, "selected partition illegal" },
52             { -1, NULL }
53             };
54              
55              
56             /* convert to rawname */
57 57           int di_rawname_from_name(unsigned char *rawname, char *name) {
58             int i;
59              
60 57           memset(rawname, 0xa0, 16);
61 621 100         for (i = 0; i < 16 && name[i]; ++i) {
    100          
62 564           rawname[i] = name[i];
63             }
64 57           return(i);
65             }
66              
67              
68             /* convert from rawname */
69 25           int di_name_from_rawname(char *name, unsigned char *rawname) {
70             int i;
71              
72 244 100         for (i = 0; i < 16 && rawname[i] != 0xa0; ++i) {
    100          
73 219           name[i] = rawname[i];
74             }
75 25           name[i] = 0;
76 25           return(i);
77             }
78              
79              
80             /* return status string */
81 3           int di_status(DiskImage *di, char *status) {
82 3           ErrorMessage *err = error_msg;
83              
84             /* special case for power up */
85 3 100         if (di->status == 254) {
86 1           switch (di->type) {
87             case D64:
88 1           sprintf(status, "73,cbm dos v2.6 1541,00,00");
89 1           break;
90             case D71:
91 0           sprintf(status, "73,cbm dos v3.0 1571,00,00");
92 0           break;
93             case D81:
94 0           sprintf(status, "73,copyright cbm dos v10 1581,00,00");
95 0           break;
96             }
97 1           return(73);
98             }
99              
100 2 50         while (err->number >= 0) {
101 2 50         if (di->status == err->number) {
102 2           sprintf(status, "%02d,%s,%02d,%02d", di->status, err->string, di->statusts.track, di->statusts.sector);
103 2           return(di->status);
104             }
105 0           err->number++;
106             }
107 0           sprintf(status, "%02d,unknown error,%02d,%02d", di->status, di->statusts.track, di->statusts.sector);
108 0           return(di->status);
109             }
110              
111              
112 95           int set_status(DiskImage *di, int status, int track, int sector) {
113 95           di->status = status;
114 95           di->statusts.track = track;
115 95           di->statusts.sector = sector;
116 95           return(status);
117             }
118              
119              
120             /* return write interleave */
121 12           int interleave(ImageType type) {
122 12           switch (type) {
123             case D64:
124 12           return(10);
125             break;
126             case D71:
127 0           return(6);
128             break;
129             default:
130 0           return(1);
131             break;
132             }
133             }
134              
135              
136             /* return number of tracks for image type */
137 2761           int di_tracks(ImageType type) {
138 2761           switch (type) {
139             case D64:
140 2759           return(35);
141             break;
142             case D71:
143 1           return(70);
144             break;
145             case D81:
146 1           return(80);
147             break;
148             }
149 0           return(0);
150             }
151              
152              
153             /* return disk geometry for track */
154 16585           int di_sectors_per_track(ImageType type, int track) {
155 16585           switch (type) {
156             case D71:
157 2 100         if (track > 35) {
158 1           track -= 35;
159             }
160             // fall through
161             case D64:
162 16583 100         if (track < 18) {
163 8633           return(21);
164 7950 100         } else if (track < 25) {
165 3256           return(19);
166 4694 100         } else if (track < 31) {
167 2622           return(18);
168             } else {
169 2072           return(17);
170             }
171             break;
172             case D81:
173 2           return(40);
174             break;
175             }
176 0           return(0);
177             }
178              
179             /* convert track, sector to blocknum */
180 17703           int get_block_num(ImageType type, TrackSector ts) {
181             int block;
182              
183 17703           switch (type) {
184             case D64:
185 17703 100         if (ts.track < 18) {
186 18           block = (ts.track - 1) * 21;
187 17685 50         } else if (ts.track < 25) {
188 17685           block = (ts.track - 18) * 19 + 17 * 21;
189 0 0         } else if (ts.track < 31) {
190 0           block = (ts.track - 25) * 18 + 17 * 21 + 7 * 19;
191             } else {
192 0           block = (ts.track - 31) * 17 + 17 * 21 + 7 * 19 + 6 * 18;
193             }
194 17703           return(block + ts.sector);
195             break;
196             case D71:
197 0 0         if (ts.track > 35) {
198 0           block = 683;
199 0           ts.track -= 35;
200             } else {
201 0           block = 0;
202             }
203 0 0         if (ts.track < 18) {
204 0           block += (ts.track - 1) * 21;
205 0 0         } else if (ts.track < 25) {
206 0           block += (ts.track - 18) * 19 + 17 * 21;
207 0 0         } else if (ts.track < 31) {
208 0           block += (ts.track - 25) * 18 + 17 * 21 + 7 * 19;
209             } else {
210 0           block += (ts.track - 31) * 17 + 17 * 21 + 7 * 19 + 6 * 18;
211             }
212 0           return(block + ts.sector);
213             break;
214             case D81:
215 0           return((ts.track - 1) * 40 + ts.sector);
216             break;
217             }
218 0           return(0);
219             }
220              
221              
222             /* get a pointer to block data */
223 17703           unsigned char *get_ts_addr(DiskImage *di, TrackSector ts) {
224 17703           return(di->image + get_block_num(di->type, ts) * 256);
225             }
226              
227              
228             /* return a pointer to the next block in the chain */
229 53           TrackSector next_ts_in_chain(DiskImage *di, TrackSector ts) {
230             unsigned char *p;
231             TrackSector newts;
232              
233 53           p = get_ts_addr(di, ts);
234 53           newts.track = p[0];
235 53           newts.sector = p[1];
236 53 50         if (p[0] > di_tracks(di->type)) {
237 0           newts.track = 0;
238 0           newts.sector = 0;
239 53 100         } else if (p[1] > di_sectors_per_track(di->type, p[0])) {
240 17           newts.track = 0;
241 17           newts.sector = 0;
242             }
243 53           return(newts);
244             }
245              
246              
247             /* return a pointer to the disk title */
248 2           unsigned char *di_title(DiskImage *di) {
249 2 50         switch (di->type) {
250             default:
251             case D64:
252             case D71:
253 2           return(get_ts_addr(di, di->dir) + 144);
254             break;
255             case D81:
256 0           return(get_ts_addr(di, di->dir) + 4);
257             break;
258             }
259             }
260              
261              
262             /* return number of free blocks in track */
263 1771           int di_track_blocks_free(DiskImage *di, int track) {
264             unsigned char *bam;
265              
266 1771           switch (di->type) {
267             default:
268             case D64:
269 1771           bam = get_ts_addr(di, di->bam);
270 1771           break;
271             case D71:
272 0           bam = get_ts_addr(di, di->bam);
273 0 0         if (track >= 36) {
274 0           return(bam[track + 185]);
275             }
276 0           break;
277             case D81:
278 0 0         if (track <= 40) {
279 0           bam = get_ts_addr(di, di->bam);
280             } else {
281 0           bam = get_ts_addr(di, di->bam2);
282 0           track -= 40;
283             }
284 0           return(bam[track * 6 + 10]);
285             break;
286             }
287 1771           return(bam[track * 4]);
288             }
289              
290              
291             /* count number of free blocks */
292 52           int blocks_free(DiskImage *di) {
293             int track;
294 52           int blocks = 0;
295              
296 1872 100         for (track = 1; track <= di_tracks(di->type); ++track) {
297 1820 100         if (track != di->dir.track) {
298 1768           blocks += di_track_blocks_free(di, track);
299             }
300             }
301 52           return(blocks);
302             }
303              
304              
305             /* check if track, sector is free in BAM */
306 15           int di_is_ts_free(DiskImage *di, TrackSector ts) {
307             unsigned char mask;
308             unsigned char *bam;
309              
310 15           switch (di->type) {
311             case D64:
312 15           bam = get_ts_addr(di, di->bam);
313 15 50         if (bam[ts.track * 4]) {
314 15           mask = 1<<(ts.sector & 7);
315 15           return(bam[ts.track * 4 + ts.sector / 8 + 1] & mask ? 1 : 0);
316             } else {
317 0           return(0);
318             }
319             break;
320             case D71:
321 0           mask = 1<<(ts.sector & 7);
322 0 0         if (ts.track < 36) {
323 0           bam = get_ts_addr(di, di->bam);
324 0           return(bam[ts.track * 4 + ts.sector / 8 + 1] & mask ? 1 : 0);
325             } else {
326 0           bam = get_ts_addr(di, di->bam2);
327 0           return(bam[(ts.track - 35) * 3 + ts.sector / 8 - 3] & mask ? 1 : 0);
328             }
329             break;
330             case D81:
331 0           mask = 1<<(ts.sector & 7);
332 0 0         if (ts.track < 41) {
333 0           bam = get_ts_addr(di, di->bam);
334             } else {
335 0           bam = get_ts_addr(di, di->bam2);
336 0           ts.track -= 40;
337             }
338 0           return(bam[ts.track * 6 + ts.sector / 8 + 11] & mask ? 1 : 0);
339             break;
340             }
341 0           return(0);
342             }
343              
344              
345             /* allocate track, sector in BAM */
346 62           void di_alloc_ts(DiskImage *di, TrackSector ts) {
347             unsigned char mask;
348             unsigned char *bam;
349              
350 62           di->modified = 1;
351 62           switch (di->type) {
352             case D64:
353 62           bam = get_ts_addr(di, di->bam);
354 62           bam[ts.track * 4] -= 1;
355 62           mask = 1<<(ts.sector & 7);
356 62           bam[ts.track * 4 + ts.sector / 8 + 1] &= ~mask;
357 62           break;
358             case D71:
359 0           mask = 1<<(ts.sector & 7);
360 0 0         if (ts.track < 36) {
361 0           bam = get_ts_addr(di, di->bam);
362 0           bam[ts.track * 4] -= 1;
363 0           bam[ts.track * 4 + ts.sector / 8 + 1] &= ~mask;
364             } else {
365 0           bam = get_ts_addr(di, di->bam);
366 0           bam[ts.track + 185] -= 1;
367 0           bam = get_ts_addr(di, di->bam2);
368 0           bam[(ts.track - 35) * 3 + ts.sector / 8 - 3] &= ~mask;
369             }
370 0           break;
371             case D81:
372 0 0         if (ts.track < 41) {
373 0           bam = get_ts_addr(di, di->bam);
374             } else {
375 0           bam = get_ts_addr(di, di->bam2);
376 0           ts.track -= 40;
377             }
378 0           bam[ts.track * 6 + 10] -= 1;
379 0           mask = 1<<(ts.sector & 7);
380 0           bam[ts.track * 6 + ts.sector / 8 + 11] &= ~mask;
381 0           break;
382             }
383 62           }
384              
385              
386             /* allocate next available block */
387 12           TrackSector alloc_next_ts(DiskImage *di, TrackSector prevts) {
388             unsigned char *bam;
389             int spt, s1, s2, t1, t2, bpt, boff, res1, res2;
390             TrackSector ts;
391              
392 12           switch (di->type) {
393             default:
394             case D64:
395 12           s1 = 1;
396 12           t1 = 35;
397 12           s2 = 1;
398 12           t2 = 35;
399 12           res1 = 18;
400 12           res2 = 0;
401 12           bpt = 4;
402 12           boff = 0;
403 12           break;
404             case D71:
405 0           s1 = 1;
406 0           t1 = 35;
407 0           s2 = 36;
408 0           t2 = 70;
409 0           res1 = 18;
410 0           res2 = 53;
411 0           bpt = 4;
412 0           boff = 0;
413 0           break;
414             case D81:
415 0           s1 = 1;
416 0           t1 = 40;
417 0           s2 = 41;
418 0           t2 = 80;
419 0           res1 = 40;
420 0           res2 = 0;
421 0           bpt = 6;
422 0           boff = 10;
423 0           break;
424             }
425              
426 12           bam = get_ts_addr(di, di->bam);
427 12 50         for (ts.track = s1; ts.track <= t1; ++ts.track) {
428 12 50         if (ts.track != res1) {
429 12 50         if (bam[ts.track * bpt + boff]) {
430 12           spt = di_sectors_per_track(di->type, ts.track);
431 12           ts.sector = (prevts.sector + interleave(di->type)) % spt;
432 0           for (; ; ts.sector = (ts.sector + 1) % spt) {
433 12 50         if (di_is_ts_free(di, ts)) {
434 12           di_alloc_ts(di, ts);
435 12           return(ts);
436             }
437 0           }
438             }
439             }
440             }
441              
442 0 0         if (di->type == D71 || di->type == D81) {
    0          
443 0           bam = get_ts_addr(di, di->bam2);
444 0 0         for (ts.track = s2; ts.track <= t2; ++ts.track) {
445 0 0         if (ts.track != res2) {
446 0 0         if (bam[(ts.track - t1) * bpt + boff]) {
447 0           spt = di_sectors_per_track(di->type, ts.track);
448 0           ts.sector = (prevts.sector + interleave(di->type)) % spt;
449 0           for (; ; ts.sector = (ts.sector + 1) % spt) {
450 0 0         if (di_is_ts_free(di, ts)) {
451 0           di_alloc_ts(di, ts);
452 0           return(ts);
453             }
454 0           }
455             }
456             }
457             }
458             }
459              
460 0           ts.track = 0;
461 0           ts.sector = 0;
462 12           return(ts);
463             }
464              
465              
466             /* allocate next available directory block */
467 0           TrackSector alloc_next_dir_ts(DiskImage *di) {
468             unsigned char *p;
469             int spt;
470             TrackSector ts, lastts;
471              
472 0 0         if (di_track_blocks_free(di, di->bam.track)) {
473 0           ts.track = di->bam.track;
474 0           ts.sector = 0;
475 0 0         while (ts.track) {
476 0           lastts = ts;
477 0           ts = next_ts_in_chain(di, ts);
478             }
479 0           ts.track = lastts.track;
480 0           ts.sector = lastts.sector + 3;
481 0           spt = di_sectors_per_track(di->type, ts.track);
482 0           for (; ; ts.sector = (ts.sector + 1) % spt) {
483 0 0         if (di_is_ts_free(di, ts)) {
484 0           di_alloc_ts(di, ts);
485 0           p = get_ts_addr(di, lastts);
486 0           p[0] = ts.track;
487 0           p[1] = ts.sector;
488 0           p = get_ts_addr(di, ts);
489 0           memset(p, 0, 256);
490 0           p[1] = 0xff;
491 0           di->modified = 1;
492 0           return(ts);
493             }
494 0           }
495             } else {
496 0           ts.track = 0;
497 0           ts.sector = 0;
498 0           return(ts);
499             }
500             }
501              
502              
503             /* free a block in the BAM */
504 15712           void di_free_ts(DiskImage *di, TrackSector ts) {
505             unsigned char mask;
506             unsigned char *bam;
507              
508 15712           di->modified = 1;
509 15712           switch (di->type) {
510             case D64:
511 15712           mask = 1<<(ts.sector & 7);
512 15712           bam = get_ts_addr(di, di->bam);
513 15712           bam[ts.track * 4 + ts.sector / 8 + 1] |= mask;
514 15712           bam[ts.track * 4] += 1;
515 15712           break;
516             case D71:
517 0           mask = 1<<(ts.sector & 7);
518 0 0         if (ts.track < 36) {
519 0           bam = get_ts_addr(di, di->bam);
520 0           bam[ts.track * 4 + ts.sector / 8 + 1] |= mask;
521 0           bam[ts.track * 4] += 1;
522             } else {
523 0           bam = get_ts_addr(di, di->bam);
524 0           bam[ts.track + 185] += 1;
525 0           bam = get_ts_addr(di, di->bam2);
526 0           bam[(ts.track - 35) * 3 + ts.sector / 8 - 3] |= mask;
527             }
528 0           break;
529             case D81:
530 0 0         if (ts.track < 41) {
531 0           bam = get_ts_addr(di, di->bam);
532             } else {
533 0           bam = get_ts_addr(di, di->bam2);
534 0           ts.track -= 40;
535             }
536 0           mask = 1<<(ts.sector & 7);
537 0           bam[ts.track * 6 + ts.sector / 8 + 11] |= mask;
538 0           bam[ts.track * 6 + 10] += 1;
539 0           break;
540             default:
541 0           break;
542             }
543 15712           }
544              
545              
546             /* free a chain of blocks */
547 1           void free_chain(DiskImage *di, TrackSector ts) {
548 2 100         while (ts.track) {
549 1           di_free_ts(di, ts);
550 1           ts = next_ts_in_chain(di, ts);
551             }
552 1           }
553              
554              
555 1           DiskImage *di_load_image(char *name) {
556             FILE *file;
557             int filesize, l, read;
558             DiskImage *di;
559              
560             /* open image */
561 1 50         if ((file = fopen(name, "rb")) == NULL) {
562             //puts("fopen failed");
563 0           return(NULL);
564             }
565              
566             /* get file size*/
567 1 50         if (fseek(file, 0, SEEK_END)) {
568             //puts("fseek failed");
569 0           fclose(file);
570 0           return(NULL);
571             }
572 1           filesize = ftell(file);
573 1           fseek(file, 0, SEEK_SET);
574              
575 1 50         if ((di = malloc(sizeof(*di))) == NULL) {
576             //puts("malloc failed");
577 0           fclose(file);
578 0           return(NULL);
579             }
580              
581             /* check image type */
582 1           switch (filesize) {
583             case 174848: // standard D64
584             case 175531: // D64 with error info (which we just ignore)
585 1           di->type = D64;
586 1           di->bam.track = 18;
587 1           di->bam.sector = 0;
588 1           di->dir = di->bam;
589 1           break;
590             case 349696:
591 0           di->type = D71;
592 0           di->bam.track = 18;
593 0           di->bam.sector = 0;
594 0           di->bam2.track = 53;
595 0           di->bam2.sector = 0;
596 0           di->dir = di->bam;
597 0           break;
598             case 819200:
599 0           di->type = D81;
600 0           di->bam.track = 40;
601 0           di->bam.sector = 1;
602 0           di->bam2.track = 40;
603 0           di->bam2.sector = 2;
604 0           di->dir.track = 40;
605 0           di->dir.sector = 0;
606 0           break;
607             default:
608             //puts("unknown type");
609 0           free(di);
610 0           fclose(file);
611 0           return(NULL);
612             }
613              
614 1           di->size = filesize;
615              
616             /* allocate buffer for image */
617 1 50         if ((di->image = malloc(filesize)) == NULL) {
618             //puts("image malloc failed");
619 0           free(di);
620 0           fclose(file);
621 0           return(NULL);
622             }
623              
624             /* read file into buffer */
625 1           read = 0;
626 2 100         while (read < filesize) {
627 1 50         if ((l = fread(di->image, 1, filesize - read, file))) {
628 1           read += l;
629             } else {
630             //puts("fread failed");
631 0           free(di->image);
632 0           free(di);
633 0           fclose(file);
634 0           return(NULL);
635             }
636             }
637              
638 1           di->filename = malloc(strlen(name) + 1);
639 1           strcpy(di->filename, name);
640 1           di->openfiles = 0;
641 1           di->blocksfree = blocks_free(di);
642 1           di->modified = 0;
643 1           fclose(file);
644 1           set_status(di, 254, 0, 0);
645 1           return(di);
646             }
647              
648              
649 28           DiskImage *di_create_image(char *name, int size) {
650             DiskImage *di;
651              
652 28 50         if ((di = malloc(sizeof(*di))) == NULL) {
653             //puts("malloc failed");
654 0           return(NULL);
655             }
656              
657             /* check image type */
658 28           switch (size) {
659             case 174848: // standard D64
660             case 175531: // D64 with error info (which we just ignore)
661 28           di->type = D64;
662 28           di->bam.track = 18;
663 28           di->bam.sector = 0;
664 28           di->dir = di->bam;
665 28           break;
666             case 349696:
667 0           di->type = D71;
668 0           di->bam.track = 18;
669 0           di->bam.sector = 0;
670 0           di->bam2.track = 53;
671 0           di->bam2.sector = 0;
672 0           di->dir = di->bam;
673 0           break;
674             case 819200:
675 0           di->type = D81;
676 0           di->bam.track = 40;
677 0           di->bam.sector = 1;
678 0           di->bam2.track = 40;
679 0           di->bam2.sector = 2;
680 0           di->dir.track = 40;
681 0           di->dir.sector = 0;
682 0           break;
683             default:
684             //puts("unknown type");
685 0           free(di);
686 0           return(NULL);
687             }
688              
689 28           di->size = size;
690              
691             /* allocate buffer for image */
692 28 50         if ((di->image = malloc(size)) == NULL) {
693             //puts("image malloc failed");
694 0           free(di);
695 0           return(NULL);
696             }
697 28           memset(di->image, 0, size);
698              
699 28           di->filename = malloc(strlen(name) + 1);
700 28           strcpy(di->filename, name);
701 28           di->openfiles = 0;
702 28           di->blocksfree = blocks_free(di);
703 28           di->modified = 1;
704 28           set_status(di, 254, 0, 0);
705 28           return(di);
706             }
707              
708              
709 28           void di_sync(DiskImage *di) {
710             FILE *file;
711             int l, left;
712             unsigned char *image;
713              
714 28 50         if ((file = fopen(di->filename, "wb"))) {
715 28           image = di->image;
716 28           left = di->size;
717 28           l = 0;
718 56 100         while (left) {
719 28 50         if ((l = fwrite(image, 1, left, file)) == 0) {
720 0           fclose(file);
721 0           return;
722             }
723 28           left -= l;
724 28           image += l;
725             }
726 28           fclose(file);
727 28           di->modified = 0;
728             }
729             }
730              
731              
732 29           void di_free_image(DiskImage *di) {
733 29 100         if (di->modified) {
734 27           di_sync(di);
735             }
736 29 50         if (di->filename) {
737 29           free(di->filename);
738             }
739 29           free(di->image);
740 29           free(di);
741 29           }
742              
743              
744 9           int match_pattern(unsigned char *rawpattern, unsigned char *rawname) {
745             int i;
746              
747 76 50         for (i = 0; i < 16; ++i) {
748 76 50         if (rawpattern[i] == '*') {
749 0           return(1);
750             }
751 76 100         if (rawname[i] == 0xa0) {
752 8 50         if (rawpattern[i] == 0xa0) {
753 8           return(1);
754             } else {
755 0           return(0);
756             }
757             } else {
758 68 50         if (rawpattern[i] == '?' || rawpattern[i] == rawname[i]) {
    100          
759             } else {
760 1           return(0);
761             }
762             }
763             }
764 0           return(1);
765             }
766              
767              
768 12           RawDirEntry *find_file_entry(DiskImage *di, unsigned char *rawpattern, FileType type) {
769             unsigned char *buffer;
770             TrackSector ts;
771             RawDirEntry *rde;
772             int offset;
773              
774 12           ts = next_ts_in_chain(di, di->bam);
775 16 100         while (ts.track) {
776 12           buffer = get_ts_addr(di, ts);
777 44 100         for (offset = 0; offset < 256; offset += 32) {
778 40           rde = (RawDirEntry *)(buffer + offset);
779 40 100         if ((rde->type & ~0x40) == (type | 0x80)) {
780 9 100         if (match_pattern(rawpattern, rde->rawname)) {
781 8           return(rde);
782             }
783             }
784             }
785 4           ts = next_ts_in_chain(di, ts);
786             }
787 12           return(NULL);
788             }
789              
790              
791 12           RawDirEntry *alloc_file_entry(DiskImage *di, unsigned char *rawname, FileType type) {
792             unsigned char *buffer;
793             TrackSector ts, lastts;
794             RawDirEntry *rde;
795             int offset;
796              
797             /* check if file already exists */
798 12           ts = next_ts_in_chain(di, di->bam);
799 24 100         while (ts.track) {
800 12           buffer = get_ts_addr(di, ts);
801 108 100         for (offset = 0; offset < 256; offset += 32) {
802 96           rde = (RawDirEntry *)(buffer + offset);
803 96 50         if (rde->type) {
804 0 0         if (strncmp(rawname, rde->rawname, 16) == 0) {
805 0           set_status(di, 63, 0, 0);
806             //puts("file exists");
807 0           return(NULL);
808             }
809             }
810             }
811 12           ts = next_ts_in_chain(di, ts);
812             }
813              
814             /* allocate empty slot */
815 12           ts = next_ts_in_chain(di, di->bam);
816 12 50         while (ts.track) {
817 12           buffer = get_ts_addr(di, ts);
818 12 50         for (offset = 0; offset < 256; offset += 32) {
819 12           rde = (RawDirEntry *)(buffer + offset);
820 12 50         if (rde->type == 0) {
821 12           memset((unsigned char *)rde + 2, 0, 30);
822 12           memcpy(rde->rawname, rawname, 16);
823 12           rde->type = type;
824 12           di->modified = 1;
825 12           return(rde);
826             }
827             }
828 0           lastts = ts;
829 0           ts = next_ts_in_chain(di, ts);
830             }
831              
832             /* allocate new dir block */
833 0           ts = alloc_next_dir_ts(di);
834 0 0         if (ts.track) {
835 0           rde = (RawDirEntry *)get_ts_addr(di, ts);
836 0           memset((unsigned char *)rde + 2, 0, 30);
837 0           memcpy(rde->rawname, rawname, 16);
838 0           rde->type = type;
839 0           di->modified = 1;
840 0           return(rde);
841             } else {
842 0           set_status(di, 72, 0, 0);
843             //puts("directory full");
844 12           return(NULL);
845             }
846             }
847              
848              
849             /* open a file */
850 20           ImageFile *di_open(DiskImage *di, unsigned char *rawname, FileType type, char *mode) {
851             ImageFile *imgfile;
852             RawDirEntry *rde;
853             unsigned char *p;
854              
855 20           set_status(di, 255, 0, 0);
856              
857 20 100         if (strcmp("rb", mode) == 0) {
858              
859 8 50         if ((imgfile = malloc(sizeof(*imgfile))) == NULL) {
860             //puts("malloc failed");
861 0           return(NULL);
862             }
863 8 50         if (strcmp("$", rawname) == 0) {
864 0           imgfile->mode = 'r';
865 0           imgfile->ts = di->dir;
866 0           p = get_ts_addr(di, di->dir);
867 0           imgfile->buffer = p + 2;
868 0 0         if ((di->type == D64) || (di->type == D71)) {
    0          
869 0           imgfile->nextts.track = 18; // 1541/71 ignores bam t/s link
870 0           imgfile->nextts.sector = 1;
871             } else {
872 0           imgfile->nextts.track = p[0];
873 0           imgfile->nextts.sector = p[1];
874             }
875 0           imgfile->buflen = 254;
876 0           rde = NULL;
877             } else {
878 8 100         if ((rde = find_file_entry(di, rawname, type)) == NULL) {
879 3           set_status(di, 62, 0, 0);
880             //puts("find_file_entry failed");
881 3           free(imgfile);
882 3           return(NULL);
883             }
884 5           imgfile->mode = 'r';
885 5           imgfile->ts = rde->startts;
886 5 50         if (imgfile->ts.track > di_tracks(di->type)) {
887 0           return(NULL);
888             }
889 5           p = get_ts_addr(di, rde->startts);
890 5           imgfile->buffer = p + 2;
891 5           imgfile->nextts.track = p[0];
892 5           imgfile->nextts.sector = p[1];
893 5 50         if (imgfile->nextts.track == 0) {
894 5           imgfile->buflen = imgfile->nextts.sector - 1;
895             } else {
896 5           imgfile->buflen = 254;
897             }
898             }
899              
900 12 50         } else if (strcmp("wb", mode) == 0) {
901              
902 12 50         if ((rde = alloc_file_entry(di, rawname, type)) == NULL) {
903             //puts("alloc_file_entry failed");
904 0           return(NULL);
905             }
906 12 50         if ((imgfile = malloc(sizeof(*imgfile))) == NULL) {
907             //puts("malloc failed");
908 0           return(NULL);
909             }
910 12           imgfile->mode = 'w';
911 12           imgfile->ts.track = 0;
912 12           imgfile->ts.sector = 0;
913 12 50         if ((imgfile->buffer = malloc(254)) == NULL) {
914 0           free(imgfile);
915             //puts("malloc failed");
916 0           return(NULL);
917             }
918 12           imgfile->buflen = 254;
919 12           di->modified = 1;
920              
921             } else {
922 0           return(NULL);
923             }
924              
925 17           imgfile->diskimage = di;
926 17           imgfile->rawdirentry = rde;
927 17           imgfile->position = 0;
928 17           imgfile->bufptr = 0;
929              
930 17           ++(di->openfiles);
931 17           set_status(di, 0, 0, 0);
932 17           return(imgfile);
933             }
934              
935              
936 3           int di_read(ImageFile *imgfile, unsigned char *buffer, int len) {
937             unsigned char *p;
938             int bytesleft;
939 3           int counter = 0;
940              
941 6 100         while (len) {
942 5           bytesleft = imgfile->buflen - imgfile->bufptr;
943 5 100         if (bytesleft == 0) {
944 2 50         if (imgfile->nextts.track == 0) {
945 2           return(counter);
946             }
947 0 0         if (((imgfile->diskimage->type == D64) || (imgfile->diskimage->type == D71)) && imgfile->ts.track == 18 && imgfile->ts.sector == 0) {
    0          
    0          
    0          
948 0           imgfile->ts.track = 18;
949 0           imgfile->ts.sector = 1;
950             } else {
951 0           imgfile->ts = next_ts_in_chain(imgfile->diskimage, imgfile->ts);
952             }
953 0 0         if (imgfile->ts.track == 0) {
954 0           return(counter);
955             }
956 0           p = get_ts_addr(imgfile->diskimage, imgfile->ts);
957 0           imgfile->buffer = p + 2;
958 0           imgfile->nextts.track = p[0];
959 0           imgfile->nextts.sector = p[1];
960 0 0         if (imgfile->nextts.track == 0) {
961 0           imgfile->buflen = imgfile->nextts.sector - 1;
962             } else {
963 0           imgfile->buflen = 254;
964             }
965 0           imgfile->bufptr = 0;
966             } else {
967 3 100         if (len >= bytesleft) {
968 510 100         while (bytesleft) {
969 508           *buffer++ = imgfile->buffer[imgfile->bufptr++];
970 508           --len;
971 508           --bytesleft;
972 508           ++counter;
973 508           ++(imgfile->position);
974             }
975             } else {
976 6 100         while (len) {
977 5           *buffer++ = imgfile->buffer[imgfile->bufptr++];
978 5           --len;
979 5           --bytesleft;
980 5           ++counter;
981 5           ++(imgfile->position);
982             }
983             }
984             }
985             }
986 1           return(counter);
987             }
988              
989              
990 12           int di_write(ImageFile *imgfile, unsigned char *buffer, int len) {
991             unsigned char *p;
992             int bytesleft;
993 12           int counter = 0;
994              
995 24 100         while (len) {
996 12           bytesleft = imgfile->buflen - imgfile->bufptr;
997 12 50         if (bytesleft == 0) {
998 0 0         if (imgfile->diskimage->blocksfree == 0) {
999 0           set_status(imgfile->diskimage, 72, 0, 0);
1000 0           return(counter);
1001             }
1002 0           imgfile->nextts = alloc_next_ts(imgfile->diskimage, imgfile->ts);
1003 0 0         if (imgfile->ts.track == 0) {
1004 0           imgfile->rawdirentry->startts = imgfile->nextts;
1005             } else {
1006 0           p = get_ts_addr(imgfile->diskimage, imgfile->ts);
1007 0           p[0] = imgfile->nextts.track;
1008 0           p[1] = imgfile->nextts.sector;
1009             }
1010 0           imgfile->ts = imgfile->nextts;
1011 0           p = get_ts_addr(imgfile->diskimage, imgfile->ts);
1012 0           p[0] = 0;
1013 0           p[1] = 0xff;
1014 0           memcpy(p + 2, imgfile->buffer, 254);
1015 0           imgfile->bufptr = 0;
1016 0 0         if (++(imgfile->rawdirentry->sizelo) == 0) {
1017 0           ++(imgfile->rawdirentry->sizehi);
1018             }
1019 0           --(imgfile->diskimage->blocksfree);
1020             } else {
1021 12 50         if (len >= bytesleft) {
1022 0 0         while (bytesleft) {
1023 0           imgfile->buffer[imgfile->bufptr++] = *buffer++;
1024 0           --len;
1025 0           --bytesleft;
1026 0           ++counter;
1027 0           ++(imgfile->position);
1028             }
1029             } else {
1030 110 100         while (len) {
1031 98           imgfile->buffer[imgfile->bufptr++] = *buffer++;
1032 98           --len;
1033 98           --bytesleft;
1034 98           ++counter;
1035 98           ++(imgfile->position);
1036             }
1037             }
1038             }
1039             }
1040 12           return(counter);
1041             }
1042              
1043              
1044 16           void di_close(ImageFile *imgfile) {
1045             unsigned char *p;
1046              
1047 16 100         if (imgfile->mode == 'w') {
1048 12 50         if (imgfile->bufptr) {
1049 12 50         if (imgfile->diskimage->blocksfree) {
1050 12           imgfile->nextts = alloc_next_ts(imgfile->diskimage, imgfile->ts);
1051 12 50         if (imgfile->ts.track == 0) {
1052 12           imgfile->rawdirentry->startts = imgfile->nextts;
1053             } else {
1054 0           p = get_ts_addr(imgfile->diskimage, imgfile->ts);
1055 0           p[0] = imgfile->nextts.track;
1056 0           p[1] = imgfile->nextts.sector;
1057             }
1058 12           imgfile->ts = imgfile->nextts;
1059 12           p = get_ts_addr(imgfile->diskimage, imgfile->ts);
1060 12           p[0] = 0;
1061 12           p[1] = 0xff;
1062 12           memcpy(p + 2, imgfile->buffer, 254);
1063 12           imgfile->bufptr = 0;
1064 12 50         if (++(imgfile->rawdirentry->sizelo) == 0) {
1065 0           ++(imgfile->rawdirentry->sizehi);
1066             }
1067 12           --(imgfile->diskimage->blocksfree);
1068 12           imgfile->rawdirentry->type |= 0x80;
1069             }
1070             } else {
1071 0           imgfile->rawdirentry->type |= 0x80;
1072             }
1073 12           free(imgfile->buffer);
1074             }
1075 16           --(imgfile->diskimage->openfiles);
1076 16           free(imgfile);
1077 16           }
1078              
1079              
1080 23           int di_format(DiskImage *di, unsigned char *rawname, unsigned char *rawid) {
1081             unsigned char *p;
1082             TrackSector ts;
1083              
1084 23           di->modified = 1;
1085              
1086 23           switch (di->type) {
1087              
1088             case D64:
1089             /* erase disk */
1090 23 50         if (rawid) {
1091 23           memset(di->image, 0, 174848);
1092             }
1093              
1094             /* get ptr to bam */
1095 23           p = get_ts_addr(di, di->bam);
1096              
1097             /* setup header */
1098 23           p[0] = 18;
1099 23           p[1] = 1;
1100 23           p[2] = 'A';
1101 23           p[3] = 0;
1102              
1103             /* clear bam */
1104 23           memset(p + 4, 0, 0x8c);
1105              
1106             /* free blocks */
1107 828 100         for (ts.track = 1; ts.track <= di_tracks(di->type); ++ts.track) {
1108 16514 100         for (ts.sector = 0; ts.sector < di_sectors_per_track(di->type, ts.track); ++ts.sector) {
1109 15709           di_free_ts(di, ts);
1110             }
1111             }
1112              
1113             /* allocate bam and dir */
1114 23           ts.track = 18;
1115 23           ts.sector = 0;
1116 23           di_alloc_ts(di, ts);
1117 23           ts.sector = 1;
1118 23           di_alloc_ts(di, ts);
1119              
1120             /* copy name */
1121 23           memcpy(p + 0x90, rawname, 16);
1122              
1123             /* set id */
1124 23           memset(p + 0xa0, 0xa0, 2);
1125 23 50         if (rawid) {
1126 23           memcpy(p + 0xa2, rawid, 2);
1127             }
1128 23           memset(p + 0xa4, 0xa0, 7);
1129 23           p[0xa5] = '2';
1130 23           p[0xa6] = 'A';
1131              
1132             /* clear unused bytes */
1133 23           memset(p + 0xab, 0, 0x55);
1134              
1135             /* clear first dir block */
1136 23           memset(p + 256, 0, 256);
1137 23           p[257] = 0xff;
1138 23           break;
1139              
1140             case D71:
1141             /* erase disk */
1142 0 0         if (rawid) {
1143 0           memset(di->image, 0, 349696);
1144             }
1145              
1146             /* get ptr to bam2 */
1147 0           p = get_ts_addr(di, di->bam2);
1148              
1149             /* clear bam2 */
1150 0           memset(p, 0, 256);
1151              
1152             /* get ptr to bam */
1153 0           p = get_ts_addr(di, di->bam);
1154              
1155             /* setup header */
1156 0           p[0] = 18;
1157 0           p[1] = 1;
1158 0           p[2] = 'A';
1159 0           p[3] = 0x80;
1160              
1161             /* clear bam */
1162 0           memset(p + 4, 0, 0x8c);
1163              
1164             /* clear bam2 counters */
1165 0           memset(p + 0xdd, 0, 0x23);
1166              
1167             /* free blocks */
1168 0 0         for (ts.track = 1; ts.track <= di_tracks(di->type); ++ts.track) {
1169 0 0         if (ts.track != 53) {
1170 0 0         for (ts.sector = 0; ts.sector < di_sectors_per_track(di->type, ts.track); ++ts.sector) {
1171 0           di_free_ts(di, ts);
1172             }
1173             }
1174             }
1175              
1176             /* allocate bam and dir */
1177 0           ts.track = 18;
1178 0           ts.sector = 0;
1179 0           di_alloc_ts(di, ts);
1180 0           ts.sector = 1;
1181 0           di_alloc_ts(di, ts);
1182              
1183             /* copy name */
1184 0           memcpy(p + 0x90, rawname, 16);
1185              
1186             /* set id */
1187 0           memset(p + 0xa0, 0xa0, 2);
1188 0 0         if (rawid) {
1189 0           memcpy(p + 0xa2, rawid, 2);
1190             }
1191 0           memset(p + 0xa4, 0xa0, 7);
1192 0           p[0xa5] = '2';
1193 0           p[0xa6] = 'A';
1194              
1195             /* clear unused bytes */
1196 0           memset(p + 0xab, 0, 0x32);
1197              
1198             /* clear first dir block */
1199 0           memset(p + 256, 0, 256);
1200 0           p[257] = 0xff;
1201 0           break;
1202              
1203             case D81:
1204             /* erase disk */
1205 0 0         if (rawid) {
1206 0           memset(di->image, 0, 819200);
1207             }
1208              
1209             /* get ptr to bam */
1210 0           p = get_ts_addr(di, di->bam);
1211              
1212             /* setup header */
1213 0           p[0] = 0x28;
1214 0           p[1] = 0x02;
1215 0           p[2] = 0x44;
1216 0           p[3] = 0xbb;
1217 0           p[6] = 0xc0;
1218              
1219             /* set id */
1220 0 0         if (rawid) {
1221 0           memcpy(p + 4, rawid, 2);
1222             }
1223              
1224             /* clear bam */
1225 0           memset(p + 7, 0, 0xfa);
1226              
1227             /* get ptr to bam2 */
1228 0           p = get_ts_addr(di, di->bam2);
1229              
1230             /* setup header */
1231 0           p[0] = 0x00;
1232 0           p[1] = 0xff;
1233 0           p[2] = 0x44;
1234 0           p[3] = 0xbb;
1235 0           p[6] = 0xc0;
1236              
1237             /* set id */
1238 0 0         if (rawid) {
1239 0           memcpy(p + 4, rawid, 2);
1240             }
1241              
1242             /* clear bam2 */
1243 0           memset(p + 7, 0, 0xfa);
1244              
1245             /* free blocks */
1246 0 0         for (ts.track = 1; ts.track <= di_tracks(di->type); ++ts.track) {
1247 0 0         for (ts.sector = 0; ts.sector < di_sectors_per_track(di->type, ts.track); ++ts.sector) {
1248 0           di_free_ts(di, ts);
1249             }
1250             }
1251              
1252             /* allocate bam and dir */
1253 0           ts.track = 40;
1254 0           ts.sector = 0;
1255 0           di_alloc_ts(di, ts);
1256 0           ts.sector = 1;
1257 0           di_alloc_ts(di, ts);
1258 0           ts.sector = 2;
1259 0           di_alloc_ts(di, ts);
1260 0           ts.sector = 3;
1261 0           di_alloc_ts(di, ts);
1262              
1263             /* get ptr to dir */
1264 0           p = get_ts_addr(di, di->dir);
1265              
1266             /* copy name */
1267 0           memcpy(p + 4, rawname, 16);
1268              
1269             /* set id */
1270 0           memset(p + 0x14, 0xa0, 2);
1271 0 0         if (rawid) {
1272 0           memcpy(p + 0x16, rawid, 2);
1273             }
1274 0           memset(p + 0x18, 0xa0, 5);
1275 0           p[0x19] = '3';
1276 0           p[0x1a] = 'D';
1277              
1278             /* clear unused bytes */
1279 0           memset(p + 0x1d, 0, 0xe3);
1280              
1281             /* clear first dir block */
1282 0           memset(p + 768, 0, 256);
1283 0           p[769] = 0xff;
1284 0           break;
1285              
1286             }
1287              
1288 23           di->blocksfree = blocks_free(di);
1289              
1290 23           return(set_status(di, 0, 0, 0));
1291             }
1292              
1293              
1294 1           int di_delete(DiskImage *di, unsigned char *rawpattern, FileType type) {
1295             RawDirEntry *rde;
1296 1           int delcount = 0;
1297              
1298 1 50         switch (type) {
1299              
1300             case T_SEQ:
1301             case T_PRG:
1302             case T_USR:
1303 2 100         while ((rde = find_file_entry(di, rawpattern, type))) {
1304 1           free_chain(di, rde->startts);
1305 1           rde->type = 0;
1306 1           ++delcount;
1307             }
1308 1 50         if (delcount) {
1309 1           return(set_status(di, 1, delcount, 0));
1310             } else {
1311 0           return(set_status(di, 62, 0, 0));
1312             }
1313             break;
1314              
1315             default:
1316 0           return(set_status(di, 64, 0, 0));
1317             break;
1318              
1319             }
1320             }
1321              
1322              
1323 2           int di_rename(DiskImage *di, unsigned char *oldrawname, unsigned char *newrawname, FileType type) {
1324             RawDirEntry *rde;
1325              
1326 2 50         if ((rde = find_file_entry(di, oldrawname, type))) {
1327 2           memcpy(rde->rawname, newrawname, 16);
1328 2           return(set_status(di, 0, 0, 0));
1329             } else {
1330 0           return(set_status(di, 62, 0, 0));
1331             }
1332             }