1 /***************************************************************************
2 * *
3 * LIBDSK: General floppy and diskimage access library *
4 * Copyright (C) 2001-2, 2016 John Elliott <seasip.webmaster@gmail.com>*
5 * *
6 * This library is free software; you can redistribute it and/or *
7 * modify it under the terms of the GNU Library General Public *
8 * License as published by the Free Software Foundation; either *
9 * version 2 of the License, or (at your option) any later version. *
10 * *
11 * This library is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
14 * Library General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU Library General Public *
17 * License along with this library; if not, write to the Free *
18 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, *
19 * MA 02111-1307, USA *
20 * *
21 ***************************************************************************/
22
23 /* This driver works with the ImageDisk "IMD" format. This compresses
24 * individual sectors using RLE, so we have to load the whole image into
25 * memory and work on it as an array of sectors.
26 */
27
28 #include <stdio.h>
29 #include "libdsk.h"
30 #include "drvi.h"
31 #include "drvimd.h"
32 #ifdef HAVE_TIME_H
33 #include <time.h>
34 #endif
35
36 #define ST_NODATA 0 /* ID but no data */
37 #define ST_NORMAL 1 /* Normal data, uncompressed */
38 #define ST_CNORMAL 2 /* Normal data, compressed */
39 #define ST_DELETED 3 /* Deleted data, uncompressed */
40 #define ST_CDELETED 4 /* Deleted data, compressed */
41 #define ST_DATAERR 5 /* Normal data, uncompressed, data error on read */
42 #define ST_CDATAERR 6 /* Normal data, compressed, data error on read */
43 #define ST_DELERR 7 /* Deleted data, uncompressed, data error on read */
44 #define ST_CDELERR 8 /* Deleted data, compressed, data error on read */
45
46 /* This struct contains function pointers to the driver's functions, and the
47 * size of its DSK_DRIVER subclass */
48
49 DRV_CLASS dc_imd =
50 {
51 sizeof(IMD_DSK_DRIVER),
52 "imd",
53 "IMD file driver",
54 imd_open, /* open */
55 imd_creat, /* create new */
56 imd_close, /* close */
57 imd_read, /* read sector, working from physical address */
58 imd_write, /* write sector, working from physical address */
59 imd_format, /* format track, physical */
60 imd_getgeom, /* get geometry */
61 imd_secid, /* sector ID */
62 imd_xseek, /* seek to track */
63 imd_status, /* drive status */
64 imd_xread, /* Extended sector read */
65 imd_xwrite, /* Extended sector write */
66 };
67
68
imd_alloc_track(int sectors)69 static IMD_TRACK *imd_alloc_track(int sectors)
70 {
71 int n;
72
73 IMD_TRACK *t = dsk_malloc(sizeof(IMD_TRACK) +
74 sectors * sizeof(IMD_SECTOR *));
75
76 if (!t) return NULL;
77 for (n = 0; n < sectors; n++)
78 {
79 t->imdt_sec[n] = NULL;
80 }
81 return t;
82 }
83
imd_free_track(IMD_TRACK * t)84 static void imd_free_track(IMD_TRACK *t)
85 {
86 int n;
87
88 if (!t) return;
89
90 for (n = 0; n < t->imdt_sectors; n++)
91 {
92 if (t->imdt_sec[n]) dsk_free(t->imdt_sec[n]);
93 }
94 dsk_free(t);
95 }
96
97 /* For reading strings from the file: Read up to the next occurrence of
98 * either of the specified characters c1 / c2, and return how many bytes
99 * that is (including the terminator).
100 *
101 * If only interested in one terminating character, pass c2 = c1
102 */
imd_readto(FILE * fp,char c1,char c2,int * count,int * termch)103 static dsk_err_t imd_readto(FILE *fp, char c1, char c2, int *count, int *termch)
104 {
105 int ch;
106 long pos = ftell(fp);
107 int cnt = 0;
108
109 *termch = EOF;
110 if (pos < 0)
111 {
112 return DSK_ERR_SYSERR;
113 }
114 while (1)
115 {
116 ++cnt;
117 ch = fgetc(fp);
118 if (ch == EOF || ch == c1 || ch == c2)
119 {
120 *termch = ch;
121 break;
122 }
123 }
124 if (fseek(fp, pos, SEEK_SET))
125 {
126 return DSK_ERR_SYSERR;
127 }
128 *count = cnt;
129 return DSK_ERR_OK;
130 }
131
132 /* Ensure there are always at least 'count' + 1 tracks in the
133 * self->imd_tracks array */
imd_ensure_trackcount(IMD_DSK_DRIVER * self,dsk_ltrack_t trk)134 static dsk_err_t imd_ensure_trackcount(IMD_DSK_DRIVER *self, dsk_ltrack_t trk)
135 {
136 IMD_TRACK **ptr;
137 unsigned n, newc;
138
139 if (trk < self->imd_ntracks) return DSK_ERR_OK;
140
141 /* Need to malloc some more */
142 if (self->imd_ntracks == 0)
143 {
144 newc = 20;
145 }
146 else
147 {
148 newc = 2 * self->imd_ntracks;
149 }
150 ptr = dsk_malloc(newc * sizeof(IMD_TRACK *));
151 if (!ptr) return DSK_ERR_NOMEM;
152
153 /* Copy existing array (if any) */
154 for (n = 0; n < self->imd_ntracks; n++)
155 ptr[n] = self->imd_tracks[n];
156
157 /* Blank additional pointers */
158 for (n = self->imd_ntracks; n < newc; n++)
159 ptr[n] = NULL;
160
161 dsk_free(self->imd_tracks);
162 self->imd_tracks = ptr;
163 self->imd_ntracks = newc;
164 return DSK_ERR_OK;
165 }
166
167
168
imd_load_track(IMD_DSK_DRIVER * self,dsk_ltrack_t count,FILE * fp)169 static dsk_err_t imd_load_track(IMD_DSK_DRIVER *self, dsk_ltrack_t count,
170 FILE *fp)
171 {
172 /* Start by loading the track header: Fixed */
173 IMD_TRACK tmp, *trkh;
174 IMD_SECTOR *tmpsec;
175 dsk_err_t err;
176 int n, c;
177
178 /* printf("Loading track %ld, offset %ld \n", count, ftell(fp)); */
179 if (fread(&tmp.imdt_mode, 1, 4, fp) < 4)
180 {
181 return DSK_ERR_OVERRUN; /* EOF */
182 }
183 c = fgetc(fp);
184 if (c == EOF)
185 {
186 return DSK_ERR_OVERRUN; /* EOF */
187 }
188 if (c == 0xFF) tmp.imdt_seclen = 0xFFFF;
189 else tmp.imdt_seclen = (128 << c);
190 /* printf("Mode %d Cyl %d Head %d Secs %d Size %d\n",
191 tmp.imdt_mode, tmp.imdt_cylinder, tmp.imdt_head,
192 tmp.imdt_sectors, tmp.imdt_seclen); */
193
194 err = imd_ensure_trackcount(self, count);
195 if (err) return err;
196
197 /* Allocate the full variable-size structure */
198 trkh = imd_alloc_track(tmp.imdt_sectors);
199 if (!trkh)
200 {
201 return DSK_ERR_NOMEM;
202 }
203 /* Copy fixed parts */
204 memcpy(trkh, &tmp, sizeof(tmp));
205 /* [1.4.2] But not the sector buffer (so we don't double-free) */
206 trkh->imdt_sec[0] = NULL;
207
208 /* Create a temporary array of sector headers */
209 tmpsec = dsk_malloc(tmp.imdt_sectors * sizeof(IMD_SECTOR));
210 if (!tmpsec)
211 {
212 imd_free_track(trkh);
213 return DSK_ERR_NOMEM;
214 }
215 /* Populate temporary sector headers */
216 for (n = 0; n < tmp.imdt_sectors; n++)
217 {
218 tmpsec[n].imds_cylinder = tmp.imdt_cylinder;
219 tmpsec[n].imds_head = tmp.imdt_head & 0x3F;
220 tmpsec[n].imds_sector = 0;
221 tmpsec[n].imds_status = ST_NODATA;
222 tmpsec[n].imds_datalen = 0;
223 tmpsec[n].imds_seclen = tmp.imdt_seclen;
224 }
225 /* Load sector IDs */
226 for (n = 0; n < tmp.imdt_sectors; n++)
227 {
228 c = fgetc(fp);
229 if (c == EOF)
230 {
231 dsk_free(tmpsec);
232 imd_free_track(trkh);
233 return DSK_ERR_SYSERR;
234 }
235
236 tmpsec[n].imds_sector = c;
237 }
238 /* Load sector cylinder map (if present) */
239 if (tmp.imdt_head & 0x80)
240 {
241 for (n = 0; n < tmp.imdt_sectors; n++)
242 {
243 c = fgetc(fp);
244 if (c == EOF)
245 {
246 dsk_free(tmpsec);
247 imd_free_track(trkh);
248 return DSK_ERR_SYSERR;
249 }
250
251 tmpsec[n].imds_cylinder = c;
252 }
253 }
254 /* Load sector head map (if present) */
255 if (tmp.imdt_head & 0x40)
256 {
257 for (n = 0; n < tmp.imdt_sectors; n++)
258 {
259 c = fgetc(fp);
260 if (c == EOF)
261 {
262 dsk_free(tmpsec);
263 imd_free_track(trkh);
264 return DSK_ERR_SYSERR;
265 }
266
267 tmpsec[n].imds_head = c;
268 }
269 }
270 /* Load sector lengths (if present) */
271 if (tmp.imdt_seclen == 0xFFFF)
272 {
273 int l, h;
274
275 for (n = 0; n < tmp.imdt_sectors; n++)
276 {
277 l = fgetc(fp);
278 h = fgetc(fp);
279 if (l == EOF || h == EOF)
280 {
281 dsk_free(tmpsec);
282 imd_free_track(trkh);
283 return DSK_ERR_SYSERR;
284 }
285 tmpsec[n].imds_seclen = (l & 0xFF) | ((h << 8) & 0xFF00);
286 }
287 }
288
289
290 for (n = 0; n < tmp.imdt_sectors; n++)
291 {
292 /* Get status for sector */
293 c = fgetc(fp);
294 if (c == EOF)
295 {
296 dsk_free(tmpsec);
297 imd_free_track(trkh);
298 return DSK_ERR_SYSERR;
299 }
300 tmpsec[n].imds_status = c;
301 switch(tmpsec[n].imds_status)
302 {
303 default:
304 #ifndef WIN16
305 fprintf(stderr, "Unsupported IMD status "
306 "0x%02x\n", tmpsec[n].imds_status);
307 #endif
308 dsk_free(tmpsec);
309 imd_free_track(trkh);
310 return DSK_ERR_NOTME;
311 /* ID, no data */
312 case ST_NODATA: tmpsec[n].imds_datalen = 0; break;
313 /* Uncompressed */
314 case ST_NORMAL:
315 case ST_DELETED:
316 case ST_DATAERR:
317 case ST_DELERR: tmpsec[n].imds_datalen =
318 tmpsec[n].imds_seclen; break;
319 /* Compressed */
320 case ST_CNORMAL:
321 case ST_CDELETED:
322 case ST_CDATAERR:
323 case ST_CDELERR: tmpsec[n].imds_datalen = 1; break;
324 }
325 trkh->imdt_sec[n] = dsk_malloc(sizeof(IMD_SECTOR) +
326 tmpsec[n].imds_datalen);
327 if (!trkh->imdt_sec[n])
328 {
329 dsk_free(tmpsec);
330 imd_free_track(trkh);
331 return DSK_ERR_NOMEM;
332 }
333 memcpy(trkh->imdt_sec[n], &tmpsec[n], sizeof(IMD_SECTOR));
334
335 if (tmpsec[n].imds_datalen)
336 {
337 if (fread(trkh->imdt_sec[n]->imds_data, 1,
338 tmpsec[n].imds_datalen, fp) <
339 tmpsec[n].imds_datalen)
340 {
341 dsk_free(tmpsec);
342 imd_free_track(trkh);
343 return DSK_ERR_SYSERR;
344 }
345 }
346 }
347 self->imd_tracks[count] = trkh;
348 dsk_free(tmpsec);
349 // for (n = 0; n < tmp.imdt_sectors; n++)
350 // {
351 // printf("c=%d h=%d s=%d len=%d status=%d datalen=%d\n",
352 // tmpsec[n].imds_cylinder, tmpsec[n].imds_head,
353 // tmpsec[n].imds_sector, tmp.imdt_secsize,
354 // tmpsec[n].imds_status, tmpsec[n].imds_len);
355 // }
356 return DSK_ERR_OK;
357 }
358
359
360
imd_open(DSK_DRIVER * self,const char * filename)361 dsk_err_t imd_open(DSK_DRIVER *self, const char *filename)
362 {
363 FILE *fp;
364 IMD_DSK_DRIVER *imdself;
365 dsk_err_t err;
366 int ccmt, n;
367 dsk_ltrack_t count = 0;
368 char *comment;
369 int termch;
370
371 /* Sanity check: Is this meant for our driver? */
372 if (self->dr_class != &dc_imd) return DSK_ERR_BADPTR;
373 imdself = (IMD_DSK_DRIVER *)self;
374
375 fp = fopen(filename, "r+b");
376 if (!fp)
377 {
378 imdself->imd_readonly = 1;
379 fp = fopen(filename, "rb");
380 }
381 if (!fp) return DSK_ERR_NOTME;
382
383 /* Try to check the magic number. Read the first line, which
384 * may terminate with '\n' if a comment follows, or 0x1A
385 * otherwise. */
386 err = imd_readto(fp, '\n', 0x1A, &ccmt, &termch);
387 if (err)
388 {
389 fclose(fp);
390 return DSK_ERR_NOTME;
391 }
392 comment = dsk_malloc(1 + ccmt);
393 if (!comment)
394 {
395 fclose(fp);
396 return DSK_ERR_NOTME;
397 }
398 if ((int)fread(comment, 1, ccmt, fp) < ccmt)
399 {
400 fclose(fp);
401 return DSK_ERR_SYSERR;
402 }
403 comment[ccmt] = 0;
404
405 /* printf("Header='%s' pos=%ld\n", comment, ftell(fp)); */
406
407 /* IMD signature is 4 bytes magic, then the rest of the line is
408 * freeform (but probably includes a date stamp) */
409 if (memcmp(comment, "IMD ", 4))
410 {
411 dsk_free(comment);
412 fclose(fp);
413 return DSK_ERR_NOTME;
414 }
415 dsk_free(comment);
416
417 /* If the header wasn't terminated by 0x1A, then a comment
418 * follows. */
419 if (termch != 0x1A)
420 {
421 err = imd_readto(fp, 0x1A, 0x1A, &ccmt, &termch);
422 if (err)
423 {
424 fclose(fp);
425 return DSK_ERR_NOTME;
426 }
427 comment = dsk_malloc(1 + ccmt);
428 if (!comment)
429 {
430 fclose(fp);
431 return DSK_ERR_NOMEM;
432 }
433 if ((int)fread(comment, 1, ccmt, fp) < ccmt)
434 {
435 fclose(fp);
436 return DSK_ERR_SYSERR;
437 }
438 comment[ccmt - 1] = 0;
439 dsk_set_comment(self, comment);
440 dsk_free(comment);
441 }
442 imdself->imd_dirty = 0;
443 imdself->imd_sec = 0;
444 /* Keep a copy of the filename; when writing back, we will need it */
445 imdself->imd_filename = dsk_malloc(1 + strlen(filename));
446 if (!imdself->imd_filename) return DSK_ERR_NOMEM;
447 strcpy(imdself->imd_filename, filename);
448
449 /* And now we're onto the tracks */
450 ccmt = 0;
451 dsk_report("Loading IMD file into memory");
452
453 err = DSK_ERR_OK;
454
455 while (!feof(fp))
456 {
457 err = imd_load_track(imdself, count, fp);
458 if (err == DSK_ERR_OVERRUN) /* EOF */
459 {
460 dsk_report_end();
461 return DSK_ERR_OK;
462 }
463 else if (err)
464 {
465 dsk_free(imdself->imd_filename);
466 for (n = 0; n < (int)(imdself->imd_ntracks); n++)
467 {
468 imd_free_track(imdself->imd_tracks[n]);
469 }
470 dsk_report_end();
471 return err;
472 }
473 ++count;
474 }
475 /* Should never get here! */
476 dsk_report_end();
477 return DSK_ERR_OK;
478 }
479
480
imd_creat(DSK_DRIVER * self,const char * filename)481 dsk_err_t imd_creat(DSK_DRIVER *self, const char *filename)
482 {
483 IMD_DSK_DRIVER *imdself;
484 FILE *fp;
485
486 /* Sanity check: Is this meant for our driver? */
487 if (self->dr_class != &dc_imd) return DSK_ERR_BADPTR;
488 imdself = (IMD_DSK_DRIVER *)self;
489
490 /* See if the file can be created. But don't hold it open. */
491 fp = fopen(filename, "wb");
492 imdself->imd_readonly = 0;
493 if (!fp) return DSK_ERR_SYSERR;
494 fclose(fp);
495 imdself->imd_dirty = 1;
496
497 /* Keep a copy of the filename, for writing back */
498 imdself->imd_filename = dsk_malloc(1 + strlen(filename));
499 if (!imdself->imd_filename) return DSK_ERR_NOMEM;
500 strcpy(imdself->imd_filename, filename);
501
502 imdself->imd_ntracks = 0;
503 imdself->imd_tracks = NULL;
504
505 return DSK_ERR_OK;
506 }
507
compare_tracks(const void * a,const void * b)508 static int compare_tracks(const void *a, const void *b)
509 {
510 const IMD_TRACK *ta = *(const IMD_TRACK **)a;
511 const IMD_TRACK *tb = *(const IMD_TRACK **)b;
512
513 /* Sort by cylinder and head, with nulls at the end */
514 if (ta == NULL && tb == NULL) return 0;
515 if (ta == NULL) return 1;
516 if (tb == NULL) return -1;
517
518 if (ta->imdt_cylinder != tb->imdt_cylinder)
519 return ta->imdt_cylinder - tb->imdt_cylinder;
520 return (ta->imdt_head & 0x3F) - (tb->imdt_head & 0x3F);
521 }
522
imd_save_track(IMD_DSK_DRIVER * self,IMD_TRACK * trk,FILE * fp)523 static dsk_err_t imd_save_track(IMD_DSK_DRIVER *self, IMD_TRACK *trk, FILE *fp)
524 {
525 int nsec;
526 unsigned char secsize = dsk_get_psh(trk->imdt_seclen);
527 IMD_SECTOR *sec;
528
529 /* See if we need to write cylinder / head maps */
530 trk->imdt_head &= 0x3F;
531
532 for (nsec = 0; nsec < trk->imdt_sectors; nsec++)
533 {
534 sec = trk->imdt_sec[nsec];
535 if (sec->imds_cylinder != trk->imdt_cylinder)
536 trk->imdt_head |= 0x80;
537 if (sec->imds_head != (trk->imdt_head & 0x3F))
538 trk->imdt_head |= 0x40;
539 if (sec->imds_seclen != trk->imdt_seclen)
540 secsize = 0xFF;
541 }
542
543
544 /* Write fixed part of header */
545 if (fwrite(&trk->imdt_mode, 1, 4, fp) < 4
546 || fputc(secsize, fp) == EOF)
547 {
548 return DSK_ERR_SYSERR;
549 }
550
551 /* Write sector IDs */
552 for (nsec = 0; nsec < trk->imdt_sectors; nsec++)
553 {
554 sec = trk->imdt_sec[nsec];
555 if (fputc(sec->imds_sector, fp) == EOF)
556 return DSK_ERR_SYSERR;
557 }
558 /* Write cylinder IDs, if necessary */
559 if (trk->imdt_head & 0x80)
560 {
561 for (nsec = 0; nsec < trk->imdt_sectors; nsec++)
562 {
563 sec = trk->imdt_sec[nsec];
564 if (fputc(sec->imds_cylinder, fp) == EOF)
565 return DSK_ERR_SYSERR;
566 }
567 }
568 /* Write head IDs, if necessary */
569 if (trk->imdt_head & 0x40)
570 {
571 for (nsec = 0; nsec < trk->imdt_sectors; nsec++)
572 {
573 sec = trk->imdt_sec[nsec];
574 if (fputc(sec->imds_head, fp) == EOF)
575 return DSK_ERR_SYSERR;
576 }
577 }
578 /* Write sector sizes, if necessary */
579 if (secsize == 0xFF)
580 {
581 for (nsec = 0; nsec < trk->imdt_sectors; nsec++)
582 {
583 sec = trk->imdt_sec[nsec];
584 if (fputc(sec->imds_seclen & 0xFF, fp) == EOF)
585 return DSK_ERR_SYSERR;
586 if (fputc(sec->imds_seclen >> 8, fp) == EOF)
587 return DSK_ERR_SYSERR;
588 }
589 }
590
591 /* Write sector data */
592 for (nsec = 0; nsec < trk->imdt_sectors; nsec++)
593 {
594 sec = trk->imdt_sec[nsec];
595 if (fputc(sec->imds_status, fp) == EOF)
596 return DSK_ERR_SYSERR;
597 if (sec->imds_status)
598 {
599 if (fwrite(sec->imds_data, 1, sec->imds_datalen, fp) <
600 sec->imds_datalen)
601 return DSK_ERR_SYSERR;
602 }
603 }
604 return DSK_ERR_OK;
605 }
606
607
608
imd_close(DSK_DRIVER * self)609 dsk_err_t imd_close(DSK_DRIVER *self)
610 {
611 IMD_DSK_DRIVER *imdself;
612 dsk_err_t err = DSK_ERR_OK;
613 dsk_ltrack_t trk;
614 char buf[128];
615 FILE *fp;
616
617 if (self->dr_class != &dc_imd) return DSK_ERR_BADPTR;
618 imdself = (IMD_DSK_DRIVER *)self;
619
620 if (imdself->imd_filename && (imdself->imd_dirty))
621 {
622 /* When writing back to a IMD, create the file from scratch. We might as well
623 * sort the tracks, too.*/
624
625 qsort(imdself->imd_tracks, imdself->imd_ntracks,
626 sizeof(IMD_TRACK *), compare_tracks);
627
628 fp = fopen(imdself->imd_filename, "wb");
629 if (!fp) err = DSK_ERR_SYSERR;
630 else
631 {
632 time_t t;
633 struct tm *ptm;
634 char *comment;
635
636 time (&t);
637 ptm = localtime(&t);
638
639 if (dsk_get_comment(self, &comment) != DSK_ERR_OK ||
640 comment == NULL)
641 {
642 comment = "\r\n";
643 }
644 dsk_report("Writing IMD file");
645 /* The spec seems to imply the header must read "IMD 1.18: datestamp" but
646 * TD02IMD writes a completely different header... */
647 sprintf(buf, "IMD LibDsk %s: %02d/%02d/%04d "
648 "%02d:%02d:%02d\r\n",
649 LIBDSK_VERSION,
650 ptm->tm_mday, ptm->tm_mon + 1,
651 ptm->tm_year + 1900,
652 ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
653
654 if (fwrite(buf, 1, strlen(buf), fp) < strlen(buf))
655 {
656 err = DSK_ERR_SYSERR;
657 }
658 /* Write comment */
659 else if (fwrite(comment, 1, strlen(comment), fp) < strlen(comment))
660 {
661 err = DSK_ERR_SYSERR;
662 }
663 else if (fputc(0x1A, fp) == EOF)
664 {
665 err = DSK_ERR_SYSERR;
666 }
667 else for (trk = 0; trk < imdself->imd_ntracks; trk++)
668 {
669 if (imdself->imd_tracks[trk])
670 {
671 err = imd_save_track(imdself,
672 imdself->imd_tracks[trk], fp);
673 }
674 if (err) break;
675 }
676 fclose(fp);
677 dsk_report_end();
678 }
679 }
680 /* Free track buffers if we have them */
681 if (imdself->imd_tracks)
682 {
683 unsigned int n;
684 for (n = 0; n < imdself->imd_ntracks; n++)
685 {
686 imd_free_track(imdself->imd_tracks[n]);
687 }
688 dsk_free(imdself->imd_tracks);
689 imdself->imd_tracks = NULL;
690 imdself->imd_ntracks = 0;
691 }
692 if (imdself->imd_filename)
693 {
694 dsk_free(imdself->imd_filename);
695 imdself->imd_filename = NULL;
696 }
697 return err;
698 }
699
encode_mode(const DSK_GEOMETRY * geom)700 static int encode_mode(const DSK_GEOMETRY *geom)
701 {
702 int fm = ((geom->dg_fm & RECMODE_MASK) == RECMODE_FM);
703 switch (geom->dg_datarate)
704 {
705 case RATE_SD: return fm ? 2 : 5;
706 case RATE_DD: return fm ? 1 : 4;
707 case RATE_HD: return fm ? 0 : 3;
708 case RATE_ED: /* Not supported by IMD format, so
709 * we'll just have to improvise */
710 return fm ? 6 : 9;
711 }
712 return -1;
713 }
714
715 /* In our internal array, locate the track with the specified cylinder
716 * and head (if it's there) */
imd_seektrack(DSK_DRIVER * self,const DSK_GEOMETRY * geom,dsk_pcyl_t cylinder,dsk_phead_t head,int * result)717 static dsk_err_t imd_seektrack(DSK_DRIVER *self, const DSK_GEOMETRY *geom,
718 dsk_pcyl_t cylinder, dsk_phead_t head, int *result)
719 {
720 IMD_DSK_DRIVER *imdself;
721 int track;
722 int mode = encode_mode(geom);
723
724 if (!self || !geom || self->dr_class != &dc_imd) return DSK_ERR_BADPTR;
725 imdself = (IMD_DSK_DRIVER *)self;
726
727 if (!imdself->imd_filename) return DSK_ERR_NOTRDY;
728
729 for (track = 0; track < (int)(imdself->imd_ntracks); track++)
730 {
731 if (imdself->imd_tracks[track] == NULL) continue;
732
733 if ( imdself->imd_tracks[track]->imdt_cylinder == cylinder &&
734 (imdself->imd_tracks[track]->imdt_head & 0x3F) == (int)head &&
735 imdself->imd_tracks[track]->imdt_mode == mode)
736 {
737 if (result) *result = track;
738 return DSK_ERR_OK;
739 }
740 }
741 return DSK_ERR_SEEKFAIL;
742 }
743
isdeleted(int status)744 static int isdeleted(int status)
745 {
746 return (status == ST_DELETED || status == ST_CDELETED ||
747 status == ST_DELERR || status == ST_CDELERR);
748 }
749
iscompressed(int status)750 static int iscompressed(int status)
751 {
752 return (status == ST_CNORMAL || status == ST_CDELETED ||
753 status == ST_CDATAERR || status == ST_CDELERR);
754 }
755
756
imd_find_sector(IMD_DSK_DRIVER * self,const DSK_GEOMETRY * geom,dsk_pcyl_t cylinder,dsk_phead_t head,dsk_psect_t sector,dsk_pcyl_t cyl_expected,dsk_phead_t head_expected,int * deleted,IMD_TRACK ** restrack,IMD_SECTOR ** result)757 static dsk_err_t imd_find_sector(IMD_DSK_DRIVER *self, const DSK_GEOMETRY *geom,
758 dsk_pcyl_t cylinder, dsk_phead_t head,
759 dsk_psect_t sector, dsk_pcyl_t cyl_expected,
760 dsk_phead_t head_expected, int *deleted,
761 IMD_TRACK **restrack, IMD_SECTOR **result)
762 {
763 int ntrack, nsec;
764 IMD_TRACK *trk;
765 IMD_SECTOR *sec;
766 int want_deleted = 0;
767 int have_deleted = 0;
768 dsk_err_t err;
769
770 *restrack = NULL;
771 *result = NULL;
772 if (deleted && *deleted) want_deleted = 1;
773
774 err = imd_seektrack(&self->imd_super, geom, cylinder, head, &ntrack);
775 if (err) return err;
776
777 trk = self->imd_tracks[ntrack];
778 for (nsec = 0; nsec < trk->imdt_sectors; nsec++)
779 {
780 sec = trk->imdt_sec[nsec];
781
782 if (sec->imds_cylinder == cyl_expected &&
783 sec->imds_head == head_expected &&
784 sec->imds_sector == sector)
785 {
786 if (isdeleted(sec->imds_status)) have_deleted = 1;
787
788 if (geom->dg_noskip == 0 &&
789 want_deleted != have_deleted) continue;
790
791 if (deleted) *deleted = have_deleted;
792 *restrack = trk;
793 *result = sec;
794
795 /* Translate status to libdsk error code */
796 switch (sec->imds_status)
797 {
798 case ST_NODATA: return DSK_ERR_NODATA;
799 /* ID but no data */
800 case ST_DATAERR:
801 case ST_CDATAERR:
802 case ST_DELERR:
803 case ST_CDELERR: return DSK_ERR_DATAERR;
804 default:return DSK_ERR_OK;
805 }
806 }
807 }
808 self->imd_sec = 0;
809 return DSK_ERR_NOADDR;
810 }
811
812
imd_read(DSK_DRIVER * self,const DSK_GEOMETRY * geom,void * buf,dsk_pcyl_t cylinder,dsk_phead_t head,dsk_psect_t sector)813 dsk_err_t imd_read(DSK_DRIVER *self, const DSK_GEOMETRY *geom,
814 void *buf, dsk_pcyl_t cylinder,
815 dsk_phead_t head, dsk_psect_t sector)
816 {
817 return imd_xread(self, geom, buf, cylinder, head, cylinder,
818 dg_x_head(geom, head),
819 dg_x_sector(geom, head, sector), geom->dg_secsize, NULL);
820 }
821
822
823 /* Expand a (possibly compressed) sector */
expand_sector(unsigned char * bbuf,size_t sector_size,IMD_SECTOR * sec)824 static void expand_sector(unsigned char *bbuf, size_t sector_size,
825 IMD_SECTOR *sec)
826 {
827 unsigned n;
828
829 if (iscompressed(sec->imds_status))
830 {
831 for (n = 0; n < sector_size; n++)
832 {
833 bbuf[n] = sec->imds_data[0];
834 }
835 }
836 else
837 {
838 for (n = 0; n < sector_size; n++)
839 {
840 if (n < sec->imds_datalen)
841 bbuf[n] = sec->imds_data[n];
842 else bbuf[n] = 0xE5;
843 }
844 }
845 }
846
imd_xread(DSK_DRIVER * self,const DSK_GEOMETRY * geom,void * buf,dsk_pcyl_t cylinder,dsk_phead_t head,dsk_pcyl_t cyl_expected,dsk_phead_t head_expected,dsk_psect_t sector,size_t sector_size,int * deleted)847 dsk_err_t imd_xread(DSK_DRIVER *self, const DSK_GEOMETRY *geom,
848 void *buf, dsk_pcyl_t cylinder, dsk_phead_t head,
849 dsk_pcyl_t cyl_expected, dsk_phead_t head_expected,
850 dsk_psect_t sector, size_t sector_size, int *deleted)
851 {
852 IMD_DSK_DRIVER *imdself;
853 IMD_SECTOR *sec = NULL;
854 IMD_TRACK *trk = NULL;
855 unsigned char *bbuf = (unsigned char *)buf;
856 dsk_err_t err;
857
858 if (!buf || !self || !geom || self->dr_class != &dc_imd)
859 return DSK_ERR_BADPTR;
860 imdself = (IMD_DSK_DRIVER *)self;
861
862 if (!imdself->imd_filename) return DSK_ERR_NOTRDY;
863
864 err = imd_find_sector(imdself, geom, cylinder, head, sector,
865 cyl_expected, head_expected, deleted, &trk, &sec);
866
867 if (sec && (err == DSK_ERR_OK || err == DSK_ERR_DATAERR))
868 {
869 expand_sector(bbuf, sector_size, sec);
870 }
871 return err;
872 }
873
874
875
imd_write(DSK_DRIVER * self,const DSK_GEOMETRY * geom,const void * buf,dsk_pcyl_t cylinder,dsk_phead_t head,dsk_psect_t sector)876 dsk_err_t imd_write(DSK_DRIVER *self, const DSK_GEOMETRY *geom,
877 const void *buf, dsk_pcyl_t cylinder,
878 dsk_phead_t head, dsk_psect_t sector)
879 {
880 return imd_xwrite(self, geom, buf, cylinder, head,
881 cylinder, dg_x_head(geom, head),
882 dg_x_sector(geom, head, sector),
883 geom->dg_secsize, 0);
884 }
885
imd_xwrite(DSK_DRIVER * self,const DSK_GEOMETRY * geom,const void * buf,dsk_pcyl_t cylinder,dsk_phead_t head,dsk_pcyl_t cyl_expected,dsk_phead_t head_expected,dsk_psect_t sector,size_t sector_size,int deleted)886 dsk_err_t imd_xwrite(DSK_DRIVER *self, const DSK_GEOMETRY *geom,
887 const void *buf, dsk_pcyl_t cylinder, dsk_phead_t head,
888 dsk_pcyl_t cyl_expected, dsk_phead_t head_expected,
889 dsk_psect_t sector, size_t sector_size, int deleted)
890 {
891 IMD_DSK_DRIVER *imdself;
892 IMD_SECTOR *sec = NULL, *newsec = NULL;
893 IMD_TRACK *trk = NULL;
894 dsk_err_t err;
895 unsigned n;
896 int newstatus;
897 size_t secsize;
898 unsigned char *curbuf;
899
900 if (!buf || !self || !geom || self->dr_class != &dc_imd)
901 return DSK_ERR_BADPTR;
902 imdself = (IMD_DSK_DRIVER *)self;
903
904 if (!imdself->imd_filename) return DSK_ERR_NOTRDY;
905 if (imdself->imd_readonly) return DSK_ERR_RDONLY;
906
907 err = imd_find_sector(imdself, geom, cylinder, head, sector,
908 cyl_expected, head_expected, &deleted, &trk, &sec);
909
910 if (err != DSK_ERR_OK && err != DSK_ERR_DATAERR) return err;
911
912 secsize = sec->imds_seclen;
913 curbuf = dsk_malloc(secsize);
914 if (!curbuf) return DSK_ERR_NOMEM;
915
916 expand_sector(curbuf, secsize, sec);
917
918 if (sector_size > secsize)
919 memcpy(curbuf, buf, secsize);
920 else memcpy(curbuf, buf, sector_size);
921
922 /* Curbuf is the buffer to write back. */
923 newstatus = deleted ? 4 : 2;
924
925 /* See if we can compress. */
926 for (n = 1; n < secsize; n++)
927 {
928 if (curbuf[n] != curbuf[0]) /* Can't compress */
929 {
930 --newstatus;
931 break;
932 }
933 }
934 /* Fancy that! Writing back exactly the same data! */
935 if (newstatus == sec->imds_status &&
936 !memcmp(curbuf, sec->imds_data, sec->imds_datalen))
937 {
938 dsk_free(curbuf);
939 return DSK_ERR_OK;
940 }
941 /* OK. We need to do an actual write. */
942 if (newstatus == 1 || newstatus == 3)
943 {
944 newsec = dsk_malloc(sizeof(IMD_SECTOR) + secsize);
945 }
946 else
947 {
948 newsec = dsk_malloc(sizeof(IMD_SECTOR) + 1);
949 }
950 if (!newsec)
951 {
952 dsk_free(curbuf);
953 return DSK_ERR_NOMEM;
954 }
955 newsec->imds_cylinder = sec->imds_cylinder;
956 newsec->imds_head = sec->imds_head;
957 newsec->imds_sector = sec->imds_sector;
958 newsec->imds_seclen = secsize;
959 newsec->imds_status = newstatus;
960 newsec->imds_datalen = (newstatus == 1 || newstatus == 3) ? secsize : 1;
961 memcpy(newsec->imds_data, curbuf, newsec->imds_datalen);
962 /* Replace the old sector record with the new one we have
963 * constructed */
964 for (n = 0; n < trk->imdt_sectors; n++)
965 {
966 if (trk->imdt_sec[n] == sec)
967 trk->imdt_sec[n] = newsec;
968 }
969 dsk_free(curbuf);
970 /* Free the old sector */
971 dsk_free(sec);
972
973 /* And mark the file as dirty */
974 imdself->imd_dirty = 1;
975 return DSK_ERR_OK;
976 }
977
978
979
imd_format(DSK_DRIVER * self,DSK_GEOMETRY * geom,dsk_pcyl_t cylinder,dsk_phead_t head,const DSK_FORMAT * format,unsigned char filler)980 dsk_err_t imd_format(DSK_DRIVER *self, DSK_GEOMETRY *geom,
981 dsk_pcyl_t cylinder, dsk_phead_t head,
982 const DSK_FORMAT *format, unsigned char filler)
983 {
984 IMD_DSK_DRIVER *imdself;
985 dsk_err_t err;
986 dsk_psect_t nlsec;
987 IMD_TRACK *trk;
988 IMD_SECTOR *sec;
989 int ntrack;
990
991 if (!self || !geom || self->dr_class != &dc_imd) return DSK_ERR_BADPTR;
992 imdself = (IMD_DSK_DRIVER *)self;
993
994 if (!imdself->imd_filename) return DSK_ERR_NOTRDY;
995 if (imdself->imd_readonly) return DSK_ERR_RDONLY;
996
997 /* Construct a new track */
998 trk = imd_alloc_track(geom->dg_sectors);
999 if (!trk) return DSK_ERR_NOMEM;
1000
1001 trk->imdt_mode = encode_mode(geom);
1002 trk->imdt_cylinder = cylinder;
1003 trk->imdt_head = head;
1004 trk->imdt_sectors = geom->dg_sectors;
1005
1006 for (nlsec = 0; nlsec < geom->dg_sectors; nlsec++)
1007 {
1008 sec = dsk_malloc(1 + sizeof(IMD_SECTOR));
1009 if (!sec)
1010 {
1011 imd_free_track(trk);
1012 return DSK_ERR_NOMEM;
1013 }
1014
1015 sec->imds_seclen = format[nlsec].fmt_secsize;
1016 if (nlsec == 0)
1017 {
1018 trk->imdt_seclen = sec->imds_seclen;
1019 }
1020 else if(trk->imdt_seclen != sec->imds_seclen)
1021 {
1022 trk->imdt_seclen = 0xFFFF;
1023 }
1024 sec->imds_cylinder = format[nlsec].fmt_cylinder;
1025 sec->imds_head = format[nlsec].fmt_head;
1026 sec->imds_sector = format[nlsec].fmt_sector;
1027 sec->imds_status = ST_CNORMAL;
1028 sec->imds_datalen = 1;
1029 sec->imds_data[0] = filler;
1030 trk->imdt_sec[nlsec] = sec;
1031 }
1032 /* Track populated. Now add it to the image (if it's not there
1033 * already) */
1034 err = imd_seektrack(self, geom, cylinder, head, &ntrack);
1035
1036 if (!err)
1037 {
1038 imd_free_track(imdself->imd_tracks[ntrack]);
1039 imdself->imd_tracks[ntrack] = trk;
1040 }
1041 else /* New track, add it */
1042 {
1043 for (ntrack = 0; ntrack < (int)imdself->imd_ntracks; ntrack++)
1044 {
1045 /* Look for a blank slot */
1046 if (imdself->imd_tracks[ntrack] == NULL)
1047 break;
1048 }
1049 /* If no blank slot found, use imd_ensure_trackcount to add
1050 * some more blank slots */
1051 err = imd_ensure_trackcount(imdself, ntrack);
1052 if (err)
1053 {
1054 imd_free_track(trk);
1055 return err;
1056 }
1057 imdself->imd_tracks[ntrack] = trk;
1058 }
1059 /* And mark the file as dirty */
1060 imdself->imd_dirty = 1;
1061
1062 return DSK_ERR_OK;
1063 }
1064
1065
1066
imd_xseek(DSK_DRIVER * self,const DSK_GEOMETRY * geom,dsk_pcyl_t cylinder,dsk_phead_t head)1067 dsk_err_t imd_xseek(DSK_DRIVER *self, const DSK_GEOMETRY *geom,
1068 dsk_pcyl_t cylinder, dsk_phead_t head)
1069 {
1070 return imd_seektrack(self, geom, cylinder, head, NULL);
1071 }
1072
imd_status(DSK_DRIVER * self,const DSK_GEOMETRY * geom,dsk_phead_t head,unsigned char * result)1073 dsk_err_t imd_status(DSK_DRIVER *self, const DSK_GEOMETRY *geom,
1074 dsk_phead_t head, unsigned char *result)
1075 {
1076 IMD_DSK_DRIVER *imdself;
1077
1078 if (!self || !geom || self->dr_class != &dc_imd) return DSK_ERR_BADPTR;
1079 imdself = (IMD_DSK_DRIVER *)self;
1080
1081 if (!imdself->imd_filename) *result &= ~DSK_ST3_READY;
1082 if (imdself->imd_readonly) *result |= DSK_ST3_RO;
1083 return DSK_ERR_OK;
1084 }
1085
1086
1087 /* Read a sector ID from a given track */
imd_secid(DSK_DRIVER * self,const DSK_GEOMETRY * geom,dsk_pcyl_t cylinder,dsk_phead_t head,DSK_FORMAT * result)1088 dsk_err_t imd_secid(DSK_DRIVER *self, const DSK_GEOMETRY *geom,
1089 dsk_pcyl_t cylinder, dsk_phead_t head, DSK_FORMAT *result)
1090 {
1091 IMD_DSK_DRIVER *imdself;
1092 IMD_TRACK *trk;
1093 IMD_SECTOR *sec;
1094 dsk_psect_t count;
1095 int ntrack;
1096 dsk_err_t err;
1097
1098 if (!self || !geom || !result || self->dr_class != &dc_imd)
1099 return DSK_ERR_BADPTR;
1100 imdself = (IMD_DSK_DRIVER *)self;
1101
1102 err = imd_seektrack(self, geom, cylinder, head, &ntrack);
1103 if (err) return err;
1104 trk = imdself->imd_tracks[ntrack];
1105
1106 count = imdself->imd_sec;
1107 ++imdself->imd_sec;
1108
1109 sec = trk->imdt_sec[count % trk->imdt_sectors];
1110
1111 result->fmt_cylinder = sec->imds_cylinder;
1112 result->fmt_head = sec->imds_head;
1113 result->fmt_sector = sec->imds_sector;
1114 result->fmt_secsize = sec->imds_seclen;
1115 return DSK_ERR_OK;
1116 }
1117
1118
1119 /* An IMD file contains enough information to take a decent stab at
1120 * determining the drive geometry. So if the default libdsk probe
1121 * fails, have a go ourselves. */
1122
imd_getgeom(DSK_DRIVER * self,DSK_GEOMETRY * geom)1123 dsk_err_t imd_getgeom(DSK_DRIVER *self, DSK_GEOMETRY *geom)
1124 {
1125 dsk_err_t err;
1126 IMD_DSK_DRIVER *imdself;
1127 IMD_TRACK *trk;
1128 IMD_SECTOR *sec;
1129 DSK_GEOMETRY testgeom;
1130 int track, sector, es;
1131 int minsec0, maxsec0, minsec1, maxsec1;
1132 int fm = 0;
1133
1134 if (!self || !geom || self->dr_class != &dc_imd)
1135 return DSK_ERR_BADPTR;
1136 imdself = (IMD_DSK_DRIVER *)self;
1137 err = dsk_defgetgeom(self, geom);
1138 if (err == DSK_ERR_OK)
1139 return err;
1140
1141 /* Initialise our test structure to known values */
1142 dg_stdformat(&testgeom, FMT_180K, NULL, NULL);
1143 testgeom.dg_cylinders = 0;
1144 testgeom.dg_sectors = 0;
1145 testgeom.dg_heads = 0;
1146
1147 minsec0 = minsec1 = 256;
1148 maxsec0 = maxsec1 = 0;
1149 es = 0;
1150
1151 for (track = 0; track < (int)imdself->imd_ntracks; track++)
1152 {
1153 trk = imdself->imd_tracks[track];
1154 if (trk == NULL) continue;
1155
1156 if (trk->imdt_sectors > testgeom.dg_sectors)
1157 testgeom.dg_sectors = trk->imdt_sectors;
1158 if (trk->imdt_cylinder >= testgeom.dg_cylinders)
1159 testgeom.dg_cylinders = trk->imdt_cylinder + 1;
1160 if ((trk->imdt_head & 0x3F) >= (int)(testgeom.dg_heads))
1161 testgeom.dg_heads = (trk->imdt_head & 0x3F) + 1;
1162
1163 switch (trk->imdt_mode)
1164 {
1165 case 0: fm = 1; testgeom.dg_datarate = RATE_HD; break;
1166 case 1: fm = 1; testgeom.dg_datarate = RATE_DD; break;
1167 case 2: fm = 1; testgeom.dg_datarate = RATE_SD; break;
1168 case 3: fm = 0; testgeom.dg_datarate = RATE_HD; break;
1169 case 4: fm = 0; testgeom.dg_datarate = RATE_DD; break;
1170 case 5: fm = 0; testgeom.dg_datarate = RATE_SD; break;
1171 case 6: fm = 1; testgeom.dg_datarate = RATE_ED; break;
1172 case 9: fm = 0; testgeom.dg_datarate = RATE_ED; break;
1173 }
1174 testgeom.dg_fm = (fm ? RECMODE_FM : RECMODE_MFM);
1175 for (sector = 0; sector < trk->imdt_sectors; sector++)
1176 {
1177 sec = trk->imdt_sec[sector];
1178 if (sec == NULL) continue;
1179
1180 testgeom.dg_secsize = sec->imds_seclen;
1181
1182 if ((trk->imdt_head & 0x3F) == 1)
1183 {
1184 if (sec->imds_head == 0) es = 1;
1185 if (sec->imds_sector < minsec1)
1186 minsec1 = sec->imds_sector;
1187 if (sec->imds_sector > maxsec1)
1188 maxsec1 = sec->imds_sector;
1189 }
1190 if ((trk->imdt_head & 0x3F) == 0)
1191 {
1192 if (sec->imds_sector < minsec0)
1193 minsec0 = sec->imds_sector;
1194 if (sec->imds_sector > maxsec0)
1195 maxsec0 = sec->imds_sector;
1196 }
1197 }
1198 }
1199 testgeom.dg_secbase = minsec0;
1200 testgeom.dg_sectors = (maxsec0 - minsec0) + 1;
1201
1202 /* Check for an 'extended surface' format: 2 heads, the
1203 * sector ranges on each side are disjoint, and sectors on head 1
1204 * are labelled as head 0 */
1205 if (testgeom.dg_heads == 2 && (maxsec0 + 1 == minsec1) && es)
1206 {
1207 testgeom.dg_sidedness = SIDES_EXTSURFACE;
1208 }
1209
1210 if (testgeom.dg_cylinders == 0 ||
1211 testgeom.dg_sectors == 0) return DSK_ERR_BADFMT;
1212
1213 memcpy(geom, &testgeom, sizeof(*geom));
1214 return DSK_ERR_OK;
1215 }
1216
1217