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 MS DOS Game binaries,
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_common.h"
36 #include "loader_atarixl.h"
37 #include "vm68k_macros.h"
38
39 #define LOADER_ATARIXL_OK 0
40 #define LOADER_ATARIXL_NOK -1
41
42 typedef struct _tGameInfo
43 {
44 unsigned char name[22];
45 unsigned char version; // 0=The Pawn. 1=The Guild of Thieves. 2=Jinxter
46 unsigned int offs_code1; // the offset of the first code section
47 unsigned int offs_code2; // the offset to the second code section
48 unsigned int offs_string1; // the offset to the string1 section
49 unsigned int offs_string2; // the offset to the string2 section
50 unsigned int offs_dict; // the offset to the dict section
51
52 unsigned int offs_pictures[30]; // the offset to the pictures within the .ATR files.
53 } tGameInfo;
54
55 #define DISK_SIZE 133136
56 #define DISK_OFFSETMASK 0x3ffff
57 #define DISK1_FLAG 0x40000
58 #define DISK2_FLAG 0x80000
59
60
61 // i was not able to find the proper directory.
62 // but i was able to find the code/string/dict sections through correlations and other means.
63 // this is how the following table has been created.
64 #define GAMENUM 3
65 const tGameInfo loader_atarixl_cGameInfo[GAMENUM]={
66 {"The Pawn",0,
67 0x3990|DISK1_FLAG,0,0x11310|DISK1_FLAG,0x1c710|DISK1_FLAG,0,
68 {
69 DISK2_FLAG|0x00010,DISK2_FLAG|0x00a90,DISK2_FLAG|0x01a10,DISK2_FLAG|0x02410,
70 DISK2_FLAG|0x1d190,DISK2_FLAG|0x03310,DISK2_FLAG|0x04110,DISK2_FLAG|0x04a10,
71 DISK2_FLAG|0x05210,DISK2_FLAG|0x05890,DISK2_FLAG|0x1f910,DISK2_FLAG|0x06390,
72 DISK2_FLAG|0x11310,DISK2_FLAG|0x11f10,DISK2_FLAG|0x12e90,DISK2_FLAG|0x13990,
73
74 DISK2_FLAG|0x14190,DISK2_FLAG|0x14f90,DISK2_FLAG|0x15790,DISK2_FLAG|0x1ef10,
75 DISK2_FLAG|0x06f90,DISK2_FLAG|0x16510,DISK2_FLAG|0x17010,DISK2_FLAG|0x1dd90,
76 DISK2_FLAG|0x1e510,DISK2_FLAG|0x18010,DISK2_FLAG|0x18600,DISK2_FLAG|0x18f90,
77 DISK2_FLAG|0x19e10,DISK2_FLAG|0x1a610
78 }
79 },
80 {"The Guild Of Thieves",1,
81 0x3890|DISK1_FLAG,0x10|DISK2_FLAG,0xc010|DISK2_FLAG,0x1b110|DISK2_FLAG,0,
82 {
83 DISK1_FLAG|0x1b310,DISK1_FLAG|0x09690,DISK1_FLAG|0x14210,DISK2_FLAG|0x1be90,
84 DISK1_FLAG|0x11d90,DISK1_FLAG|0x08990,DISK1_FLAG|0x10090,DISK1_FLAG|0x17490,
85 DISK1_FLAG|0x11110,DISK1_FLAG|0x08190,DISK2_FLAG|0x1e010,DISK1_FLAG|0x0d610,
86 DISK1_FLAG|0x0ed10,DISK1_FLAG|0x18490,DISK1_FLAG|0x1f710,DISK2_FLAG|0x1d190,
87
88 DISK1_FLAG|0x0c690,DISK1_FLAG|0x0e010,DISK1_FLAG|0x1ea10,DISK1_FLAG|0x16c90,
89 DISK1_FLAG|0x0aa10,DISK1_FLAG|0x15910,DISK1_FLAG|0x0cc10,DISK1_FLAG|0x09f90,
90 DISK1_FLAG|0x0ba10,DISK1_FLAG|0x19210,DISK1_FLAG|0x1a810,DISK1_FLAG|0x12b90,
91 DISK1_FLAG|0x14d90,0
92 }
93 },
94 {"Jinxter",2,
95 0x3790|DISK1_FLAG,0x10|DISK2_FLAG,0xc710|DISK2_FLAG,0x1a710|DISK2_FLAG,0x6490|DISK1_FLAG,
96 {
97 DISK1_FLAG|0x08690,DISK1_FLAG|0x09990,DISK1_FLAG|0x0a690,DISK1_FLAG|0x0b390,
98 DISK1_FLAG|0x0c490, 0, 0,DISK1_FLAG|0x0d840,
99 DISK1_FLAG|0x0e690, 0,DISK1_FLAG|0x0f210,DISK1_FLAG|0x10090,
100 DISK1_FLAG|0x10f10,DISK1_FLAG|0x11d10,DISK1_FLAG|0x12c10,DISK1_FLAG|0x13910,
101
102 DISK1_FLAG|0x14590,DISK1_FLAG|0x15290,DISK1_FLAG|0x15d10, 0,
103 DISK1_FLAG|0x16d90,DISK1_FLAG|0x17f90,DISK1_FLAG|0x18810,DISK1_FLAG|0x19590,
104 DISK1_FLAG|0x1a410,DISK1_FLAG|0x1b910,DISK1_FLAG|0x1c510, 0,
105 DISK1_FLAG|0x1ce90, 0
106 }
107 }
108 };
109
110 #define BLOCKSIZE 256
111 #define MAXPIVOT 8
112 #define GETIDX(idx,offset,disk1offs,disk2offs) \
113 idx=(offset); \
114 if ((idx)&DISK1_FLAG) idx=((idx)&DISK_OFFSETMASK)+(disk1offs); \
115 if ((idx)&DISK2_FLAG) idx=((idx)&DISK_OFFSETMASK)+(disk2offs);
116
loader_atarixl_detectgame(unsigned char * diskbuf,int * disk1offs,int * disk2offs)117 int loader_atarixl_detectgame(unsigned char* diskbuf,int* disk1offs,int* disk2offs)
118 {
119 int d1,d2;
120 int found;
121 int i;
122 unsigned char tmp[256];
123
124 d1=*disk1offs;
125 d2=*disk2offs;
126 // the game code always starts with the same instruction 49FA FFFE.
127 // in each game release, this is at a different postion within the
128 // floppy disks. this can be used to determine which game it is.
129 // since the game code has been scrambled, it needs to be descrambled first.
130
131 // first, assume that the first argument was also the first floppy disk
132 found=-1;
133 for (i=0;i<GAMENUM && found==-1;i++)
134 {
135 unsigned char lc;
136 lc=0xff;
137 loader_common_descramble(&diskbuf[d1+(DISK_OFFSETMASK&loader_atarixl_cGameInfo[i].offs_code1)],tmp,0,&lc,0);
138 if (tmp[ 0]==0x49 && tmp[ 1]==0xfa && tmp[ 2]==0xff && tmp[ 3]==0xfe) found=i;
139 if (tmp[2+ 0]==0x49 && tmp[2+ 1]==0xfa && tmp[2+ 2]==0xff && tmp[2+ 3]==0xfe) found=i;
140 }
141 if (found!=-1) return found;
142
143 // if this failed, try to swap the disks
144 *disk1offs=d2;
145 *disk2offs=d1;
146 for (i=0;i<GAMENUM;i++)
147 {
148 unsigned char lc;
149 lc=0xff;
150 loader_common_descramble(&diskbuf[d2+(DISK_OFFSETMASK&loader_atarixl_cGameInfo[i].offs_code1)],tmp,0,&lc,0);
151 if (tmp[ 0]==0x49 && tmp[ 1]==0xfa && tmp[ 2]==0xff && tmp[ 3]==0xfe) found=i;
152 if (tmp[2+ 0]==0x49 && tmp[2+ 1]==0xfa && tmp[2+ 2]==0xff && tmp[2+ 3]==0xfe) found=i;
153 }
154 return found;
155 }
156
loader_atarixl_mkmag(unsigned char * diskbuf,int disksize,int disk1offs,int disk2offs,unsigned char * magbuf,int * magbufsize,const tGameInfo * pGameInfo)157 int loader_atarixl_mkmag(unsigned char* diskbuf,int disksize,int disk1offs,int disk2offs,unsigned char* magbuf,int* magbufsize,const tGameInfo *pGameInfo)
158 {
159 int magidx;
160 int code1size;
161 int code2size;
162 int string1size;
163 int string2size;
164 int dictsize;
165 int huffmantreeidx;
166
167
168
169 magidx=42;
170
171 code1size=0;
172 code2size=0;
173 {
174 int idx;
175 unsigned char lc;
176 int n;
177 int rle;
178 int codeleft;
179 int pivot;
180
181 codeleft=0x10000-0x100;
182 rle=0;
183 if (pGameInfo->version!=0)
184 {
185 magidx-=2;
186 rle=1;
187 }
188
189 pivot=0;
190 lc=0xff;
191 GETIDX(idx,pGameInfo->offs_code1,disk1offs,disk2offs);
192 n=loader_common_descramble(&diskbuf[idx],&magbuf[magidx],pivot,&lc,rle);
193 if (pGameInfo->version!=0)
194 {
195 codeleft=READ_INT16BE(magbuf,magidx);
196 }
197 code1size+=n;
198 idx+=BLOCKSIZE;
199 magidx+=n;
200 while (codeleft>=BLOCKSIZE)
201 {
202 pivot=(pivot+1)%MAXPIVOT;
203 n=loader_common_descramble(&diskbuf[idx],&magbuf[magidx],pivot,&lc,rle);
204 codeleft-=BLOCKSIZE;
205 idx+=BLOCKSIZE;
206 magidx+=n;
207 code1size+=n;
208 }
209 if (codeleft!=0)
210 {
211 codeleft=0x100-codeleft;
212 code1size-=(codeleft+2);
213 magidx-=codeleft;
214 }
215 GETIDX(idx,pGameInfo->offs_code2,disk1offs,disk2offs);
216 codeleft=0x10000-code1size;
217 while (codeleft>0)
218 {
219 pivot=(pivot+1)%MAXPIVOT;
220 n=loader_common_descramble(&diskbuf[idx],&magbuf[magidx],pivot,&lc,0);
221 codeleft-=BLOCKSIZE;
222 idx+=BLOCKSIZE;
223 magidx+=n;
224 code2size+=n;
225 }
226 }
227
228
229 // the strings are not scrambled. they are being copied over from the disk buffer into the .mag buffer.
230 string1size=0;
231 string2size=0;
232 {
233 int idx1;
234 int idx2;
235 int nxtdisk;
236 int magidx0;
237
238
239 magidx0=magidx;
240 GETIDX(idx1,pGameInfo->offs_string1,disk1offs,disk2offs);
241 GETIDX(idx2,pGameInfo->offs_string2,disk1offs,disk2offs);
242 nxtdisk=disksize;
243 if (idx1<disk1offs) nxtdisk=disk1offs;
244 if (idx1<disk2offs) nxtdisk=disk2offs;
245
246 while (idx1<idx2 && idx1<nxtdisk)
247 {
248 magbuf[magidx++]=diskbuf[idx1++];
249 string1size++;
250 }
251 // TODO: string2 is waaay too big.
252 nxtdisk=disksize;
253 if (idx2<disk1offs) nxtdisk=disk1offs;
254 if (idx2<disk2offs) nxtdisk=disk2offs;
255 while (idx2<nxtdisk)
256 {
257 magbuf[magidx++]=diskbuf[idx2++];
258 string2size++;
259 }
260 if (pGameInfo->version==0)
261 {
262 huffmantreeidx=string1size;
263 } else {
264 int i;
265 huffmantreeidx=0;
266 // the huffman tree starts with the sequence 01 02 03 ?? 05.
267
268 for (i=magidx0;i<magidx-3 && huffmantreeidx==0;i++)
269 {
270 if (magbuf[i+0]==0x01 && magbuf[i+1]==0x02 && magbuf[i+2]==0x03 && magbuf[i+4]==0x05)
271 {
272 huffmantreeidx=i-magidx0;
273 }
274 }
275 }
276 if (string1size>=0x10000)
277 {
278 int x;
279 x=string1size+string2size;
280 string1size=0x10000;
281 string2size=x-string1size;
282 }
283 }
284
285
286 dictsize=0;
287 if (pGameInfo->offs_dict!=0)
288 {
289 int n;
290 int idx;
291 int pivot;
292 unsigned char lc;
293 GETIDX(idx,pGameInfo->offs_dict,disk1offs,disk2offs);
294 pivot=0;
295 while (dictsize<8704) // TODO: magic number
296 {
297 lc=0xff;
298 n=loader_common_descramble(&diskbuf[idx],&magbuf[magidx],pivot,&lc,0);
299
300 magidx+=n;
301 dictsize+=n;
302 pivot=(pivot+1)%MAXPIVOT;
303 idx+=BLOCKSIZE;
304 }
305 }
306 loader_common_addmagheader(magbuf,magidx,pGameInfo->version,code1size+code2size,string1size,string2size,dictsize,huffmantreeidx);
307 *magbufsize=magidx;
308
309
310 return LOADER_ATARIXL_OK;
311 }
loader_atarixl_mkgfx(unsigned char * gfxbuf,int * gfxbufsize,int disk1offs,int disk2offs,const tGameInfo * pGameInfo)312 int loader_atarixl_mkgfx(unsigned char* gfxbuf,int *gfxbufsize,int disk1offs,int disk2offs,const tGameInfo* pGameInfo)
313 {
314 int i;
315 int idx;
316 int gfxidx;
317
318 // i am lazy
319 // just translate the pre-determined offsets into the gfx buffer index
320 gfxidx=0;
321
322 gfxbuf[gfxidx++]='M';
323 gfxbuf[gfxidx++]='a';
324 gfxbuf[gfxidx++]='P';
325 gfxbuf[gfxidx++]='7';
326 for (i=0;i<30;i++)
327 {
328 GETIDX(idx,pGameInfo->offs_pictures[i],disk1offs,disk2offs);
329 WRITE_INT32BE(gfxbuf,gfxidx,idx);gfxidx+=4;
330 }
331 // that's it
332
333 return LOADER_ATARIXL_OK;
334 }
loader_atarixl(char * atarixlname,char * magbuf,int * magbufsize,char * gfxbuf,int * gfxbufsize)335 int loader_atarixl(char* atarixlname,
336 char* magbuf,int* magbufsize,
337 char* gfxbuf,int* gfxbufsize)
338 {
339 int disk1offs;
340 int disk2offs;
341 int disksize;
342 int detectedgame;
343 FILE *f;
344 char* diskfile1;
345 char* diskfile2;
346 int i;
347
348 diskfile1=atarixlname;
349 diskfile2=atarixlname;
350 for (i=0;i<strlen(atarixlname);i++)
351 {
352 if (atarixlname[i]==',')
353 {
354 diskfile2=&atarixlname[i+1];
355 atarixlname[i]=0;
356 }
357 }
358
359
360 disk1offs=256; // leave some room at the beginning for the directory
361 disk2offs=0;
362
363
364 f=fopen(diskfile1,"rb");
365 if (!f)
366 {
367 printf("unable to open [%s]. sorry\n",diskfile1);
368 return LOADER_ATARIXL_NOK;
369 }
370 disksize=fread(&gfxbuf[disk1offs],sizeof(char),*gfxbufsize-disk1offs,f);
371 if (disksize!=DISK_SIZE)
372 {
373 printf("[%s] does not look like an .ATR image. Sorry\n",diskfile1);
374 return LOADER_ATARIXL_NOK;
375 }
376 fclose(f);
377 disk2offs=disk1offs+disksize;
378
379 f=fopen(diskfile2,"rb");
380 if (!f)
381 {
382 printf("unable to open [%s]. sorry\n",diskfile2);
383 return LOADER_ATARIXL_NOK;
384 }
385 disksize=fread(&gfxbuf[disk2offs],sizeof(char),*gfxbufsize-disk2offs,f);
386 fclose(f);
387 if (disksize!=DISK_SIZE)
388 {
389 printf("[%s] does not look like an .ATR image. Sorry\n",diskfile2);
390 return LOADER_ATARIXL_NOK;
391 }
392 *gfxbufsize=disksize+disk2offs;
393
394 ////////////////////
395 detectedgame=loader_atarixl_detectgame((unsigned char*)gfxbuf,&disk1offs,&disk2offs);
396 if (detectedgame==-1)
397 {
398 printf("unable to detect the game. Sorry!\n");
399 return LOADER_ATARIXL_NOK;
400 }
401 printf("Detected [%s]\n",loader_atarixl_cGameInfo[detectedgame].name);
402
403
404 ////////////////////
405 if (loader_atarixl_mkmag((unsigned char*)gfxbuf,*gfxbufsize,disk1offs,disk2offs,(unsigned char*)magbuf,magbufsize,&loader_atarixl_cGameInfo[detectedgame])!=LOADER_ATARIXL_OK)
406 {
407 return LOADER_ATARIXL_NOK;
408 }
409
410 if (loader_atarixl_mkgfx((unsigned char*)gfxbuf,gfxbufsize,disk1offs,disk2offs,&loader_atarixl_cGameInfo[detectedgame])!=LOADER_ATARIXL_OK)
411 {
412 return LOADER_ATARIXL_NOK;
413 }
414
415
416
417 return LOADER_ATARIXL_OK;
418 }
419
420
421