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 D64 disk image files
30 // and to translate them into the .mag/.gfx format.
31 
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include "loader_appleii.h"
36 #include "loader_common.h"
37 #include "configuration.h"
38 #include "vm68k_macros.h"
39 
40 #define	PREAMBLE_SIZE	3
41 #define	EPILOGUE_SIZE	3
42 #define	ADDRBUF_SIZE	8
43 #define	DATABUF_SIZE	343
44 
45 #define	MAXDISKS	3
46 #define	MAXTRACKS	35
47 #define	MAXSECTORS	16
48 #define	SECTORBYTES	256
49 #define	DSKTRACKSIZE	(MAXSECTORS*SECTORBYTES)
50 #define	DSKSIZE		(MAXTRACKS*MAXSECTORS*SECTORBYTES)
51 #define	NIBTRACKSIZE	(MAXSECTORS*416)	// TODO: why 416?
52 
53 
54 
55 #define	GAME_PAWN	0
56 #define	GAME_GUILD	1
57 #define	GAME_JINXTER	2
58 #define	GAME_CORRUPTION	3
59 #define	MAXPICTURES	32
60 
61 
62 
63 
64 #define	WOZ_MAXQUARTERTRACKS	(4*MAXTRACKS)		// TODO: not sure what to do with this...
65 #define	WOZ_BLOCKSIZE		512			// the woz format has been designed to work on SD cards. So blocks are aligned to 512 to speed up processing
66 
67 
68 // the most important information, parsed from the WOZ header
69 typedef struct _tWozInfo
70 {
71 	int quarterTrack[WOZ_MAXQUARTERTRACKS];	// TODO: not sure what to do with this...
72 	int trackStart[WOZ_MAXQUARTERTRACKS];	// the block offset within the WOZ file
73 	int trackBits[WOZ_MAXQUARTERTRACKS];	// the number of bits for a track
74 	unsigned int crc32_expected;
75 } tWozInfo;
76 
77 
loader_appleii_woz_parseheader(unsigned char * pWozBuf,int wozsize,tWozInfo * pWozInfo)78 int loader_appleii_woz_parseheader(unsigned char* pWozBuf,int wozsize,tWozInfo *pWozInfo)
79 {
80 	int idx;
81 	int len;
82 	unsigned char donemask;
83 	idx=0;
84 	donemask=0;
85 	while (idx<wozsize && donemask!=3)
86 	{
87 		if (memcmp(&pWozBuf[idx],"WOZ2",4)==0)
88 		{
89 			if (pWozBuf[idx+4]!=0xff || pWozBuf[idx+5]!=0xa || pWozBuf[idx+6]!=0xd || pWozBuf[idx+7]!=0xa)
90 			{
91 				fprintf(stderr,"       WOZ2 eader corruption? Expected FF 0A 0D 0A, got %02X %02X %02X %02X \n",pWozBuf[idx+4],pWozBuf[idx+5],pWozBuf[idx+6],pWozBuf[idx+7]);
92 				return -1;
93 			}
94 			pWozInfo->crc32_expected=READ_INT32BE(pWozBuf,idx+8);
95 			idx+=12;
96 		}
97 		else if (memcmp(&pWozBuf[idx],"INFO",4)==0)
98 		{
99 			len=READ_INT32LE(pWozBuf,idx+4);
100 			idx+=8;
101 			// skip the info chunk
102 			idx+=len;
103 		}
104 		else if (memcmp(&pWozBuf[idx],"TMAP",4)==0)
105 		{
106 			int i;
107 			len=READ_INT32LE(pWozBuf,idx+4);
108 			idx+=8;
109 			for (i=0;i<WOZ_MAXQUARTERTRACKS;i++)
110 			{
111 				unsigned char x;
112 				x=pWozBuf[idx+i];
113 				if (x>=0 && x<MAXTRACKS)
114 				{
115 					pWozInfo->quarterTrack[i]=x;
116 				}
117 			}
118 			idx+=len;
119 			donemask|=1;
120 		}
121 		else if (memcmp(&pWozBuf[idx],"TRKS",4)==0)
122 		{
123 			int i;
124 			int idx2;
125 			len=READ_INT32LE(pWozBuf,idx+4);
126 			idx+=8;
127 			idx2=idx;
128 			for (i=0;i<WOZ_MAXQUARTERTRACKS;i++)
129 			{
130 				pWozInfo->trackStart[i]=WOZ_BLOCKSIZE*READ_INT16LE(pWozBuf,idx2);idx2+=2;
131 				idx2+=2;		//skip the block count
132 				pWozInfo->trackBits[i]=READ_INT32LE(pWozBuf,idx2);idx2+=4;
133 			}
134 			idx+=len;
135 			donemask|=2;
136 
137 		}
138 		else if (memcmp(&pWozBuf[idx],"META",4)==0)
139 		{
140 			len=READ_INT32LE(pWozBuf,idx+4);
141 			idx+=8;
142 			// skip the info chunk
143 			idx+=len;
144 		}
145 		else
146 		{
147 			fprintf(stderr,"Unknown Tag in WOZ2 detected %02X %02X %02X %02X\n",pWozBuf[idx+0],pWozBuf[idx+1],pWozBuf[idx+2],pWozBuf[idx+3]);
148 			return -1;
149 		}
150 	}
151 	if (donemask!=3)
152 	{
153 		fprintf(stderr," Error parsing the WOZ header\n");
154 		return -1;
155 	}
156 	return 0;
157 }
158 // when the woz bit stream is synchronized, it can be interpreted as a nib stream.
loader_appleii_woz_synchronize(unsigned char * trackbuf,unsigned char * wozbuf,int len)159 int loader_appleii_woz_synchronize(unsigned char* trackbuf,unsigned char* wozbuf,int len)
160 {
161 	int i;
162 	unsigned char byte;
163 	unsigned char bit;
164 	unsigned int reg;
165 	int addrcnt;
166 	int datacnt;
167 	int part_cnt;
168 	int outidx;
169 
170 
171 
172 	reg=0;
173 	byte=0;
174 	bit=0;
175 	addrcnt=0;
176 	datacnt=0;
177 	part_cnt=0;
178 	i=0;
179 	outidx=0;
180 	for (i=0;i<NIBTRACKSIZE;i++) trackbuf[i]=0xff;	// initialize
181 	while (outidx<NIBTRACKSIZE && i<(len*2) && (part_cnt!=0 || addrcnt!=MAXSECTORS || datacnt!=MAXSECTORS))
182 	{
183 		int wozbyte;
184 		int wozbit;
185 
186 		wozbyte=(i%len)/8;
187 		wozbit=(i%len)%8;
188 		bit=(wozbuf[wozbyte]>>(7-wozbit))&1;
189 		byte<<=1;
190 		byte|=bit;
191 		if (byte&0x80)	// byte is synchronized when the highest bit is set.
192 		{
193 			reg<<=8;
194 			reg|=((unsigned int)byte)&0xff;
195 			reg&=0x00ffffff;
196 			if (part_cnt==0)
197 			{
198 				if (reg==0xD5AA96)	// addr preamble found
199 				{
200 					addrcnt++;
201 					trackbuf[outidx++]=0xD5;		// write the preamble
202 					trackbuf[outidx++]=0xAA;
203 					trackbuf[outidx++]=0x96;
204 					part_cnt=ADDRBUF_SIZE+EPILOGUE_SIZE;	// collect 11 bytes
205 				}
206 				if (reg==0xD5AAAD)	// data preamble found
207 				{
208 					datacnt++;
209 					trackbuf[outidx++]=0xD5;		// write the preamble
210 					trackbuf[outidx++]=0xAA;
211 					trackbuf[outidx++]=0xAD;
212 					part_cnt=DATABUF_SIZE+EPILOGUE_SIZE;	// collect 346 bytes
213 				}
214 			} else {
215 				trackbuf[outidx++]=byte;
216 				part_cnt--;
217 			}
218 			byte=0;
219 		}
220 		i++;
221 	}
222 	// at this point, the trackbuf contains the NIB stream, even though there is no padding between the sectors.
223 	// the nib decoder will be able to handle it, even though a physical drive might not be able to.
224 	return 0;
225 }
226 
loader_appleii_decode_addrbuf(unsigned char * pAddrBuf,unsigned char * volume,unsigned char * track,unsigned char * sector,unsigned char * checksum)227 int loader_appleii_decode_addrbuf(unsigned char* pAddrBuf,unsigned char* volume,unsigned char* track,unsigned char* sector,unsigned char* checksum)
228 {
229 	const unsigned char loader_appleii_deinterleave[16]={ 0x0,0x7,0xe,0x6,0xd,0x5,0xc,0x4,0xb,0x3,0xa,0x2,0x9,0x1,0x8,0xf };
230 	int ridx;
231 	unsigned char x;
232 	unsigned char check;
233 	ridx=0;
234 	check=0;
235 #define	ROL(x)	((((x)&0x80)>>7|(x)<<1)&0xff)
236 	x=pAddrBuf[ridx++];x=ROL(x);x&=pAddrBuf[ridx++];*volume=x;check^=x;
237 	x=pAddrBuf[ridx++];x=ROL(x);x&=pAddrBuf[ridx++];*track=x;check^=x;
238 	x=pAddrBuf[ridx++];x=ROL(x);x&=pAddrBuf[ridx++];*sector=loader_appleii_deinterleave[x&0xf];check^=x;
239 	x=pAddrBuf[ridx++];x=ROL(x);x&=pAddrBuf[ridx++];*checksum=x;check^=x;
240 	if (check)
241 	{
242 		fprintf(stderr,"Warning. Checksum mismatch\n");
243 	}
244 	return check;
245 }
246 
loader_appleii_decodenibtrack(unsigned char * pTrackBuf,int track,unsigned char * pDskBuf)247 int loader_appleii_decodenibtrack(unsigned char* pTrackBuf,int track,unsigned char* pDskBuf)
248 {
249 #define	PREAMBLESIZE	3
250 #define	DECODEROFFS	0x96
251 #define	SECTORLSB	86
252 	const	unsigned char loader_appleii_addr_preamble[PREAMBLESIZE]={0xD5,0xAA,0x96};
253 	const	unsigned char loader_appleii_data_preamble[PREAMBLESIZE]={0xD5,0xAA,0xAD};
254 	//const	unsigned char loader_appleii_epilog[PREAMBLESIZE]={0xDE,0xAA,0xEB};
255 
256 	const	unsigned char loader_appleii_translatetab[106]={
257 		0x00,0x01,0xFF,0xFF,0x02,0x03,0xFF,0x04,
258 		0x05,0x06,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
259 		0x07,0x08,0xFF,0xFF,0xFF,0x09,0x0A,0x0B,
260 		0x0C,0x0D,0xFF,0xFF,0x0E,0x0F,0x10,0x11,
261 		0x12,0x13,0xFF,0x14,0x15,0x16,0x17,0x18,
262 		0x19,0x1A,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
263 		0xFF,0xFF,0xFF,0xFF,0xFF,0x1B,0xFF,0x1C,
264 		0x1D,0x1E,0xFF,0xFF,0xFF,0x1F,0xFF,0xFF,
265 		0x20,0x21,0xFF,0x22,0x23,0x24,0x25,0x26,
266 		0x27,0x28,0xFF,0xFF,0xFF,0xFF,0xFF,0x29,
267 		0x2A,0x2B,0xFF,0x2C,0x2D,0x2E,0x2F,0x30,
268 		0x31,0x32,0xFF,0xFF,0x33,0x34,0x35,0x36,
269 		0x37,0x38,0xFF,0x39,0x3A,0x3B,0x3C,0x3D,
270 		0x3E,0x3F};
271 
272 
273 	unsigned char addr_track=0;
274 	unsigned char addr_sector=0;
275 	unsigned char addr_volume=0;
276 	unsigned char addr_checksum=0;
277 	int volumeid;
278 	int foundsectors;
279 	int ridx;
280 	int state;
281 
282 	volumeid=-1;
283 	foundsectors=0;
284 	ridx=0;
285 	state=0;
286 	while (ridx<(NIBTRACKSIZE+SECTORBYTES+SECTORLSB+1+9*PREAMBLESIZE))
287 	{
288 		switch(state)
289 		{
290 			case 0:		// find the ADDR preamble
291 				{
292 					if ( pTrackBuf[(ridx+0)%NIBTRACKSIZE]==loader_appleii_addr_preamble[0] && pTrackBuf[(ridx+1)%NIBTRACKSIZE]==loader_appleii_addr_preamble[1] && pTrackBuf[(ridx+2)%NIBTRACKSIZE]==loader_appleii_addr_preamble[2])
293 					{
294 						ridx+=PREAMBLE_SIZE;
295 						state=1;
296 						foundsectors++;
297 					} else ridx++;
298 				}
299 				break;
300 			case 1:		// decode the ADDR data
301 				{
302 					unsigned char addrbuf[ADDRBUF_SIZE];
303 					int i;
304 					for (i=0;i<ADDRBUF_SIZE;i++)
305 					{
306 						addrbuf[i]=pTrackBuf[(ridx++)%NIBTRACKSIZE];
307 					}
308 					loader_appleii_decode_addrbuf(addrbuf,&addr_volume,&addr_track,&addr_sector,&addr_checksum);
309 					if (volumeid==-1 || volumeid==addr_volume)
310 					{
311 						volumeid=addr_volume;
312 					} else {
313 						printf("volumeid mismatch\n");
314 						return -1;
315 					}
316 					if (addr_track!=track)
317 					{
318 						printf("track mismatch %d vs %d\n",addr_track,track);
319 						return -1;
320 					}
321 					ridx+=PREAMBLESIZE;	// skip over the epilogue
322 					state=2;	// start looking for data
323 				}
324 				break;
325 			case 2:		// find the DATA preamble
326 				{
327 					if ( pTrackBuf[(ridx+0)%NIBTRACKSIZE]==loader_appleii_data_preamble[0] && pTrackBuf[(ridx+1)%NIBTRACKSIZE]==loader_appleii_data_preamble[1] && pTrackBuf[(ridx+2)%NIBTRACKSIZE]==loader_appleii_data_preamble[2])
328 					{
329 						ridx+=PREAMBLE_SIZE;
330 						state=3;
331 					} else ridx++;
332 				}
333 				break;
334 			case 3:		// decode the DATA
335 				{
336 					unsigned char lsbbuf[SECTORLSB];
337 					unsigned char accu;
338 					int j;
339 					accu=0;
340 					for (j=0;j<SECTORLSB;j++)
341 					{
342 						accu^=loader_appleii_translatetab[pTrackBuf[ridx%NIBTRACKSIZE]-DECODEROFFS];
343 						lsbbuf[j]=accu;
344 						ridx++;
345 					}
346 					for (j=0;j<SECTORBYTES;j++)
347 					{
348 						int widx;
349 						accu^=loader_appleii_translatetab[pTrackBuf[ridx%NIBTRACKSIZE]-DECODEROFFS];
350 						widx=j+SECTORBYTES*addr_sector;
351 						pDskBuf[widx]=accu;
352 						pDskBuf[widx]<<=1;pDskBuf[widx]|=(1&lsbbuf[j%SECTORLSB]);lsbbuf[j%SECTORLSB]>>=1;
353 						pDskBuf[widx]<<=1;pDskBuf[widx]|=(1&lsbbuf[j%SECTORLSB]);lsbbuf[j%SECTORLSB]>>=1;
354 						ridx++;
355 					}
356 					ridx+=PREAMBLESIZE;	// skip over the epilogue
357 					state=0;		// search for the next ADDR preamble
358 				}
359 				break;
360 		}
361 
362 	}
363 	return volumeid;
364 }
365 
loader_appleii_mkgfx(unsigned char * gfxbuf,int * gfxsize,int gameid,int diskcnt,int * pDskOffs)366 int loader_appleii_mkgfx(unsigned char *gfxbuf,int* gfxsize,int gameid,int diskcnt,int *pDskOffs)
367 {
368 #define	PICTURE_HOTFIX1		0x80000000
369 #define	PICTURE_HOTFIX2		0x40000000
370 #define	PICTURE_HOTFIX3		0x20000000
371 #define	PICTURENUM		26
372 #define	CODESECTIONS		5
373 #define	TOTALSECTIONS		(PICTURENUM+CODESECTIONS)
374 
375 	unsigned int hotfix1=(1<<1)|(1<< 7);
376 	unsigned int hotfix2=(1<<2)|(1<<13);
377 	unsigned int hotfix3=(1<<16);
378 
379 	if (gameid!=GAME_CORRUPTION)
380 	{
381 		*gfxsize=0;
382 		return 0;
383 	}
384 	if (diskcnt!=MAXDISKS)
385 	{
386 		fprintf(stderr,"wrong number of floppy disks\n");
387 		return -1;
388 	}
389 	*gfxsize=4+4*32+diskcnt*DSKSIZE;
390 	{
391 		unsigned char mask;
392 		unsigned char byte;
393 #define	UNHUFFSTART	0x00a00
394 #define	DIR_START	0x997
395 #define	DIR_END		0x9d5
396 
397 		int outidx;
398 		int tracks[MAXPICTURES];
399 		int sectors[MAXPICTURES];
400 		int i;
401 		int cnt;
402 		int unhuffsize;
403 		unsigned char terminal;
404 		int treeoffs;
405 		int bitidx;
406 		int unhuffoffs;
407 		int treeidx=0;
408 		outidx=0;
409 		cnt=0;
410 
411 		unhuffoffs=4+4*MAXPICTURES+pDskOffs[0]+UNHUFFSTART;
412 		terminal=0;
413 		unhuffsize=READ_INT16LE(gfxbuf,unhuffoffs);
414 		treeoffs=unhuffoffs+2;
415 		bitidx=treeoffs+16+16;
416 		treeidx=0;
417 		mask=0;
418 		byte=0;
419 		while (outidx<unhuffsize || mask)
420 		{
421 			unsigned char branchl,branchr;
422 			unsigned char branch;
423 
424 			if (mask==0x00)
425 			{
426 				mask=0x80;
427 				byte=gfxbuf[bitidx++];
428 			}
429 
430 			branchl=gfxbuf[treeoffs+ 0+treeidx];
431 			branchr=gfxbuf[treeoffs+16+treeidx];
432 			branch=(byte&mask)?branchl:branchr;
433 			mask>>=1;
434 
435 			if (branch&0x80)
436 			{
437 				treeidx=branch&0xf;
438 			} else {
439 				treeidx=0;
440 				terminal<<=4;
441 				terminal|=(branch&0xf);
442 				terminal&=0xff;
443 				if (outidx>=(DIR_START*2) && outidx<=(DIR_END*2) && ((outidx&1)==1))
444 				{
445 					if (cnt<TOTALSECTIONS) tracks[cnt]=terminal;
446 					else {
447 						sectors[cnt-TOTALSECTIONS]=terminal;
448 					}
449 					cnt++;
450 				}
451 				outidx++;
452 			}
453 		}
454 		// read the locations of the pictures. skip over the code sections
455 		for (i=0;i<PICTURENUM;i++)
456 		{
457 			unsigned int offs;
458 			offs=4+MAXPICTURES*4;
459 
460 
461 			offs+=((tracks[i+CODESECTIONS]&0x1f)<<12);
462 			offs+=(sectors[i+CODESECTIONS]<<8);
463 			if (tracks[i+CODESECTIONS]&0x80) offs+=pDskOffs[2];		// picture is on disk 3
464 			else if (tracks[i+CODESECTIONS]&0x40) offs+=pDskOffs[1];	// picture is on disk 2
465 			else offs+=pDskOffs[0];						// picture is on disk 1
466 
467 			if (hotfix1&1) offs|=PICTURE_HOTFIX1;
468 			if (hotfix2&1) offs|=PICTURE_HOTFIX2;
469 			if (hotfix3&1) offs|=PICTURE_HOTFIX3;
470 			hotfix1>>=1;
471 			hotfix2>>=1;
472 			hotfix3>>=1;
473 			WRITE_INT32BE(gfxbuf,4+i*4,offs);
474 		}
475 
476 	}
477 
478 
479 	gfxbuf[0]='M';gfxbuf[1]='a';gfxbuf[2]='P';gfxbuf[3]='8';
480 
481 	return 0;
482 }
483 typedef	 struct _tSection
484 {
485 	int track;
486 	int sector;
487 	int disk;
488 	int len;
489 	int scrambled;
490 	int rle;
491 } tSection;
loader_appleii_readsection(unsigned char * pOut,tSection section,unsigned char * pDskBuf,int diskcnt,int * pDskOffs,int pivot)492 int loader_appleii_readsection(unsigned char* pOut,tSection section,unsigned char* pDskBuf,int diskcnt,int* pDskOffs,int pivot)
493 {
494 	int idx;
495 	int outidx;
496 	int firstsector;
497 	int rle;
498 	int rlecutoff=DSKSIZE;
499 	unsigned char tmp[SECTORBYTES];
500 	unsigned char lc;
501 	int i;
502 
503 	rle=section.rle;
504 	outidx=0;
505 	firstsector=1;
506 	idx=(section.track*MAXSECTORS+section.sector)*SECTORBYTES+pDskOffs[section.disk];
507 	lc=0xff;
508 
509 	while (outidx<section.len)
510 	{
511 		int ridx;
512 		int removeendmarker;
513 		memcpy(tmp,&pDskBuf[idx],SECTORBYTES);
514 		idx+=SECTORBYTES;
515 		ridx=0;
516 		removeendmarker=0;
517 		if (section.scrambled)
518 		{
519 			loader_common_descramble(tmp,tmp,pivot,NULL,0);
520 			pivot=(pivot+1)%8;
521 			if (firstsector && rle)
522 			{
523 				rlecutoff=READ_INT16BE(tmp,0);
524 				ridx=2;
525 				firstsector=0;
526 			}
527 		}
528 		for (;ridx<SECTORBYTES;ridx++)
529 		{
530 			unsigned char c;
531 			int n;
532 			c=tmp[ridx];
533 			if (lc!=0 || !rle)
534 			{
535 				n=1;
536 				lc=c;
537 			} else {
538 				lc=c;
539 				n=c-1;
540 				c=0;
541 			}
542 			for (i=0;i<n;i++)
543 			{
544 				pOut[outidx++]=c;
545 			}
546 			rlecutoff--;
547 			if (rle && rlecutoff==0)
548 			{
549 				rle=0;
550 				removeendmarker=1;
551 			}
552 		}
553 		if (removeendmarker==1)
554 		{
555 			outidx-=4;	// remove the last 4 bytes
556 		}
557 	}
558 	if (outidx>section.len) outidx=section.len;
559 
560 	return outidx;
561 }
loader_appleii_mkmag(unsigned char * magbuf,int * magsize,int gameid,unsigned char * pDskBuf,int diskcnt,int * pDskOffs)562 int loader_appleii_mkmag(unsigned char* magbuf,int* magsize,int gameid,unsigned char* pDskBuf,int diskcnt,int* pDskOffs)
563 {
564 	int magidx;
565 	int codesize;
566 	int stringidx0;
567 	int string1size;
568 	int string2size;
569 	int dictsize;
570 	int huffmantreeidx;
571 	int i;
572 	typedef	 struct _tGameInfo
573 	{
574 		int name_track;
575 		int name_sector;
576 		tSection code_section;
577 		tSection code2_section;
578 		int pivot_code2;
579 		tSection string1_section;
580 		tSection string2_section;
581 		tSection dict_section;
582 		int version;
583 		char gamename[21];
584 	} tGameInfo;
585 
586 	const tGameInfo loader_appleii_gameInfo[4]={
587 		{0x01,0x0, {0x04,0x0,0,65536,1,0},{-1,-1,-1,-1,0,0},		-1,{0x12,0x0,0,0xc000,0,0},	{0x1e,0x0,0,0xb00,0,0},	{-1,-1,-1,0,0,0},	0,"The Pawn"},
588 		{0x00,0x9, {0x03,0x9,0,65536,1,1},{-1,-1,-1,-1,0,0},		-1,{0x12,0xb,0,0xf100,0,0},	{0x21,0xc,0,0xe00,0,0},	{-1,-1,-1,0,0,0},	1,"The Guild of Thieves"},
589 
590 		{0x00,0x9, {0x08,0x2,0,0x3300,1,1},{0x00,0x0,1,0xcd00,1,0},	7,{0x0c,0xc,1, 57344,0,0},	{0x1a,0xc,1, 24832,0,0},	{0x06,0x0,0,  8704,1,0},	2,"Jinxter"},
591 		{0x00,0x9, {0x04,0x0,0,0x4200,1,0},{0x00,0x0,1,0xbe00,1,0},	2,{0x0b,0xe,1, 57344,0,0},	{0x19,0xe,1, 37120,0,0},	{0x08,0x2,0,  7680,1,0},	3,"Corruption"}
592 	};
593 	{
594 		int offs;
595 		int i;
596 		unsigned char c;
597 		printf("Detected '%s'\n",loader_appleii_gameInfo[gameid].gamename);
598 		offs=(loader_appleii_gameInfo[gameid].name_track*MAXSECTORS+loader_appleii_gameInfo[gameid].name_sector)*SECTORBYTES;
599 		offs+=3;
600 		i=0;
601 		c=0;
602 		printf("[");
603 		while (i<0x2c && c!=0xa9)
604 		{
605 			c=pDskBuf[pDskOffs[0]+offs];
606 			i++;
607 			offs++;
608 			if (c>=' ' && c<127) printf("%c",c);
609 		}
610 		printf("]\n");
611 	}
612 
613 	magidx=42;
614 	codesize=loader_appleii_readsection(&magbuf[magidx],loader_appleii_gameInfo[gameid].code_section,pDskBuf,diskcnt,pDskOffs,0);
615 	codesize+=loader_appleii_readsection(&magbuf[magidx+codesize],loader_appleii_gameInfo[gameid].code2_section,pDskBuf,diskcnt,pDskOffs,loader_appleii_gameInfo[gameid].pivot_code2);
616 	magidx+=codesize;
617 
618 	stringidx0=magidx;
619 	string1size=loader_appleii_readsection(&magbuf[magidx],loader_appleii_gameInfo[gameid].string1_section,pDskBuf,diskcnt,pDskOffs,0);
620 	magidx+=string1size;
621 	string2size=loader_appleii_readsection(&magbuf[magidx],loader_appleii_gameInfo[gameid].string2_section,pDskBuf,diskcnt,pDskOffs,0);
622 	magidx+=string2size;
623 	dictsize=loader_appleii_readsection(&magbuf[magidx],loader_appleii_gameInfo[gameid].dict_section,pDskBuf,diskcnt,pDskOffs,0);
624 	magidx+=dictsize;
625 
626 
627 	{
628 		int j;
629 		int matchcnt;
630 		huffmantreeidx=0;
631 		for (i=string1size;i<string1size+string2size-6 && huffmantreeidx==0;i++)
632 		{
633 			matchcnt=0;
634 			for (j=0;j<6;j++)
635 			{
636 				if (magbuf[stringidx0+i+j]==(j+1)) matchcnt++;
637 			}
638 			if (matchcnt>=4)
639 			{
640 				huffmantreeidx=i;
641 			}
642 		}
643 	}
644 
645 	if (gameid==GAME_CORRUPTION) for (i=0x212a;i<0x232a;i++) magbuf[i]=0;	// finishing touches on corruption
646 
647 	loader_common_addmagheader(magbuf,magidx,loader_appleii_gameInfo[gameid].version,codesize,string1size,string2size,dictsize,huffmantreeidx);
648 	*magsize=magidx;
649 	return 0;
650 
651 }
652 
loader_appleii(char * appleiiname,char * magbuf,int * magsize,char * gfxbuf,int * gfxsize)653 int loader_appleii(char *appleiiname,
654 			char *magbuf,int* magsize,
655 			char *gfxbuf,int* gfxsize)
656 {
657 	unsigned char* pDskBuf;
658 	char filename[1024];
659 	unsigned char trackbuf[NIBTRACKSIZE];
660 	int i,l;
661 	int j;
662 	int diskcnt;
663 	int volumeids[MAXDISKS];
664 	int dskidx;
665 	int gameid;
666 	int diskoffs[MAXDISKS];
667 	tWozInfo wozInfo;
668 	FILE *f;
669 
670 #define	SIZE_NIBIMAGE	232960
671 #define	SIZE_2MGIMAGE	143424
672 #define	SIZE_DSKIMAGE	143360
673 
674 	pDskBuf=(unsigned char*)&gfxbuf[4+MAXPICTURES*4];
675 	l=strlen(appleiiname);
676 	j=0;
677 	diskcnt=0;
678 	dskidx=0;
679 	for (i=0;i<l+1 && diskcnt<MAXDISKS;i++)
680 	{
681 		if (appleiiname[i]!=',') filename[j++]=appleiiname[i];
682 		filename[j]=0;
683 
684 		if (appleiiname[i]==',' || appleiiname[i]==0)
685 		{
686 			int n;
687 			int filesize;
688 			f=fopen(filename,"rb");
689 			if (!f)
690 			{
691 				fprintf(stderr,"Unable to open [%s]\n",filename);
692 				return -1;
693 			}
694 			fseek(f,0L,SEEK_END);
695 			filesize=ftell(f);
696 			fseek(f,0L,SEEK_SET);
697 			n=0;
698 			if (filesize==SIZE_NIBIMAGE)
699 			{
700 				for (j=0;j<MAXTRACKS;j++)
701 				{
702 					n+=fread(trackbuf,sizeof(char),NIBTRACKSIZE,f);
703 					volumeids[diskcnt]=loader_appleii_decodenibtrack(trackbuf,j,&pDskBuf[dskidx]);
704 					dskidx+=MAXSECTORS*SECTORBYTES;
705 				}
706 			}
707 			else if (filesize==SIZE_2MGIMAGE)
708 			{
709 				n+=fread(&pDskBuf[dskidx],sizeof(char),0x40,f);	// read in the header. https://apple2.org.za/gswv/a2zine/Docs/DiskImage_2MG_Info.txt
710 				volumeids[diskcnt]=pDskBuf[dskidx+0x10];	// according to my observations, this is where the volume ID is
711 				n+=fread(&pDskBuf[dskidx],sizeof(char),DSKSIZE,f);
712 				dskidx+=DSKSIZE;
713 			}
714 			else
715 			{
716 				n+=fread(&pDskBuf[dskidx],sizeof(char),filesize,f);	// read in the full file. maybe it is a .WOZ?
717 				if (pDskBuf[dskidx+0]=='W' && pDskBuf[dskidx+1]=='O' && pDskBuf[dskidx+2]=='Z' && pDskBuf[dskidx+3]=='2')
718 				{
719 
720 					int dskidx0;
721 					// the file is a .woz file, basically an unsynchronized .nib file with a header.
722 					// the idea is to synchronize the tracks and treat it as a NIB file.
723 
724 					// first, the wo header needs to be parsed, to find the tracks within the diskfile
725 					if (loader_appleii_woz_parseheader(&pDskBuf[dskidx],filesize,&wozInfo))
726 					{
727 						return -1;
728 					}
729 					// at this point, the header information has been read. the tracks can be found, and the translation from WOZ to DSK can be written into the DskBuf (inplace)
730 					dskidx0=dskidx;
731 					for (j=0;j<MAXTRACKS;j++)
732 					{
733 						int start;
734 						int len;
735 						int quarterTrack;
736 
737 						quarterTrack=j;//wozInfo.quarterTrack[j];
738 						start=wozInfo.trackStart[quarterTrack];
739 						len=wozInfo.trackBits[quarterTrack];
740 
741 						if (start)
742 						{
743 							loader_appleii_woz_synchronize(trackbuf,&pDskBuf[dskidx0+start],len);
744 							volumeids[diskcnt]=loader_appleii_decodenibtrack(trackbuf,j,&pDskBuf[dskidx]);
745 						}
746 						dskidx+=MAXSECTORS*SECTORBYTES;
747 					}
748 
749 
750 				} else {
751 					fprintf(stderr,"Unexpected filesize %d bytes.\n",filesize);
752 					return -1;
753 				}
754 			}
755 			fclose(f);
756 			printf("read %d bytes from [%s]. Volume ID [%02X]\n",n,filename,volumeids[diskcnt]);
757 			diskcnt++;
758 			j=0;
759 		}
760 	}
761 	gameid=-1;
762 
763 	for (i=0;i<diskcnt;i++)
764 	{
765 		int newgameid;
766 		int disknum;
767 
768 		disknum=-1;
769 		switch(volumeids[i])
770 		{
771 			case 0x68:	newgameid=GAME_PAWN;		disknum=volumeids[i]-0x68;break;
772 			case 0x69:	newgameid=GAME_GUILD;		disknum=volumeids[i]-0x69;break;
773 			case 0x70:
774 			case 0x71:	newgameid=GAME_JINXTER;		disknum=volumeids[i]-0x70;break;
775 			case 0x72:
776 			case 0x73:
777 			case 0x74:	newgameid=GAME_CORRUPTION;	disknum=volumeids[i]-0x72;break;
778 			default:
779 					return -1;
780 		}
781 		if (gameid==-1 || gameid==newgameid) gameid=newgameid;
782 		else {
783 			fprintf(stderr,"Game detection ambigous\n");
784 			return -1;
785 		}
786 		if (disknum>=0 && disknum<MAXDISKS)
787 		{
788 			diskoffs[disknum]=i*DSKSIZE;
789 		}
790 	}
791 	if (gameid==-1)
792 	{
793 		fprintf(stderr,"Unable to detect the game\n");
794 		return -1;
795 	}
796 
797 
798 	loader_appleii_mkmag((unsigned char*)magbuf,magsize,gameid,pDskBuf,diskcnt,diskoffs);	// since the memory for the gfx buffer includes the dskbuf, this is enough
799 	return loader_appleii_mkgfx((unsigned char*)gfxbuf,gfxsize,gameid,diskcnt,diskoffs);	// since the memory for the gfx buffer includes the dskbuf, this is enough
800 
801 }
802 
803 
804