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