2 * This program is free software: you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation, either version 3 of the License, or
5 * (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "SDL_endian.h"
27 #include "awishcommon.h"
32 #include "gameglobals.h"
42 ////////////////////////////////////////////////////////////////////////////////
43 static SDL_Surface
*loadCFScreen (const char *fname
, SDL_Surface
*old
, int w
, int h
, int chained
) {
45 static Uint8 pal
[256][3];
46 static Uint32 opal
[256];
47 static Uint8 scr
[64000];
49 uint8_t *diskdata
= tryDiskFile(fname
, &rsz
);
51 if (diskdata
== NULL
) return old
;
52 if (rsz
< w
*h
+(chained
>= 0 ? 768 : 0)) { free(diskdata
); return old
; }
54 new = createSurface(320, 200);
55 if (!new) { free(diskdata
); return old
; }
56 SDL_SetColorKey(new, 0, 0); // clear colorkey info (it's the fullscreen image after all!)
58 for (int dy
= 0; dy
< 200; ++dy
) {
59 for (int dx
= 0; dx
< 320; ++dx
) {
60 putPixel2x(new, dx
, dy
, 0);
64 memcpy(scr
, diskdata
, w
*h
);
65 if (chained
>= 0) memcpy(pal
, diskdata
+w
*h
, 768);
69 memcpy(opal
, palette
, 256*4);
70 for (int f
= 0; f
< 256; ++f
) {
72 if (pal
[f
][0] != 0) pal
[f
][0] |= 3;
74 if (pal
[f
][1] != 0) pal
[f
][1] |= 3;
76 if (pal
[f
][2] != 0) pal
[f
][2] |= 3;
77 palette
[f
] = SDL_MapRGB(screen
->format
, pal
[f
][0], pal
[f
][1], pal
[f
][2]);
82 for (int dy
= 0; dy
< h
; ++dy
) {
83 for (int dx
= 0; dx
< w
/4; ++dx
) {
84 for (int c
= 0; c
< 4; ++c
) {
85 putPixel2x(new, dx
*4+c
, dy
, scr
[dy
*(w
/4)+(w
*h
)/4*c
+dx
]);
90 for (int dy
= 0; dy
< h
; ++dy
) {
91 for (int dx
= 0; dx
< w
; ++dx
) {
92 putPixel2x(new, dx
, dy
, scr
[dy
*w
+dx
]);
97 memcpy(palette
, opal
, 256*4);
103 ////////////////////////////////////////////////////////////////////////////////
104 static int loadFont (void) {
106 uint8_t *buf
= loadResFile(&resfile
, 92, &sz
);
108 if (buf
== NULL
) return -1;
109 if (sz
!= 256*8) { free(buf
); return -1; }
110 memcpy(font
, buf
, 256*8);
116 static int loadPalette (void) {
118 uint8_t *buf
= loadResFile(&resfile
, 83, &sz
);
120 if (buf
== NULL
) return -1;
121 if (sz
!= 768) { free(buf
); return -1; }
122 for (int f
= 0; f
< 768; ++f
) {
124 if (buf
[f
] != 0) buf
[f
] |= 3;
126 for (int f
= 0; f
< 256; ++f
) palette
[f
] = SDL_MapRGB(screen
->format
, buf
[f
*3+0], buf
[f
*3+1], buf
[f
*3+2]);
132 static SDL_Surface
*loadImage (ResFile
*resfile
, int idx
) {
137 img
= loadResFile(resfile
, idx
, &sz
);
138 if (img
== NULL
) return NULL
;
139 if (sz
< 320*200) { free(img
); return NULL
; }
140 res
= createSurface(320, 200);
141 if (res
== NULL
) { free(img
); return NULL
; }
142 SDL_SetColorKey(res
, 0, 0); // clear colorkey info (it's the fullscreen image after all!)
143 for (int f
= 0; f
< 320*200; ++f
) putPixel2x(res
, f
%320, f
/320, img
[f
]);
149 // return # of sprites loaded or <0 on error
150 static int loadSpriteBank (SpriteBank
*bank
, ResFile
*resfile
, int idx
) {
154 if ((buf
= loadResFile(resfile
, idx
, &sz
)) == NULL
) return -1;
155 if (sz
< 3) { free(buf
); return -1; }
157 for (int f
= bank
->count
-1; f
>= 0; --f
) {
158 SDL_FreeSurface(bank
->spr
[f
][1]);
159 SDL_FreeSurface(bank
->spr
[f
][0]);
162 while (pos
+4 <= sz
) {
163 int h
= ((Uint32
)buf
[pos
+0])+256*((Uint32
)buf
[pos
+1]);
164 int w
= ((Uint32
)buf
[pos
+2])+256*((Uint32
)buf
[pos
+3]);
167 if (h
> 0 && w
> 0) {
168 SDL_Surface
*s0
= createSurface(w
, h
);
169 SDL_Surface
*s1
= createSurface(w
, h
);
171 if (!s0
|| !s1
) { free(buf
); return -1; }
173 for (int y
= 0; y
< h
; ++y
) {
174 for (int x
= 0; x
< w
; ++x
) {
176 putPixel2x(s0
, x
, y
, buf
[pos
]);
177 putPixel2x(s1
, w
-x
-1, y
, buf
[pos
]);
182 bank
->spr
[bank
->count
][0] = s0
;
183 bank
->spr
[bank
->count
][1] = s1
;
193 static void freeSpriteBank (SpriteBank *bank) {
194 for (int f = bank->count-1; f >= 0; --f) {
195 SDL_FreeSurface(bank->spr[f][1]);
196 SDL_FreeSurface(bank->spr[f][0]);
202 ////////////////////////////////////////////////////////////////////////////////
203 static void quitCleanupRes (void) {
205 //if (!disableSound) unloadAllSounds();
206 deinitResFile(&sndfile
);
207 deinitResFile(&resfile
);
208 //for (int f = 0; f < sizeof(banks)/sizeof(SpriteBank); ++f) freeSpriteBank(&banks[f]);
212 ////////////////////////////////////////////////////////////////////////////////
213 #include "sincostab.c"
214 static int awishRST (int tid
, int opcode
, int argc
, int argv
[], int *argp
[]) {
215 vmGVars
[GVAR_RST_RESULT
] = 0;
217 if (argv
[0] == CONST_FRST_ML_TITLE
) {
219 } else if (argv
[0] == CONST_FRST_ML_GAME
) {
221 } else if (argv
[0] == CONST_FRST_GET_MAX_THREADS
) {
222 vmGVars
[GVAR_RST_RESULT
] = VM_MAX_THREADS
;
223 } else if (argv
[0] == CONST_FRST_GET_MAX_THREAD_ID
) {
224 vmGVars
[GVAR_RST_RESULT
] = vmLastThread();
225 } else if (argv
[0] == CONST_FRST_GET_RAND
) {
226 int nmin
= 0, nmax
= 32767;
229 case 2: nmax
= argv
[1]; break;
230 case 3: nmin
= argv
[1]; nmax
= argv
[2]; break;
233 vmGVars
[GVAR_RST_RESULT
] = nmin
;
235 vmGVars
[GVAR_RST_RESULT
] = nmin
+(randUInt32()%(nmax
-nmin
+1));
237 } else if (argv
[0] == CONST_FRST_GET_SEED_H
) {
238 vmGVars
[GVAR_RST_RESULT
] = getSeedH();
239 } else if (argv
[0] == CONST_FRST_GET_SEED_L
) {
240 vmGVars
[GVAR_RST_RESULT
] = getSeedL();
241 } else if (argv
[0] == CONST_FRST_SET_SEED_H
) {
242 if (argc
>= 2) setSeedH(argv
[1]);
243 } else if (argv
[0] == CONST_FRST_SET_SEED_L
) {
244 if (argc
>= 2) setSeedL(argv
[1]);
245 } else if (argv
[0] == CONST_FRST_DEBUG_PRINT_STR
) {
246 int pos
= 0, len
= 0;
254 dolen
: if (pos
>= 0) {
255 while (pos
< vmCodeSize
&& vmCode
[pos
+len
]) ++len
;
264 if (pos
>= -255 && pos
<= -1) {
267 for (; len
> 0; --len
, ++pos
) if (pos
>= 0 && pos
< vmCodeSize
) fputc(vmCode
[pos
], stderr
);
270 } else if (argv
[0] == CONST_FRST_DEBUG_PRINT_NUM
) {
272 case 1: argv
[1] = vmPop(tid
); // fallthru
273 case 2: if (goobers
) fprintf(stderr
, "%d", argv
[1]); break;
274 case 3: if (goobers
) fprintf(stderr
, "%d,%d", argv
[1], argv
[2]); break;
276 } else if (argv
[0] == CONST_FRST_PLAY_SOUND
) {
277 // arg1: sound index; arg2 (if present) channel; rst_result: -1 or channel
278 if (argc
== 1) argv
[1] = vmPop(tid
);
279 if (argc
< 2) argv
[2] = -1;
280 vmGVars
[GVAR_RST_RESULT
] = playSound(argv
[1], argv
[2]);
281 } else if (argv
[0] == CONST_FRST_STOP_CHANNEL
) {
283 if (argc
== 1) argv
[1] = vmPop(tid
);
284 vmGVars
[GVAR_RST_RESULT
] = stopChannel(argv
[1]);
285 } else if (argv
[0] == CONST_FRST_IS_CHANNEL_PLAYING
) {
286 // arg1: channel; rst_result: bool
287 if (argc
== 1) argv
[1] = vmPop(tid
);
288 vmGVars
[GVAR_RST_RESULT
] = isChannelPlaying(argv
[1]);
289 } else if (argv
[0] == CONST_FRST_LOAD_SOUND
) {
290 // arg1: sound index; rst_result: bool
291 if (argc
== 1) argv
[1] = vmPop(tid
);
292 vmGVars
[GVAR_RST_RESULT
] = loadSound(argv
[1]) ? 0 : 1;
293 } else if (argv
[0] == CONST_FRST_UNLOAD_SOUND
) {
294 // arg1: sound index; rst_result: bool
295 if (argc
== 1) argv
[1] = vmPop(tid
);
296 vmGVars
[GVAR_RST_RESULT
] = unloadSound(argv
[1]) ? 0 : 1;
297 } else if (argv
[0] == CONST_FRST_IS_SOUND_LOADED
) {
298 // arg1: sound index; rst_result: bool
299 if (argc
== 1) argv
[1] = vmPop(tid
);
300 vmGVars
[GVAR_RST_RESULT
] = isSoundLoaded(argv
[1]) ? 0 : 1;
301 } else if (argv
[0] == CONST_FRST_LOAD_SPRITES
) {
302 // arg1: bank, arg2: filename (0-terminated)
307 argv
[2] = vmPop(tid
);
308 argv
[1] = vmPop(tid
);
311 argv
[2] = vmPop(tid
);
316 vmGVars
[GVAR_RST_RESULT
] = -1;
317 if (fnpos
>= 0 && fnpos
< vmCodeSize
-1 && argv
[1] >= 0 && argv
[1] <= 256) {
320 for (end
= fnpos
; end
< vmCodeSize
&& vmCode
[end
]; ++end
) ;
321 if (end
< vmCodeSize
) {
322 //fprintf(stderr, "!!! %d: [%s]\n", argv[1], (const char *)(vmCode+fnpos));
323 vmGVars
[GVAR_RST_RESULT
] = loadSpriteBankFromFile(&banks
[argv
[1]], (const char *)(vmCode
+fnpos
));
326 } else if (argv
[0] == CONST_FRST_ADD_LEVEL_SPRITE
|| argv
[0] == CONST_FRST_ADD_SPRITE
) {
327 // x, y, layer, bank, num, dir (can take 1st 2 args from stack too)
328 int inlevel
= (argv
[0] == CONST_FRST_ADD_LEVEL_SPRITE
);
329 int x
= 0, y
= 0, layer
, bank
, num
, dir
;
349 //fprintf(stderr, "x=%d, y=%d, layer=%d, bank=%d, num=%d, dir=%d, inlevel=%d\n", x, y, layer, bank, num, dir, inlevel);
350 addSpriteToLayer(x
, y
, layer
, bank
, num
, dir
, inlevel
);
351 } else if (argv
[0] == CONST_FRST_START_POLY
) {
352 int angle
= vmPop(tid
);
353 int scale
= vmPop(tid
);
354 int ofsx
= 0, ofsy
= 0;
370 pmStart(ofsx
, ofsy
, scale
, angle
);
371 } else if (argv
[0] == CONST_FRST_ADD_POLY_POINT
) {
374 argv
[2] = vmPop(tid
);
375 argv
[1] = vmPop(tid
);
378 argv
[2] = vmPop(tid
);
381 pmAddPoint(argv
[1], argv
[2]);
382 } else if (argv
[0] == CONST_FRST_END_POLY
) {
385 argv
[2] = vmPop(tid
);
386 argv
[1] = vmPop(tid
);
389 argv
[2] = vmPop(tid
);
392 pmDone(argv
[1], argv
[2]);
393 } else if (argv
[0] == CONST_FRST_DRAW_TEXT
|| argv
[0] == CONST_FRST_TEXT_WIDTH
) {
394 // arg1: addr; arg2: len, x, y, scale, angle, color
395 // arg1: addr; arg2: len, scale, angle
396 int color
= (argv
[0] == CONST_FRST_DRAW_TEXT
) ? vmPop(tid
) : 0;
397 int angle
= vmPop(tid
);
398 int scale
= vmPop(tid
);
399 int y
= (argv
[0] == CONST_FRST_DRAW_TEXT
) ? vmPop(tid
) : 0;
400 int x
= (argv
[0] == CONST_FRST_DRAW_TEXT
) ? vmPop(tid
) : 0;
401 int addr
= 0, len
= -1;
402 const char *str
= (const char *)vmCode
;
416 if (len
!= 0 && addr
>= 0 && addr
< vmCodeSize
) {
419 for (len
= 0; addr
+len
< vmCodeSize
&& str
[len
]; ++len
) ;
421 if (addr
+len
> vmCodeSize
) len
= vmCodeSize
-addr
;
423 if (str
!= NULL
&& len
> 0) {
424 if (argv
[0] == CONST_FRST_TEXT_WIDTH
) {
425 char *s
= calloc(len
+1, 1);
427 if (s
== NULL
) fatal("out of memory");
428 if (len
> 0) memcpy(s
, str
, len
);
429 vmGVars
[GVAR_RST_RESULT
] = polymodStr(NULL
, s
, 0, 0, scale
, angle
, 0, 255);
432 textAdd(str
, len
, x
, y
, scale
, angle
, color
, 255);
436 } else if (argv
[0] == CONST_FRST_COS
|| argv
[0] == CONST_FRST_SIN
) {
437 if (argc
< 2) argv
[1] = vmPop(tid
);
438 argv
[1] %= 360; if (argv
[1] < 0) argv
[1] += 360;
439 if (argv
[0] == CONST_FRST_COS
) argv
[1] = (argv
[1]+90)%360;
440 vmGVars
[GVAR_RST_RESULT
] = sintab
[argv
[1]];
442 if (gameRSTCB
) return gameRSTCB(tid
, opcode
, argc
, argv
, argp
);
443 fatal("invalid RST: %d", argv
[0]);
445 return 0; // continue
449 ////////////////////////////////////////////////////////////////////////////////
450 static int checkLevelCode (const char *t
) {
453 if (!t
|| !t
[0]) return -1;
454 ctrd
= vmNewThread(0);
455 for (int level
= 0; level
< vmGVars
[GVAR_MAX_LEVEL
]; ++level
) {
456 //fprintf(stderr, "%d/%d\n", level+1, vmGVars[GVAR_MAX_LEVEL]);
458 if (vmExecuteBSR(ctrd
, CODE_ENTRY_GET_LEVEL_CODE
, 0) == 0) {
459 int pos
= vmGVars
[GVAR_LEVEL_CODE_OFS
], len
= vmGVars
[GVAR_LEVEL_CODE_LEN
], ok
= 1;
461 if (strlen(t
) == len
&& pos
>= 0 && len
> 0 && pos
+len
<= vmCodeSize
) {
462 //fwrite(vmCode+pos, len, 1, stderr);
463 //fprintf(stderr, " [%s]\n", t);
464 for (int f
= 0; f
< len
; ++f
) {
465 //fprintf(stderr, "%c %c\n", tolower(t[f]), tolower(vmCode[pos+f]));
466 if (tolower(t
[f
]) != tolower(vmCode
[pos
+f
])) { ok
= 0; break; }
469 if (goobers
) fprintf(stderr
, "found code for level #%02d\n", level
+1);
474 if (goobers
) fprintf(stderr
, "sorry!\n");
483 ////////////////////////////////////////////////////////////////////////////////
485 # include "cmdline.c"
489 ////////////////////////////////////////////////////////////////////////////////
490 int main (int argc
, char *argv
[]) {
492 int disableSound
= 0;
500 memset(banks
, 0, sizeof(banks
));
504 for (int f
= 1; f
< argc
; ++f
) {
507 if (strcmp(argv
[f
], "-goobers") == 0) goobers
= 1;
508 else if (strcmp(argv
[f
], "--goobers") == 0) goobers
= 1;
509 else if (strcmp(argv
[f
], "-dat") == 0) datfirst
= 1;
510 else if (strcmp(argv
[f
], "--dat") == 0) datfirst
= 1;
511 else if (strcmp(argv
[f
], "-trace") == 0) vmDebugTrace
= 1;
512 else if (strcmp(argv
[f
], "--trace") == 0) vmDebugTrace
= 1;
513 else if (strcmp(argv
[f
], "-nosound") == 0) disableSound
= 1;
514 else if (strcmp(argv
[f
], "--nosound") == 0) disableSound
= 1;
515 else if (strcmp(argv
[f
], "-fs") == 0) optFullscreen
= 1;
516 else if (strcmp(argv
[f
], "--fs") == 0) optFullscreen
= 1;
517 else if (strcmp(argv
[f
], "-tracelog") == 0 || strcmp(argv
[f
], "--tracelog") == 0) {
520 vmDebugOutput
= fopen("ztrace.log", "w");
525 for (int c
= f
+eaten
; c
< argc
; ++c
) argv
[c
-1] = argv
[c
];
526 argv
[argc
-= eaten
] = NULL
;
531 initResFile(&resfile
, "RESOURCE.DAT");
532 initResFile(&sndfile
, "RESOURCE.SND");
533 atexit(quitCleanupRes
);
535 if (loadFont() != 0) {
536 fprintf(stderr
, "FATAL: can't load font!\n");
545 if (loadPalette() != 0) {
546 fprintf(stderr
, "FATAL: can't load palette!\n");
553 lockSurface(&lock
, screen
);
554 drawString(screen
, "loading...", 2, 200-10, 1);
555 unlockSurface(&lock
);
559 for (int f
= 0; f
< 8; ++f
) {
560 if ((backs
[(f
+1)%8] = loadImage(&resfile
, f
)) == NULL
) {
561 fprintf(stderr
, "FATAL: can't load image #%d!\n", f
);
565 if ((backs
[8] = loadImage(&resfile
, 8)) == NULL
) {
566 fprintf(stderr
, "FATAL: can't load image #%d!\n", 8);
570 backs
[0] = loadCFScreen("CFTITLE.DAT", backs
[0], 320, 200, 1);
571 backs
[0] = loadCFScreen("cftitle.dat", backs
[0], 320, 200, 1);
573 memset(banks
, 0, sizeof(banks
));
574 for (int f
= 84; f
<= 90; ++f
) {
575 if (loadSpriteBank(&banks
[f
], &resfile
, f
) <= 0) {
576 fprintf(stderr
, "FATAL: can't load sprite bank #%d!\n", f
);
580 if (loadSpriteBankFromFile(&banks
[256], "sprites/cursors.spr") <= 0) {
581 fprintf(stderr
, "FATAL: can't load 'cursors' sprite bank!\n");
585 if (vmInitialize() != 0) fatal("can't init VM");
587 memset(vmGVars
, 0, sizeof(vmGVars
));
590 vmAddLabel("flag_skip_title", LB_GVAR
, 120, 1); // 1: public
594 if ((csz
= loadCodeFile(&resfile
, vmCodeSize
, 93, 0)) < 1) {
595 fprintf(stderr
, "FATAL: can't load VM code!\n");
605 if ((buf
= loadDiskFileEx("goobers.vmd", &rsz
)) != NULL
) {
607 csz
= vmLoadCodeFileFromDump(buf
, rsz
, vmCodeSize
, vmMaxGVar
, vmMaxTVar
, &vmMaxGVar
, &vmMaxTVar
);
609 if (csz
> 0) vmCodeSize
+= csz
;
614 //vmGVars[GVAR_KEY_QUIT] = 0;
615 //vmGVars[GVAR_KEY_START] = 0;
616 vmGVars
[GVAR_POLYFIX_BASE
] = POLYFIX_BASE
;
617 vmGVars
[GVAR_GOOBERS
] = goobers
;
618 vmGVars
[GVAR_SOUND_DISABLED
] = disableSound
;
620 vmSetPC(0, CODE_ENTRY_TITLE
);
621 if (vmExecuteBSR(0, CODE_ENTRY_MAIN_INIT
, 0) != 0) fatal("can't initialize game");
622 //if (goobers) fprintf(stderr, "MAX LEVEL: %d\n", vmGVars[GVAR_MAX_LEVEL]);
625 VMLabelInfo
*l
= vmFindLabel("entry_goobers_init");
627 if (l
!= NULL
&& l
->type
== LB_CODE
) {
628 if (vmExecuteBSR(0, l
->value
, 0) != 0) fatal("can't initialize game");
634 for (int f
= 1; f
< argc
; ++f
) {
635 int lvl
= checkLevelCode(argv
[f
]);
638 vmGVars
[GVAR_START_LEVEL
] = lvl
;
651 int CALLBACK
WinMain (HINSTANCE hInstance
, HINSTANCE unused__
, LPSTR lpszCmdLine
, int nCmdShow
) {
652 char *shit
[] = { (char *)"shit", NULL
};
653 return SDL_main(1, shit
);