1 /** EMULib Emulation Library *********************************/
2 /** **/
3 /** FDIDisk.c **/
4 /** **/
5 /** This file contains routines to load, save, and access **/
6 /** disk images in various formats. The internal format is **/
7 /** always .FDI. See FDIDisk.h for declarations. **/
8 /** **/
9 /** Copyright (C) Marat Fayzullin 2007-2014 **/
10 /** You are not allowed to distribute this software **/
11 /** commercially. Please, notify me, if you make any **/
12 /** changes to this file. **/
13 /*************************************************************/
14 #include "FDIDisk.h"
15 #include <stdio.h>
16 #include <string.h>
17 #include <stdlib.h>
18 #ifdef _WIN32
19 #include <direct.h>
20 #else
21 #include <unistd.h>
22 #endif
23 #include <ctype.h>
24
25 #define IMAGE_SIZE(Fmt) \
26 (Formats[Fmt].Sides*Formats[Fmt].Tracks* \
27 Formats[Fmt].Sectors*Formats[Fmt].SecSize)
28
29 #define FDI_SIDES(P) ((P)[6]+((int)((P)[7])<<8))
30 #define FDI_TRACKS(P) ((P)[4]+((int)((P)[5])<<8))
31 #define FDI_DIR(P) ((P)+(P)[12]+((int)((P)[13])<<8)+14)
32 #define FDI_DATA(P) ((P)+(P)[10]+((int)((P)[11])<<8))
33 #define FDI_INFO(P) ((P)+(P)[8]+((int)((P)[9])<<8))
34 #define FDI_SECTORS(T) ((T)[6])
35 #define FDI_TRACK(P,T) (FDI_DATA(P)+(T)[0]+((int)((T)[1])<<8)+((int)((T)[2])<<16)+((int)((T)[3])<<24))
36 #define FDI_SECSIZE(S) (SecSizes[(S)[3]<=4? (S)[3]:4])
37 #define FDI_SECTOR(P,T,S) (FDI_TRACK(P,T)+(S)[5]+((int)((S)[6])<<8))
38
39 #ifdef _MSC_VER
40 #undef unlink
41 #define unlink _unlink
42 #endif
43
44 static const struct { int Sides,Tracks,Sectors,SecSize; } Formats[] =
45 {
46 { 2,80,16,256 }, /* Dummy format */
47 { 2,80,10,512 }, /* FMT_IMG can be 256 */
48 { 2,80,10,512 }, /* FMT_MGT can be 256 */
49 { 2,80,16,256 }, /* FMT_TRD */
50 { 2,80,10,512 }, /* FMT_FDI */
51 { 2,80,16,256 }, /* FMT_SCL */
52 { 2,80,16,256 }, /* FMT_HOBETA */
53 { 2,80,9,512 }, /* FMT_DSK */
54 { 2,80,9,512 }, /* FMT_CPCDSK */
55 { 1,40,16,256 } /* FMT_SF7000 */
56 };
57
58 static const int SecSizes[] =
59 { 128,256,512,1024,4096,0 };
60
61 static const char FDIDiskLabel[] =
62 "Disk image created by EMULib (C)Marat Fayzullin";
63
64 static const byte TRDDiskInfo[] =
65 {
66 0x01,0x16,0x00,0xF0,0x09,0x10,0x00,0x00,
67 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
68 0x20,0x00,0x00,0x64,0x69,0x73,0x6B,0x6E,
69 0x61,0x6D,0x65,0x00,0x00,0x00,0x46,0x55
70 };
71
stricmpn(const char * S1,const char * S2,int Limit)72 static int stricmpn(const char *S1,const char *S2,int Limit)
73 {
74 for(;*S1&&*S2&&Limit&&(toupper(*S1)==toupper(*S2));++S1,++S2,--Limit);
75 return(Limit? toupper(*S1)-toupper(*S2):0);
76 }
77
78 /** InitFDI() ************************************************/
79 /** Clear all data structure fields. **/
80 /*************************************************************/
InitFDI(FDIDisk * D)81 void InitFDI(FDIDisk *D)
82 {
83 D->Format = 0;
84 D->Data = 0;
85 D->DataSize = 0;
86 D->Sides = 0;
87 D->Tracks = 0;
88 D->Sectors = 0;
89 D->SecSize = 0;
90 }
91
92 /** EjectFDI() ***********************************************/
93 /** Eject disk image. Free all allocated memory. **/
94 /*************************************************************/
EjectFDI(FDIDisk * D)95 void EjectFDI(FDIDisk *D)
96 {
97 if(D->Data) free(D->Data);
98 InitFDI(D);
99 }
100
101 /** NewFDI() *************************************************/
102 /** Allocate memory and create new .FDI disk image of given **/
103 /** dimensions. Returns disk data pointer on success, 0 on **/
104 /** failure. **/
105 /*************************************************************/
NewFDI(FDIDisk * D,int Sides,int Tracks,int Sectors,int SecSize)106 byte *NewFDI(FDIDisk *D,int Sides,int Tracks,int Sectors,int SecSize)
107 {
108 byte *P,*DDir;
109 int I,J,K,L,N;
110
111 /* Find sector size code */
112 for(L=0;SecSizes[L]&&(SecSizes[L]!=SecSize);++L);
113 if(!SecSizes[L]) return(0);
114
115 /* Allocate memory */
116 K = Sides*Tracks*Sectors*SecSize+sizeof(FDIDiskLabel);
117 I = Sides*Tracks*(Sectors+1)*7+14;
118 if(!(P=(byte *)malloc(I+K))) return(0);
119 memset(P,0x00,I+K);
120
121 /* Eject previous disk image */
122 EjectFDI(D);
123
124 /* Set disk dimensions according to format */
125 D->Format = FMT_FDI;
126 D->Data = P;
127 D->DataSize = I+K;
128 D->Sides = Sides;
129 D->Tracks = Tracks;
130 D->Sectors = Sectors;
131 D->SecSize = SecSize;
132
133 /* .FDI magic number */
134 memcpy(P,"FDI",3);
135 /* Disk description */
136 memcpy(P+I,FDIDiskLabel,sizeof(FDIDiskLabel));
137 /* Write protection (1=ON) */
138 P[3] = 0;
139 P[4] = Tracks&0xFF;
140 P[5] = Tracks>>8;
141 P[6] = Sides&0xFF;
142 P[7] = Sides>>8;
143 /* Disk description offset */
144 P[8] = I&0xFF;
145 P[9] = I>>8;
146 I += sizeof(FDIDiskLabel);
147 /* Sector data offset */
148 P[10] = I&0xFF;
149 P[11] = I>>8;
150 /* Track directory offset */
151 P[12] = 0;
152 P[13] = 0;
153
154 /* Create track directory */
155 for(J=K=0,DDir=P+14;J<Sides*Tracks;++J,K+=Sectors*SecSize)
156 {
157 /* Create track entry */
158 DDir[0] = K&0xFF;
159 DDir[1] = (K>>8)&0xFF;
160 DDir[2] = (K>>16)&0xFF;
161 DDir[3] = (K>>24)&0xFF;
162 /* Reserved bytes */
163 DDir[4] = 0;
164 DDir[5] = 0;
165 DDir[6] = Sectors;
166 /* For all sectors on a track... */
167 for(I=N=0,DDir+=7;I<Sectors;++I,DDir+=7,N+=SecSize)
168 {
169 /* Create sector entry */
170 DDir[0] = J/Sides;
171 DDir[1] = J%Sides;
172 DDir[2] = I+1;
173 DDir[3] = L;
174 /* CRC marks and "deleted" bit (D00CCCCC) */
175 DDir[4] = (1<<L);
176 DDir[5] = N&0xFF;
177 DDir[6] = N>>8;
178 }
179 }
180
181 /* Done */
182 return(FDI_DATA(P));
183 }
184
185 /** LoadFDI() ************************************************/
186 /** Load a disk image from a given file, in a given format **/
187 /** (see FMT_* #defines). Guess format from the file name **/
188 /** when Format=FMT_AUTO. Returns format ID on success or **/
189 /** 0 on failure. When FileName=0, ejects the disk freeing **/
190 /** memory and returns 0. **/
191 /*************************************************************/
LoadFDI(FDIDisk * D,const char * FileName,int Format)192 int LoadFDI(FDIDisk *D,const char *FileName,int Format)
193 {
194 byte Buf[256],*P,*DDir;
195 const char *T;
196 int J,I,K,L,N;
197 FILE *F;
198
199 /* If just ejecting a disk, drop out */
200 if(!FileName) { EjectFDI(D);return(0); }
201
202 /* If requested automatic format recognition... */
203 if(!Format)
204 {
205 /* Recognize disk image format */
206 T = strrchr(FileName,'\\');
207 T = T? T:strrchr(FileName,'/');
208 T = T? T+1:FileName;
209 T = strchr(T,'.');
210 Format = !T? 0
211 : !stricmpn(T,".FDI",4)? FMT_FDI
212 : !stricmpn(T,".IMG",4)? FMT_IMG
213 : !stricmpn(T,".MGT",4)? FMT_MGT
214 : !stricmpn(T,".TRD",4)? FMT_TRD
215 : !stricmpn(T,".SCL",4)? FMT_SCL
216 : !stricmpn(T,".DSK",4)? FMT_DSK
217 : !stricmpn(T,".$",2)? FMT_HOBETA
218 : 0;
219
220 /* Try loading by extension */
221 if(Format&&(J=LoadFDI(D,FileName,Format))) return(J);
222
223 /* Attention: FMT_DSK and FMT_CPCDSK share the same .DSK extension */
224 if((Format!=FMT_CPCDSK)&&LoadFDI(D,FileName,FMT_CPCDSK)) return(FMT_CPCDSK);
225
226 /* Try loading by magic number */
227 if((Format!=FMT_FDI)&&LoadFDI(D,FileName,FMT_FDI)) return(FMT_FDI);
228 if((Format!=FMT_SCL)&&LoadFDI(D,FileName,FMT_SCL)) return(FMT_SCL);
229 if((Format!=FMT_DSK)&&LoadFDI(D,FileName,FMT_DSK)) return(FMT_DSK);
230
231 /* Everything failed */
232 return(0);
233 }
234
235 /* Open file and find its size */
236 if(!(F=fopen(FileName,"rb"))) return(0);
237 if(fseek(F,0,SEEK_END)<0) { fclose(F);return(0); }
238 if((J=ftell(F))<=0) { fclose(F);return(0); }
239 rewind(F);
240
241 switch(Format)
242 {
243 case FMT_FDI: /* If .FDI format... */
244 /* Allocate memory and read file */
245 if(!(P=(byte *)malloc(J))) { fclose(F);return(0); }
246 if(fread(P,1,J,F)!=J) { free(P);fclose(F);return(0); }
247 /* Verify .FDI format tag */
248 if(memcmp(P,"FDI",3)) { free(P);fclose(F);return(0); }
249 /* Eject current disk image */
250 EjectFDI(D);
251 /* Read disk dimensions */
252 D->Sides = FDI_SIDES(P);
253 D->Tracks = FDI_TRACKS(P);
254 D->Sectors = 0;
255 D->SecSize = 0;
256 /* Check number of sectors and sector size */
257 for(J=FDI_SIDES(P)*FDI_TRACKS(P),DDir=FDI_DIR(P);J;--J)
258 {
259 /* Get number of sectors */
260 I=FDI_SECTORS(DDir);
261 /* Check that all tracks have the same number of sectors */
262 if(!D->Sectors) D->Sectors=I; else if(D->Sectors!=I) break;
263 /* Check that all sectors have the same size */
264 for(DDir+=7;I;--I,DDir+=7)
265 if(!D->SecSize) D->SecSize=FDI_SECSIZE(DDir);
266 else if(D->SecSize!=FDI_SECSIZE(DDir)) break;
267 /* Drop out if the sector size is not uniform */
268 if(I) break;
269 }
270 /* If no uniform sectors or sector size, set them to zeros */
271 if(J) D->Sectors=D->SecSize=0;
272 break;
273
274 case FMT_DSK: /* If .DSK format... */
275 /* Read header */
276 if(fread(Buf,1,32,F)!=32) { fclose(F);return(0); }
277 /* Check magic number */
278 if((Buf[0]!=0xE9)&&(Buf[0]!=0xEB)) { fclose(F);return(0); }
279 /* Check media descriptor */
280 if(Buf[21]<0xF8) { fclose(F);return(0); }
281 /* Compute disk geometry */
282 K = Buf[26]+((int)Buf[27]<<8); /* Heads */
283 N = Buf[24]+((int)Buf[25]<<8); /* Sectors */
284 L = Buf[11]+((int)Buf[12]<<8); /* SecSize */
285 I = Buf[19]+((int)Buf[20]<<8); /* Total S.*/
286 I = K&&N? I/K/N:0; /* Tracks */
287 /* Number of heads CAN BE WRONG */
288 K = I&&N&&L? J/I/N/L:0;
289 /* Create a new disk image */
290 P = NewFDI(D,K,I,N,L);
291 if(!P) { fclose(F);return(0); }
292 /* Make sure we do not read too much data */
293 I = K*I*N*L;
294 J = J>I? I:J;
295 /* Read disk image file (ignore short reads!) */
296 rewind(F);
297 fread(P,1,J,F);
298 /* Done */
299 P = D->Data;
300 break;
301
302 case FMT_CPCDSK: /* If Amstrad CPC .DSK format... */
303 /* Read header (first track is next) */
304 if(fread(Buf,1,256,F)!=256) { fclose(F);return(0); }
305 /* Check magic string */
306 if(memcmp(Buf,"MV - CPC",8)&&memcmp(Buf,"EXTENDED CPC DSK File",21))
307 { fclose(F);return(0); }
308 /* Compute disk geometry */
309 I = Buf[48]; /* Tracks */
310 K = Buf[49]; /* Heads */
311 N = Formats[Format].Sectors; /* Maximal number of sectors */
312 L = Buf[50]+((int)Buf[51]<<8); /* Track size + 0x100 */
313 /* Extended CPC .DSK format lists sizes by track */
314 if(!L)
315 for(J=0;J<I;++J)
316 if(L<((int)Buf[J+52]<<8)) L=(int)Buf[J+52]<<8;
317 /* Maximal sector size */
318 L = (L-0x100+N-1)/N;
319 /* Check geometry */
320 //printf("Tracks=%d, Heads=%d, Sectors=%d, SectorSize=%d\n",I,K,N,L);
321 if(!K||!N||!L||!I) { fclose(F);return(0); }
322 /* Create a new disk image */
323 if(!NewFDI(D,K,I,N,L)) { fclose(F);return(0); }
324 /* Sectors-per-track and bytes-per-sector may vary */
325 D->Sectors = 0;
326 D->SecSize = 0;
327 /* We are rewriting .FDI directory and data */
328 DDir = FDI_DIR(D->Data);
329 P = FDI_DATA(D->Data);
330 /* Skip to the first track info block */
331 fseek(F,0x100,SEEK_SET);
332 /* Read tracks */
333 for(I*=K;I;--I)
334 {
335 /* Read track header */
336 if(fread(Buf,1,0x18,F)!=0x18) break;
337 /* Check magic string */
338 if(memcmp(Buf,"Track-Info\r\n",12)) break;
339 /* Compute track geometry */
340 N = Buf[21]; /* Sectors */
341 L = Buf[20]; /* SecSize */
342 J = P-FDI_DATA(D->Data); /* Data offset */
343 /* Create .FDI track entry */
344 DDir[0] = J&0xFF;
345 DDir[1] = (J>>8)&0xFF;
346 DDir[2] = (J>>16)&0xFF;
347 DDir[3] = (J>>24)&0xFF;
348 DDir[4] = 0;
349 DDir[5] = 0;
350 DDir[6] = N;
351 /* Read sector headers */
352 for(DDir+=7,J=N,K=0;J&&(fread(Buf,8,1,F)==8);DDir+=7,--J,K+=SecSizes[L])
353 {
354 /* Create .FDI sector entry */
355 DDir[0] = Buf[0];
356 DDir[1] = Buf[1];
357 DDir[2] = Buf[2];
358 DDir[3] = Buf[3];
359 DDir[4] = (1<<L)|(~Buf[4]&0x80);
360 DDir[5] = K&0xFF;
361 DDir[6] = K>>8;
362 }
363 /* Seek to the track data */
364 if(fseek(F,0x100-0x18-8*N,SEEK_CUR)<0) break;
365 /* Read track data */
366 if(fread(P,1,K,F)!=K) break; else P+=K;
367 }
368 /* Done */
369 P = D->Data;
370 break;
371
372 case FMT_TRD: /* If .TRD format... */
373 case FMT_MGT: /* If .MGT format... */
374 case FMT_SF7000: /* If .SF format... */
375 /* Create a new disk image */
376 P = NewFDI(
377 D,
378 Formats[Format].Sides,
379 Formats[Format].Tracks,
380 Formats[Format].Sectors,
381 Formats[Format].SecSize
382 );
383 if(!P) { fclose(F);return(0); }
384 /* Make sure we do not read too much data */
385 J = J>IMAGE_SIZE(Format)? IMAGE_SIZE(Format):J;
386 /* Read disk image file (ignore short reads!) */
387 fread(P,1,J,F);
388 /* Done */
389 P = D->Data;
390 break;
391
392 case FMT_IMG: /* If .IMG format... */
393 /* Create a new disk image */
394 P = NewFDI(
395 D,
396 Formats[Format].Sides,
397 Formats[Format].Tracks,
398 Formats[Format].Sectors,
399 Formats[Format].SecSize
400 );
401 if(!P) { fclose(F);return(0); }
402 /* Read disk image file track-by-track */
403 K = Formats[Format].Tracks;
404 L = Formats[Format].Sectors*Formats[Format].SecSize;
405 I = Formats[Format].Tracks*Formats[Format].Sides;
406 for(J=0;J<I;++J)
407 if(fread(P+L*2*(J%K)+(J>=K? L:0),1,L,F)!=L) break;
408 /* Done */
409 P = D->Data;
410 break;
411
412 case FMT_SCL: /* If .SCL format... */
413 /* @@@ NEED TO CHECK CHECKSUM AT THE END */
414 /* Read header */
415 if(fread(Buf,1,9,F)!=9) { fclose(F);return(0); }
416 /* Verify .SCL format tag and the number of files */
417 if(memcmp(Buf,"SINCLAIR",8)||(Buf[8]>128)) { fclose(F);return(0); }
418 /* Create a new disk image */
419 P = NewFDI(
420 D,
421 Formats[Format].Sides,
422 Formats[Format].Tracks,
423 Formats[Format].Sectors,
424 Formats[Format].SecSize
425 );
426 if(!P) { fclose(F);return(0); }
427 /* Compute the number of free sectors */
428 I = D->Sides*D->Tracks*D->Sectors;
429 /* Build directory, until we run out of disk space */
430 for(J=0,K=D->Sectors,DDir=P;(J<Buf[8])&&(K<I);++J)
431 {
432 /* Read .SCL directory entry */
433 if(fread(DDir,1,14,F)!=14) break;
434 /* Compute sector and track */
435 DDir[14] = K%D->Sectors;
436 DDir[15] = K/D->Sectors;
437 /* Next entry */
438 K += DDir[13];
439 DDir += 16;
440 }
441 /* Skip over remaining directory entries */
442 if(J<Buf[8]) fseek(F,(Buf[8]-J)*14,SEEK_CUR);
443 /* Build disk information */
444 memset(P+J*16,0,D->Sectors*D->SecSize-J*16);
445 memcpy(P+0x08E2,TRDDiskInfo,sizeof(TRDDiskInfo));
446 strncpy((char *)P+0x08F5,"SPECCY",8);
447 P[0x8E1] = K%D->Sectors; /* First free sector */
448 P[0x8E2] = K/D->Sectors; /* Track it belongs to */
449 P[0x8E3] = 0x16 + (D->Tracks>40? 0:1) + (D->Sides>1? 0:2);
450 P[0x8E4] = J; /* Number of files */
451 P[0x8E5] = (I-K)&0xFF; /* Number of free sectors */
452 P[0x8E6] = (I-K)>>8;
453 /* Read data */
454 for(DDir=P;J;--J,DDir+=16)
455 {
456 /* Determine data offset and size */
457 I = (DDir[15]*D->Sectors+DDir[14])*D->SecSize;
458 N = DDir[13]*D->SecSize;
459 /* Read .SCL data (ignore short reads!) */
460 fread(P+I,1,N,F);
461 }
462 /* Done */
463 P = D->Data;
464 break;
465
466 case FMT_HOBETA: /* If .$* format... */
467 /* Create a new disk image */
468 P = NewFDI(
469 D,
470 Formats[Format].Sides,
471 Formats[Format].Tracks,
472 Formats[Format].Sectors,
473 Formats[Format].SecSize
474 );
475 if(!P) { fclose(F);return(0); }
476 /* Read header */
477 if(fread(P,1,17,F)!=17) { fclose(F);return(0); }
478 /* Determine data offset and size */
479 I = D->Sectors*D->SecSize;
480 N = P[13]+((int)P[14]<<8);
481 /* Build disk information */
482 memset(P+16,0,I-16);
483 memcpy(P+0x08E2,TRDDiskInfo,sizeof(TRDDiskInfo));
484 strncpy((char*)P+0x08F5,"SPECCY",8);
485 K = D->Sectors+N;
486 J = D->Sectors*D->Tracks*D->Sides-K;
487 P[0x8E1] = K%D->Sectors; /* First free sector */
488 P[0x8E2] = K/D->Sectors; /* Track it belongs to */
489 P[0x8E3] = 0x16 + (D->Tracks>40? 0:1) + (D->Sides>1? 0:2);
490 P[0x8E4] = 1; /* Number of files */
491 P[0x8E5] = J&0xFF; /* Number of free sectors */
492 P[0x8E6] = J>>8;
493 N = N*D->SecSize; /* N is now in bytes */
494 /* Read data (ignore short reads!) */
495 fread(P+I,1,N,F);
496 /* Compute and check checksum */
497 for(L=I=0;I<15;++I) L+=P[I];
498 L = ((L*257+105)&0xFFFF)-P[15]-((int)P[16]<<8);
499 if(L) { /* @@@ DO SOMETHING BAD! */ }
500 /* Place file at track #1 sector #0, limit its size to 255 sectors */
501 P[13] = P[14]? 255:P[13];
502 P[14] = 0;
503 P[15] = 1;
504 P[16] = 0;
505 /* Done */
506 P = D->Data;
507 break;
508
509 default:
510 /* Format not recognized */
511 return(0);
512 }
513
514 if(D->Verbose)
515 printf(
516 "LoadFDI(): Loaded '%s', %d sides x %d tracks x %d sectors x %d bytes\n",
517 FileName,D->Sides,D->Tracks,D->Sectors,D->SecSize
518 );
519
520 /* Done */
521 fclose(F);
522 D->Data = P;
523 D->Format = Format;
524 return(Format);
525 }
526
527 /** SaveFDI() ************************************************/
528 /** Save a disk image to a given file, in a given format **/
529 /** (see FMT_* #defines). Use the original format when **/
530 /** when Format=FMT_AUTO. Returns format ID on success or **/
531 /** 0 on failure. **/
532 /*************************************************************/
SaveFDI(FDIDisk * D,const char * FileName,int Format)533 int SaveFDI(FDIDisk *D,const char *FileName,int Format)
534 {
535 byte S[32];
536 int I,J,K,C,L;
537 FILE *F;
538 byte *P,*T;
539
540 /* Must have a disk to save */
541 if(!D->Data) return(0);
542 /* Use original format if requested */
543 if(!Format) Format=D->Format;
544 /* Open file for writing */
545 if(!(F=fopen(FileName,"wb"))) return(0);
546
547 /* Depending on the format... */
548 switch(Format)
549 {
550 case FMT_FDI:
551 if(fwrite(D->Data,1,D->DataSize,F)!=D->DataSize)
552 {
553 fclose(F);
554 unlink(FileName);
555 return(0);
556 }
557 break;
558
559 case FMT_IMG:
560 /* Check the number of tracks and sides */
561 if((FDI_TRACKS(D->Data)!=Formats[Format].Tracks)||(FDI_SIDES(D->Data)!=Formats[Format].Sides))
562 {
563 fclose(F);
564 unlink(FileName);
565 return(0);
566 }
567 /* Scan through all sides,tracks,sectors */
568 L=Formats[Format].SecSize;
569 for(I=0;I<Formats[Format].Sides;++I)
570 for(J=0;J<Formats[Format].Tracks;++J)
571 for(K=0;K<Formats[Format].Sectors;++K)
572 {
573 P = SeekFDI(D,I,J,I,J,K+1);
574 C = D->SecSize<L? D->SecSize:L;
575 if(!P||(fwrite(P,1,C,F)!=C)) break;
576 }
577 /* If failed to write all sectors, clean up */
578 if(I<Formats[Format].Sides)
579 {
580 fclose(F);
581 unlink(FileName);
582 return(0);
583 }
584 break;
585
586 case FMT_TRD:
587 case FMT_MGT:
588 case FMT_SF7000:
589 /* Check the number of tracks and sides */
590 if((FDI_TRACKS(D->Data)!=Formats[Format].Tracks)||(FDI_SIDES(D->Data)!=Formats[Format].Sides))
591 {
592 fclose(F);
593 unlink(FileName);
594 return(0);
595 }
596 case FMT_DSK:
597 /* Scan through all tracks */
598 J = FDI_SIDES(D->Data)*FDI_TRACKS(D->Data);
599 for(P=FDI_DIR(D->Data);J;--J,P=T)
600 {
601 /* Compute total track length for this format */
602 L = Formats[Format].Sectors*Formats[Format].SecSize;
603 /* For every sector on a track, if track length remains... */
604 for(I=FDI_SECTORS(P),T=P+7;I;--I,T+=7)
605 if(L)
606 {
607 /* Write out a sector */
608 K = FDI_SECSIZE(T);
609 K = K>L? L:K;
610 L-= K;
611 if(fwrite(FDI_SECTOR(D->Data,P,T),1,K,F)!=K)
612 {
613 fclose(F);
614 unlink(FileName);
615 return(0);
616 }
617 }
618 /* Fill remaining track length with zeros */
619 if(L>0) fseek(F,L,SEEK_CUR);
620 }
621 /* Done */
622 break;
623
624 case FMT_SCL:
625 /* Get data pointer */
626 T=FDI_DATA(D->Data);
627 /* Check tracks, sides, sectors, and the TR-DOS magic number */
628 if((FDI_SIDES(D->Data)!=Formats[Format].Sides)
629 ||(FDI_TRACKS(D->Data)!=Formats[Format].Tracks)
630 ||(D->Sectors!=Formats[Format].Sectors)
631 ||(D->SecSize!=Formats[Format].SecSize)
632 ||(T[0x8E3]!=0x16)
633 )
634 {
635 fclose(F);
636 unlink(FileName);
637 return(0);
638 }
639 /* Write header */
640 strcpy((char *)S,"SINCLAIR");
641 S[8]=T[0x8E4];
642 if(fwrite(S,1,9,F)!=9)
643 {
644 fclose(F);
645 unlink(FileName);
646 return(0);
647 }
648 for(C=I=0;I<9;++I) C+=S[I];
649 /* Write directory entries */
650 for(J=0,P=T;J<T[0x8E4];++J,P+=16)
651 {
652 if(fwrite(P,1,14,F)!=14)
653 {
654 fclose(F);
655 unlink(FileName);
656 return(0);
657 }
658 for(I=0;I<14;++I) C+=P[I];
659 }
660 /* Write files */
661 for(J=0,P=T;J<T[0x8E4];++J,P+=16)
662 {
663 /* Determine data offset and size */
664 K = (P[15]*D->Sectors+P[14])*D->SecSize;
665 I = P[13]*D->SecSize;
666 /* Write data */
667 if(fwrite(T+K,1,I,F)!=I)
668 {
669 fclose(F);
670 unlink(FileName);
671 return(0);
672 }
673 /* Compute checksum */
674 for(L=K,I+=K;L<I;++L) C+=T[L];
675 }
676 /* Write checksum */
677 S[0] = C&0xFF;
678 S[1] = (C>>8)&0xFF;
679 S[2] = (C>>16)&0xFF;
680 S[3] = (C>>24)&0xFF;
681 if(fwrite(S,1,4,F)!=4)
682 {
683 fclose(F);
684 unlink(FileName);
685 return(0);
686 }
687 /* Done */
688 break;
689
690 case FMT_HOBETA:
691 /* Get data pointer */
692 T=FDI_DATA(D->Data);
693 /* Check tracks, sides, sectors, and the TR-DOS magic number */
694 if((FDI_SIDES(D->Data)!=Formats[Format].Sides)
695 ||(FDI_TRACKS(D->Data)!=Formats[Format].Tracks)
696 ||(D->Sectors!=Formats[Format].Sectors)
697 ||(D->SecSize!=Formats[Format].SecSize)
698 ||(T[0x8E3]!=0x16)
699 )
700 {
701 fclose(F);
702 unlink(FileName);
703 return(0);
704 }
705 /* Look for the first file */
706 for(J=0,P=T;(J<T[0x8E4])&&!P[0];++J,P+=16);
707 /* If not found, drop out */
708 if(J>=T[0x8E4])
709 {
710 fclose(F);
711 unlink(FileName);
712 return(0);
713 }
714 /* Copy header */
715 memcpy(S,P,14);
716 /* Get single file address and size */
717 I = P[13]*D->SecSize;
718 P = T+(P[14]+D->Sectors*P[15])*D->SecSize;
719 /* Compute checksum and build header */
720 for(C=J=0;J<14;++J) C+=P[J];
721 S[14] = 0;
722 C = (C+S[14])*257+105;
723 S[15] = C&0xFF;
724 S[16] = (C>>8)&0xFF;
725 /* Write header */
726 if(fwrite(S,1,17,F)!=17)
727 {
728 fclose(F);
729 unlink(FileName);
730 return(0);
731 }
732 /* Write file data */
733 if(fwrite(P,1,I,F)!=I)
734 {
735 fclose(F);
736 unlink(FileName);
737 return(0);
738 }
739 /* Done */
740 break;
741
742 default:
743 /* Can't save this format for now */
744 fclose(F);
745 unlink(FileName);
746 return(0);
747 }
748
749 /* Done */
750 fclose(F);
751 return(Format);
752 }
753
754 /** SeekFDI() ************************************************/
755 /** Seek to given side/track/sector. Returns sector address **/
756 /** on success or 0 on failure. **/
757 /*************************************************************/
SeekFDI(FDIDisk * D,int Side,int Track,int SideID,int TrackID,int SectorID)758 byte *SeekFDI(FDIDisk *D,int Side,int Track,int SideID,int TrackID,int SectorID)
759 {
760 byte *P,*T;
761 int J;
762
763 /* Have to have disk mounted */
764 if(!D||!D->Data) return(0);
765
766 switch(D->Format)
767 {
768 case FMT_TRD:
769 case FMT_DSK:
770 case FMT_SCL:
771 case FMT_FDI:
772 case FMT_MGT:
773 case FMT_IMG:
774 case FMT_CPCDSK:
775 case FMT_SF7000:
776 /* Track directory */
777 P = FDI_DIR(D->Data);
778 /* Find current track entry */
779 for(J=Track*D->Sides+Side%D->Sides;J;--J) P+=(FDI_SECTORS(P)+1)*7;
780 /* Find sector entry */
781 for(J=FDI_SECTORS(P),T=P+7;J;--J,T+=7)
782 if((T[0]==TrackID)&&(T[1]==SideID)&&(T[2]==SectorID)) break;
783 /* Fall out if not found */
784 if(!J) return(0);
785 /* FDI stores a header for each sector */
786 D->Header[0] = T[0];
787 D->Header[1] = T[1];
788 D->Header[2] = T[2];
789 D->Header[3] = T[3]<=3? T[3]:3;
790 D->Header[4] = T[4];
791 D->Header[5] = 0x00;
792 /* FDI has variable sector numbers and sizes */
793 D->Sectors = FDI_SECTORS(P);
794 D->SecSize = FDI_SECSIZE(T);
795 return(FDI_SECTOR(D->Data,P,T));
796 }
797
798 /* Unknown format */
799 return(0);
800 }
801
802