1 /*
2
3 Copyright 2021, dettus@dettus.net
4
5 Redistribution and use in source and binary forms, with or without modification,
6 are permitted provided that the following conditions are met:
7
8 1. Redistributions of source code must retain the above copyright notice, this
9 list of conditions and the following disclaimer.
10
11 2. Redistributions in binary form must reproduce the above copyright notice,
12 this list of conditions and the following disclaimer in the documentation
13 and/or other materials provided with the distribution.
14
15 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
26
27 */
28
29 // the purpose of this file is to read the Amstrad .DSK image files
30 // as well as the .DSK images from the spectrum128/spectrum+3
31 // and to translate them into the .mag/.gfx format.
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include "vm68k_macros.h"
37 #include "loader_common.h"
38 #include "loader_dsk.h"
39
40
41 #define DSK_IMAGESIZE 194816
42
43 #define MAXFILENAMELEN (8+3) // filenames in CPM are 8 bytes long. with a 3 byte extension
44 #define EXTENDLEN 4 // this part is 4 bytes long
45 #define MAXBLOCKS 16 // 16 pointers per directory entry
46
47 #define MAXBLOCKSIZE 1024 // one pointer is pointing towards 1 kByte
48 #define MINSECTORSIZE 128 // the smallest number of bytes in a sector
49 #define MAXOFFSETSPERENTRY ((MAXBLOCKSIZE/MINSECTORSIZE)*MAXBLOCKS)
50
51 #define SIZE_FILEHEADER 256
52 #define SIZE_TRACKHEADER 256
53 #define SIZE_SECTORHEADER 8
54 #define SIZE_DIRENTRY 32
55 #define MAX_DIRENTRIES 32 // TODO???
56 #define MAX_SECTORNUMPERTRACK 64
57 #define MAX_SECTORNUMPERDISK (DSK_IMAGESIZE/MINSECTORSIZE)
58 #define MAX_DISKS 2
59 #define NUM_GAMES 6
60 #define MAX_TRACKNUM 128 // TODO??
61
62 #define FILESUFFIX1 1
63 #define FILESUFFIX2 2
64 #define FILESUFFIX3 3
65 #define FILESUFFIX4 4
66 #define FILESUFFIX5 5
67 #define FILESUFFIX6 6
68 #define FILESUFFIX7 7
69 #define FILESUFFIX8 8
70
71 typedef struct _tGames
72 {
73 char gamename[32];
74 char gamefilename[MAXFILENAMELEN+1];
75 int version;
76 unsigned short expectedsuffixes_amstradcpc;
77 unsigned short expectedsuffixes_spectrum;
78 } tGames;
79 const tGames loader_dsk_knownGames[NUM_GAMES]={
80 // name,game file names, version number
81 {"The Pawn", "PAWN\0 ",0,(1<<FILESUFFIX1)|(1<<FILESUFFIX2)|(1<<FILESUFFIX3)|(1<<FILESUFFIX4), (1<<FILESUFFIX1)|(1<<FILESUFFIX2)|(1<<FILESUFFIX3)|(1<<FILESUFFIX4)},
82 {"The Guild of Thieves", "GUILD\0 ",1,(1<<FILESUFFIX1)|(1<<FILESUFFIX2)|(1<<FILESUFFIX3)|(1<<FILESUFFIX4)|(1<<FILESUFFIX5)|(1<<FILESUFFIX6)|(1<<FILESUFFIX7), (1<<FILESUFFIX1)|(1<<FILESUFFIX2)|(1<<FILESUFFIX3)|(1<<FILESUFFIX4)|(1<<FILESUFFIX5)},
83 {"Jinxter", "JINX\0 ",2,(1<<FILESUFFIX1)|(1<<FILESUFFIX2)|(1<<FILESUFFIX3)|(1<<FILESUFFIX4)|(1<<FILESUFFIX5)|(1<<FILESUFFIX6)|(1<<FILESUFFIX7)|(1<<FILESUFFIX8), (1<<FILESUFFIX1)|(1<<FILESUFFIX2)|(1<<FILESUFFIX3)|(1<<FILESUFFIX4)|(1<<FILESUFFIX5)},
84 {"Corruption", "CORR\0 ",3,(1<<FILESUFFIX1)|(1<<FILESUFFIX2)|(1<<FILESUFFIX3)|(1<<FILESUFFIX4)|(1<<FILESUFFIX5)|(1<<FILESUFFIX6)|(1<<FILESUFFIX7)|(1<<FILESUFFIX8), (1<<FILESUFFIX1)|(1<<FILESUFFIX2)|(1<<FILESUFFIX3)|(1<<FILESUFFIX4)|(1<<FILESUFFIX5)},
85 {"Fish! or Myth (close enough)","FILE\0 ",3,(1<<FILESUFFIX1)|(1<<FILESUFFIX2)|(1<<FILESUFFIX3)|(1<<FILESUFFIX4)|(1<<FILESUFFIX5)|(1<<FILESUFFIX6)|(1<<FILESUFFIX7)|(1<<FILESUFFIX8), (1<<FILESUFFIX1)|(1<<FILESUFFIX2)|(1<<FILESUFFIX3)|(1<<FILESUFFIX4)|(1<<FILESUFFIX5)},
86 {"Myth", "????\0 ",3,(1<<FILESUFFIX1)|(1<<FILESUFFIX2)|(1<<FILESUFFIX3)|(1<<FILESUFFIX4)|(1<<FILESUFFIX5)|(1<<FILESUFFIX6)|(1<<FILESUFFIX7)|(1<<FILESUFFIX8), (1<<FILESUFFIX1)|(1<<FILESUFFIX2)|(1<<FILESUFFIX3)|(1<<FILESUFFIX4)|(1<<FILESUFFIX5)}
87 };
88
89
90 typedef struct _tDirEntry
91 {
92 unsigned char userID;
93 char name[MAXFILENAMELEN+1];
94 unsigned char attrs;
95 unsigned char extend[EXTENDLEN];
96 unsigned char blocks[MAXBLOCKS];// block identifier
97
98 int fileID; // the "filename" without the prefix.
99 int offsets[MAXOFFSETSPERENTRY]; // translated offsets
100 } tDirEntry;
loader_dsk_descrambler(unsigned char * outputbuf,int len,unsigned short startvalue)101 void loader_dsk_descrambler(unsigned char* outputbuf,int len,unsigned short startvalue)
102 {
103 unsigned short value;
104 unsigned char key;
105 int i;
106
107 value=startvalue;
108 for (i=0;i<len;i++)
109 {
110 value=(value+((value<<8)+0x29))&0xffff;
111 key=(value^(value>>8))&0xff;
112 outputbuf[i]^=key;
113 }
114 }
loader_dsk_readfile(unsigned char * inputbuf,unsigned char * outputbuf,int fileID,tDirEntry * pDirEntries,int entrycnt,int sectorsize)115 int loader_dsk_readfile(unsigned char* inputbuf,unsigned char* outputbuf,int fileID,tDirEntry* pDirEntries,int entrycnt,int sectorsize)
116 {
117 int i;
118 int outputidx;
119 outputidx=0;
120 for (i=0;i<entrycnt;i++)
121 {
122 int j;
123 if (pDirEntries[i].fileID==fileID)
124 {
125 for (j=0;j<MAXOFFSETSPERENTRY;j++)
126 {
127 if (pDirEntries[i].offsets[j]!=-1)
128 {
129 memcpy(&outputbuf[outputidx],&inputbuf[pDirEntries[i].offsets[j]],sectorsize);
130 outputidx+=sectorsize;
131 }
132 }
133 }
134 }
135 return outputidx;
136
137 }
loader_dsk_amstradcpc_mag(unsigned char * magbuf,int * magsize,unsigned char * diskimage,int diskcnt,unsigned char * tmpbuf,int tmpbufsize,int gamedetected,tDirEntry * pDirEntries,int entrycnt,int sectorsize)138 int loader_dsk_amstradcpc_mag(unsigned char* magbuf,int* magsize,
139 unsigned char* diskimage,int diskcnt,
140 unsigned char* tmpbuf,int tmpbufsize,
141 int gamedetected,
142 tDirEntry* pDirEntries,int entrycnt,int sectorsize)
143 {
144 int outputidx;
145 int version;
146 int code1size;
147 int code2size;
148 int string1size;
149 int string2size;
150 int dictsize;
151
152 outputidx=42;
153 version=loader_dsk_knownGames[gamedetected].version;
154 if (version==0)
155 {
156 int tmpsize;
157 // in THE PAWN, the code section is packed.
158 tmpsize=loader_dsk_readfile(diskimage,tmpbuf,FILESUFFIX1,pDirEntries,entrycnt,sectorsize);
159 code1size=loader_common_unhuffer(tmpbuf,tmpsize,&magbuf[outputidx]);
160 outputidx+=code1size;
161 code2size=0;
162 } else {
163 int i;
164 // in all the other games, it is spread out over two files: FILE1 and FILE6.
165 code1size=loader_dsk_readfile(diskimage,&magbuf[outputidx],FILESUFFIX1,pDirEntries,entrycnt,sectorsize);
166 loader_dsk_descrambler(&magbuf[outputidx],code1size,0x1803); // the first part is scrambled different than the second one.
167 outputidx+=code1size;
168 code2size=loader_dsk_readfile(diskimage,&magbuf[outputidx],FILESUFFIX6,pDirEntries,entrycnt,sectorsize);
169 for (i=0;i<code2size;i+=0x80)
170 {
171 loader_dsk_descrambler(&magbuf[outputidx],0x80,code1size+i); // each 128 block has its own PRBS.
172 outputidx+=0x80;
173 }
174 }
175 string1size=loader_dsk_readfile(diskimage,&magbuf[outputidx],FILESUFFIX3,pDirEntries,entrycnt,sectorsize);
176 outputidx+=string1size;
177 if (version==0)
178 {
179 int tmpsize;
180 // in THE PAWN, the string2 section is packed.
181 tmpsize=loader_dsk_readfile(diskimage,tmpbuf,FILESUFFIX2,pDirEntries,entrycnt,sectorsize);
182 string2size=loader_common_unhuffer(tmpbuf,tmpsize,&magbuf[outputidx]);
183 outputidx+=string2size;
184 } else {
185 string2size=loader_dsk_readfile(diskimage,&magbuf[outputidx],FILESUFFIX2,pDirEntries,entrycnt,sectorsize);
186 outputidx+=string2size;
187 }
188 // some games have a file with the suffix 8, and this is for the dict.
189 dictsize=loader_dsk_readfile(diskimage,&magbuf[outputidx],FILESUFFIX8,pDirEntries,entrycnt,sectorsize);
190 loader_dsk_descrambler(&magbuf[outputidx],dictsize,0x1803); // the dictionary is scrambled the same way the code is
191 outputidx+=dictsize;
192
193 *magsize=outputidx;
194 return loader_common_addmagheader(magbuf,outputidx,version,code1size+code2size,string1size,string2size,dictsize,-1);
195 }
loader_dsk_spectrum_mag(unsigned char * magbuf,int * magsize,unsigned char * diskimage,int diskcnt,unsigned char * tmpbuf,int tmpbufsize,int gamedetected,tDirEntry * pDirEntries,int entrycnt,int sectorsize)196 int loader_dsk_spectrum_mag(unsigned char* magbuf,int* magsize,
197 unsigned char* diskimage,int diskcnt,
198 unsigned char* tmpbuf,int tmpbufsize,
199 int gamedetected,
200 tDirEntry* pDirEntries,int entrycnt,int sectorsize)
201 {
202 int codesize;
203 int string1size;
204 int string2size;
205 int dictsize;
206 int outputidx;
207 int version;
208
209 outputidx=42; // leave enough room for the header
210
211 // start with the code in FILE1, which is huffman encoded
212 codesize=loader_dsk_readfile(diskimage,tmpbuf,FILESUFFIX1,pDirEntries,entrycnt,sectorsize);
213 codesize=loader_common_unhuffer(tmpbuf,codesize,&magbuf[outputidx]);
214 outputidx+=codesize;
215 // the string1 section is in FILE3, and copied verbatim
216 string1size=loader_dsk_readfile(diskimage,&magbuf[outputidx],FILESUFFIX3,pDirEntries,entrycnt,sectorsize);
217 outputidx+=string1size;
218 // the string2 section is in FILE2, huffmanned
219 string2size=loader_dsk_readfile(diskimage,tmpbuf,FILESUFFIX2,pDirEntries,entrycnt,sectorsize);
220 string2size=loader_common_unhuffer(tmpbuf,string2size,&magbuf[outputidx]);
221 outputidx+=string2size;
222 // the dict section is in FILE4, huffed.
223 dictsize=loader_dsk_readfile(diskimage,tmpbuf,FILESUFFIX4,pDirEntries,entrycnt,sectorsize);
224 dictsize=loader_common_unhuffer(tmpbuf,dictsize,&magbuf[outputidx]);
225 outputidx+=dictsize;
226
227 version=loader_dsk_knownGames[gamedetected].version;
228 if (version==3 && magbuf[0x2836]==0x66) magbuf[0x2836]=0x60; // final patch for myth
229 *magsize=outputidx;
230
231
232 return loader_common_addmagheader(magbuf,outputidx,version,codesize,string1size,string2size,dictsize,-1);
233 }
loader_dsk_amstradcpc_gfx(unsigned char * gfxbuf,int * gfxsize,unsigned char * diskimage,int diskcnt,int gamedetected,tDirEntry * pDirEntries,int entrycnt,int sectorsize)234 int loader_dsk_amstradcpc_gfx(
235 unsigned char* gfxbuf,int* gfxsize,
236 unsigned char* diskimage,int diskcnt,
237 int gamedetected,
238 tDirEntry* pDirEntries,int entrycnt,int sectorsize)
239 {
240 int outputidx;
241 int outputidx0;
242 int outputidx1;
243 int i;
244 int version;
245
246 version=loader_dsk_knownGames[gamedetected].version;
247
248 outputidx=0;
249 gfxbuf[outputidx++]='M';
250 gfxbuf[outputidx++]='a';
251 gfxbuf[outputidx++]='P';
252 gfxbuf[outputidx++]='6';
253 outputidx0=outputidx;
254 if (version==0)
255 {
256 // THE PAWN uses a single file for the images and the index.
257
258 // since it is not 32 bit aligned, add 2 extra bytes.
259 gfxbuf[outputidx++]=0;
260 gfxbuf[outputidx++]=0;
261
262 // now just read the file
263 outputidx+=loader_dsk_readfile(diskimage,&gfxbuf[outputidx],FILESUFFIX4,pDirEntries,entrycnt,sectorsize);
264 // it is necessary to convert the image a little bit. the header has to be added, for example.
265 // and, just for fun, change the index to BIG endian as well
266 for (i=0;i<29;i++) // no more than 29 images
267 {
268 unsigned int x;
269 x=READ_INT32LE(gfxbuf,outputidx0+2+i*4);
270 x+=6;
271 x&=0xffffff;
272 WRITE_INT32BE(gfxbuf,outputidx0+i*4,x);
273 }
274
275
276 } else {
277 int idxoffs;
278 outputidx+=loader_dsk_readfile(diskimage,&gfxbuf[outputidx],FILESUFFIX4,pDirEntries,entrycnt,sectorsize);
279 outputidx=4+4*32; // due to limitations of the readfile() function, the data being read is too much
280 outputidx0=outputidx;
281 outputidx+=loader_dsk_readfile(diskimage,&gfxbuf[outputidx],FILESUFFIX5,pDirEntries,entrycnt,sectorsize);
282 outputidx1=outputidx;
283 outputidx+=loader_dsk_readfile(diskimage,&gfxbuf[outputidx],FILESUFFIX7,pDirEntries,entrycnt,sectorsize);
284 idxoffs=4;
285 for (i=0;i<32;i++)
286 {
287 unsigned int x;
288 x=READ_INT32LE(gfxbuf,idxoffs);
289 if (x&0xff000000) // MSB set, so the picture is in FILE5
290 {
291 x&=0xffffff;
292 x+=outputidx0;
293 } else {
294 x+=outputidx1; // MSB not set. so the picture is in FILE7
295 }
296 if (x<outputidx)
297 {
298 WRITE_INT32BE(gfxbuf,idxoffs,x);
299 } else {
300 WRITE_INT32BE(gfxbuf,idxoffs,0);
301 }
302 idxoffs+=4;
303 }
304
305 }
306 *gfxsize=outputidx;
307
308 return 0;
309 }
loader_dsk(char * amstradcpcname,char * magbuf,int * magsize,char * gfxbuf,int * gfxsize,int amstrad0spectrum1)310 int loader_dsk(char* amstradcpcname,
311 char *magbuf,int* magsize,
312 char* gfxbuf,int* gfxsize,
313 int amstrad0spectrum1)
314 {
315 char* filename[MAX_DISKS];
316 unsigned char *dskimage;
317 int gfxsize0;
318 int diskcnt;
319 int directorysector;
320 int offsets[MAX_DISKS][MAX_SECTORNUMPERDISK];
321 int retval;
322 int i,l;
323 int entrycnt;
324 int gamedetected;
325 int sectorsize;
326 unsigned short expectedsuffixes;
327 unsigned short foundsuffixes;
328 int blocksize;
329 tDirEntry dirEntries[MAX_DIRENTRIES];
330 gfxsize0=*gfxsize;
331 foundsuffixes=0;
332 sectorsize=0;
333 if (gfxsize0<4*DSK_IMAGESIZE)
334 {
335 fprintf(stderr,"not enough memory to load DSK images. sorry.\n");
336 return -1;
337 }
338 dskimage=(unsigned char*)&gfxbuf[2*DSK_IMAGESIZE];
339 filename[0]=&amstradcpcname[0];
340 filename[1]=&amstradcpcname[0];
341 diskcnt=1;
342 l=strlen(amstradcpcname);
343 for (i=0;i<l;i++)
344 {
345 if (amstradcpcname[i]==',')
346 {
347 amstradcpcname[i]=0;
348 filename[1]=&amstradcpcname[i+1];
349 diskcnt++;
350 if (diskcnt>2)
351 {
352 fprintf(stderr,"Please provide no more than 2 filenames, separated by ,\n");
353 return -1;
354 }
355 }
356 }
357 // load the game
358 for (i=0;i<diskcnt;i++)
359 {
360 int n;
361 FILE *f;
362 f=fopen(filename[i],"rb");
363 if (!f)
364 {
365 fprintf(stderr,"unable to open [%s]. Sorry.\n",filename[i]);
366 return -1;
367 }
368 n=fread(&dskimage[i*DSK_IMAGESIZE],sizeof(char),DSK_IMAGESIZE,f);
369 fclose(f);
370 if (n!=DSK_IMAGESIZE)
371 {
372 fprintf(stderr,"[%s] does not look like a DSK image\n",filename[i]);
373 return -1;
374 }
375 }
376
377
378 // start by finding the offsets to the sectors
379 for (i=0;i<diskcnt;i++)
380 {
381 int tracknum;
382 int sidenum;
383 int tracksize[MAX_TRACKNUM]={0};
384 int sectorcnt;
385 int idx;
386 int j;
387 int extendedornot=0;
388 // 0x00-0x21: "MV..." "Extended"
389 // 0x22.0x2f: creator ID
390 // 0x30: number of tracks
391 // 0x31: number of sides
392 // 0x32..33: size of the tracks unused
393 // 0x34..0xff: unused track size table
394 if (dskimage[i*DSK_IMAGESIZE]=='M') extendedornot=0;
395 else if (dskimage[i*DSK_IMAGESIZE]=='E') extendedornot=1;
396 else {
397 fprintf(stderr,"unable to determine the DSK format. Sorry.\n");
398 return -1;
399 }
400
401
402
403 tracknum=dskimage[i*DSK_IMAGESIZE+0x30];
404 sidenum=dskimage[i*DSK_IMAGESIZE+0x31];
405 if (extendedornot)
406 {
407 for (j=0;j<tracknum;j++)
408 {
409 tracksize[j]=dskimage[i*DSK_IMAGESIZE+0x34+j]*256;
410 }
411 } else {
412 for (j=0;j<tracknum;j++)
413 {
414 tracksize[j]=READ_INT16LE(dskimage,i*DSK_IMAGESIZE+0x32);
415 }
416 }
417
418 sectorcnt=0;
419 idx=i*DSK_IMAGESIZE+SIZE_FILEHEADER;
420
421 for (j=0;j<tracknum*sidenum;j++)
422 {
423 int sectorids[MAX_SECTORNUMPERTRACK]={0};
424 int order[MAX_SECTORNUMPERTRACK]={0};
425 int idx0;
426 int track0,side0;
427 int k;
428 int sectornum;
429
430 idx0=idx;
431 if (tracksize[j])
432 {
433 // 0..0x0b: Magic Word "Track..."
434 // 0x0c..0x0f: unused
435 idx+=0x0c; // skip the magic word
436 idx+=0x04; // skip the header
437 track0=dskimage[idx++];
438 side0=dskimage[idx++];
439 idx+=2; // skip over the unused
440
441 if ((track0*sidenum+side0)!=j)
442 {
443 fprintf(stderr,"sanity check 1 for the DSK file failed.\n");
444 return -1;
445
446 }
447
448 sectorsize=128<<((dskimage[idx++])&0xf); // 128, 256 512, 1024 bytes
449 sectornum=dskimage[idx++];
450 idx+=2; // skip over the gap3 length and the filler byte
451 if (sectornum>=MAX_SECTORNUMPERTRACK)
452 {
453 fprintf(stderr,"this file has too many sectors\n");
454 return -1;
455 }
456 // after the track header comes the sector header
457 for (k=0;k<sectornum;k++)
458 {
459 int track1;
460 int side1;
461 // 0x00: track number
462 // 0x01: side number
463 // 0x02: sector ID
464 // 0x03: sector size
465 // 0x04..0x05: fdx status
466 // 0x06..0x07: unused
467
468 order[k]=k;
469 track1=dskimage[idx++];
470 side1=dskimage[idx++];
471 if (track1!=track0 || side1!=side0)
472 {
473 fprintf(stderr,"sanity check 2 for the DSK file failed.\n");
474 return -1;
475 }
476 sectorids[k]=dskimage[idx++];
477 idx+=3; // skip sector size and fdc status
478 idx+=2; // skip over unused bytes
479 }
480 // sort them. I am too lazy to implement a faster sorting algorithm
481 for (k=0;k<sectornum-1;k++)
482 {
483 int l;
484 for (l=k+1;l<sectornum;l++)
485 {
486 if (sectorids[order[k]]>sectorids[order[l]])
487 {
488 // swap them
489 order[k]^=order[l];
490 order[l]^=order[k];
491 order[k]^=order[l];
492 }
493 }
494 }
495 for (k=0;k<sectornum;k++)
496 {
497 offsets[i][sectorcnt++]=idx0+SIZE_TRACKHEADER+order[k]*sectorsize;
498 }
499 idx=idx0+tracksize[i];
500 }
501 }
502 }
503
504
505 ////// step 2: read the directory. now we known where it is.
506 entrycnt=0;
507 gamedetected=-1;
508 if (amstrad0spectrum1)
509 {
510 int sectorspertrack;
511 int reservedtracks;
512 // Within the Spectrum Disks, the first track contains the following information:
513 //
514 //Byte 0 Disk type
515 // 0 = Standard PCW range DD SS ST (and +3)
516 // 1 = Standard CPC range DD SS ST system format
517 // 2 = Standard CPC range DD SS ST data only format
518 // 3 = Standard PCW range DD DS DT
519 // All other values reserved
520 //Byte 1 Bits 0...1 Sidedness
521 // 0 = Single sided
522 // 1 = Double sided (alternating sides)
523 // 2 = Double sided (successive sides)
524 // Bits 2...6 Reserved (set to 0)
525 // Bit 7 Double track
526 //Byte 2 Number of tracks per side
527 //Byte 3 Number of sectors per track
528 //Byte 4 Log2(sector size) - 7
529 //Byte 5 Number of reserved tracks
530 //Byte 6 Log2(block size / 128)
531 //Byte 7 Number of directory blocks
532 //Byte 8 Gap length (read/write)
533 //Byte 9 Gap length (format)
534 //Bytes 10...14 Reserved
535 //Byte 15 Checksum (used only if disk is bootable)
536
537 // most of this information is redundant/unnecessary.
538 // except for the ones needed to find the track containing the directory
539
540 sectorspertrack=dskimage[offsets[0][0]+3];
541 reservedtracks=dskimage[offsets[0][0]+5];
542 blocksize=128<<dskimage[offsets[0][0]+6];
543
544 directorysector=sectorspertrack*reservedtracks;
545
546 } else {
547 directorysector=0;
548 blocksize=MAXBLOCKSIZE;
549 }
550 for (i=0;i<diskcnt;i++)
551 {
552 int j,k,l;
553 int validfilename;
554 unsigned char *ptr;
555 for (j=0;j<(blocksize/sectorsize)*2;j++)
556 {
557 for (k=0;k<sectorsize;k+=SIZE_DIRENTRY)
558 {
559 ptr=&dskimage[offsets[i][j+directorysector]+k];
560 if (ptr[0]==0)
561 {
562 dirEntries[entrycnt].userID=ptr[0];
563 for (l=0;l<MAXFILENAMELEN;l++)
564 {
565 dirEntries[entrycnt].name[l]=ptr[1+l]&0x7f;
566 }
567 dirEntries[entrycnt].name[MAXFILENAMELEN]=0;
568 dirEntries[entrycnt].fileID=-1;
569 validfilename=0;
570 // if (gamedetected==-1)
571 {
572 int m;
573 for (m=0;m<NUM_GAMES;m++)
574 {
575 if (strncmp(dirEntries[entrycnt].name,loader_dsk_knownGames[m].gamefilename,strlen(loader_dsk_knownGames[m].gamefilename))==0
576 && (dirEntries[entrycnt].name[strlen(loader_dsk_knownGames[m].gamefilename)]>='0' && dirEntries[entrycnt].name[strlen(loader_dsk_knownGames[m].gamefilename)]<='8')
577 )
578 {
579 gamedetected=m;
580 validfilename=1;
581 }
582 }
583 }
584 if (gamedetected!=-1)
585 {
586 int m;
587 m=strlen(loader_dsk_knownGames[gamedetected].gamefilename);
588 if (strncmp(dirEntries[entrycnt].name,loader_dsk_knownGames[gamedetected].gamefilename,m)==0)
589 {
590 dirEntries[entrycnt].fileID=dirEntries[entrycnt].name[m]-'0';
591 if (dirEntries[entrycnt].fileID>=0 && dirEntries[entrycnt].fileID<=8)
592 {
593 foundsuffixes|=(1<<dirEntries[entrycnt].fileID);
594 }
595 }
596 dirEntries[entrycnt].attrs=(ptr[ 9]>>5)&4;
597 dirEntries[entrycnt].attrs|=(ptr[10]>>6)&2;
598 dirEntries[entrycnt].attrs|=(ptr[11]>>7)&1;
599 for (l=0;l<EXTENDLEN;l++) dirEntries[entrycnt].extend[l]=ptr[12+l];
600 for (l=0;l<MAXOFFSETSPERENTRY;l++) dirEntries[entrycnt].offsets[l]=-1;
601 for (l=0;l<MAXBLOCKS;l++)
602 {
603 int m;
604 int n;
605
606 n=blocksize/sectorsize;
607 dirEntries[entrycnt].blocks[l]=ptr[16+l];
608 if (dirEntries[entrycnt].blocks[l]==0)
609 {
610 for (m=0;m<n;m++)
611 {
612 dirEntries[entrycnt].offsets[l*n+m]=-1;
613 }
614 } else {
615
616 for (m=0;m<n;m++)
617 {
618 dirEntries[entrycnt].offsets[l*n+m]=offsets[i][dirEntries[entrycnt].blocks[l]*n+m+directorysector];
619 }
620 }
621 }
622 if (entrycnt<MAX_DIRENTRIES && validfilename) entrycnt++;
623 }
624 }
625 }
626 }
627 }
628
629 if (gamedetected==-1)
630 {
631 fprintf(stderr,"!! Unable to detect the game. Sorry\n\n");
632 return 1;
633 }
634 printf("DSK Loader detected '%s'\n",loader_dsk_knownGames[gamedetected].gamename);
635 expectedsuffixes=0;
636 if (amstrad0spectrum1)
637 {
638 expectedsuffixes=loader_dsk_knownGames[gamedetected].expectedsuffixes_spectrum;
639 } else {
640 expectedsuffixes=loader_dsk_knownGames[gamedetected].expectedsuffixes_amstradcpc;
641 }
642 if ((foundsuffixes&0x1fe)!=(0x1fe&expectedsuffixes))
643 {
644 unsigned short s;
645 s=expectedsuffixes&0x1fe;
646 fprintf(stderr,"expected ");
647 for (i=0;i<9;i++)
648 {
649 if (s&1) fprintf(stderr,"%s%d ",loader_dsk_knownGames[gamedetected].gamefilename,i);
650 s>>=1;
651 }
652 fprintf(stderr,"\n");
653 s=foundsuffixes&0x1fe;
654 fprintf(stderr,"found ");
655 for (i=0;i<9;i++)
656 {
657 if (s&1) fprintf(stderr,"%s%d ",loader_dsk_knownGames[gamedetected].gamefilename,i);
658 s>>=1;
659 }
660 fprintf(stderr,"\n");
661
662 fprintf(stderr,"!! Some files missing. Sorry\n\n");
663 return 1;
664 }
665 if (amstrad0spectrum1)
666 {
667 retval=loader_dsk_spectrum_mag((unsigned char*)magbuf,magsize,
668 dskimage,diskcnt,
669 (unsigned char*)gfxbuf,2*DSK_IMAGESIZE,
670 gamedetected,
671 dirEntries,entrycnt,sectorsize);
672
673
674 *gfxsize=0;
675 } else {
676 // start with the magbuf. use the first half of the empty gfx buf as tmpbuf for the unhuff
677 retval=loader_dsk_amstradcpc_mag((unsigned char*)magbuf,magsize,
678 dskimage,diskcnt,
679 (unsigned char*)gfxbuf,2*DSK_IMAGESIZE,
680 gamedetected,
681 dirEntries,entrycnt,sectorsize);
682 if (retval) return retval;
683
684 retval=loader_dsk_amstradcpc_gfx((unsigned char*)gfxbuf,gfxsize,
685 dskimage,diskcnt,
686 gamedetected,
687 dirEntries,entrycnt,sectorsize);
688 }
689 return retval;
690
691
692 }
693
694