awasm: new code file format; fixes for (g|t)vars
[awish.git] / src / awish.c
blobe5f29ef52da9ab8b8f7ae17685e9b5f9badb5905
1 /*
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/>.
15 #include <ctype.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <unistd.h>
20 #include "SDL.h"
21 #include "SDL_endian.h"
23 #ifdef WIN32
24 # include <windows.h>
25 #endif
27 #include "resfile.h"
28 #include "video.h"
29 #include "mainloop.h"
30 #include "vm.h"
31 #include "gameglobals.h"
32 #include "game.h"
33 #include "title.h"
34 #include "polymod.h"
37 int goobers = 0;
38 int datfirst = 0;
41 ////////////////////////////////////////////////////////////////////////////////
42 static SDL_Surface *loadCFScreen (const char *fname, SDL_Surface *old, int w, int h, int chained) {
43 SDL_Surface *new;
44 static Uint8 pal[256][3];
45 static Uint32 opal[256];
46 static Uint8 scr[64000];
47 int rsz;
48 uint8_t *diskdata = tryDiskFile(fname, &rsz);
50 if (diskdata == NULL) return old;
51 if (rsz < w*h+(chained >= 0 ? 768 : 0)) { free(diskdata); return old; }
53 new = createSurface(320*2, 200*2);
54 if (!new) { free(diskdata); return old; }
55 SDL_SetColorKey(new, 0, 0); // clear colorkey info (it's the fullscreen image after all!)
57 for (int dy = 0; dy < 200; ++dy) {
58 for (int dx = 0; dx < 320; ++dx) {
59 putPixel2x(new, dx, dy, 0);
63 memcpy(scr, diskdata, w*h);
64 if (chained >= 0) memcpy(pal, diskdata+w*h, 768);
65 free(diskdata);
67 if (chained >= 0) {
68 memcpy(opal, palette, 256*4);
69 for (int f = 0; f < 256; ++f) {
70 pal[f][0] <<= 2;
71 if (pal[f][0] != 0) pal[f][0] |= 3;
72 pal[f][1] <<= 2;
73 if (pal[f][1] != 0) pal[f][1] |= 3;
74 pal[f][2] <<= 2;
75 if (pal[f][2] != 0) pal[f][2] |= 3;
76 palette[f] = SDL_MapRGB(screen->format, pal[f][0], pal[f][1], pal[f][2]);
80 if (chained > 0) {
81 for (int dy = 0; dy < h; ++dy) {
82 for (int dx = 0; dx < w/4; ++dx) {
83 for (int c = 0; c < 4; ++c) {
84 putPixel2x(new, dx*4+c, dy, scr[dy*(w/4)+(w*h)/4*c+dx]);
88 } else {
89 for (int dy = 0; dy < h; ++dy) {
90 for (int dx = 0; dx < w; ++dx) {
91 putPixel2x(new, dx, dy, scr[dy*w+dx]);
96 memcpy(palette, opal, 256*4);
97 SDL_FreeSurface(old);
98 return new;
102 ////////////////////////////////////////////////////////////////////////////////
103 static int loadFont (void) {
104 int sz;
105 uint8_t *buf = loadResFile(&resfile, 92, &sz);
107 if (buf == NULL) return -1;
108 if (sz != 256*8) { free(buf); return -1; }
109 memcpy(font, buf, 256*8);
110 free(buf);
111 return 0;
115 static int loadPalette (void) {
116 int sz;
117 uint8_t *buf = loadResFile(&resfile, 83, &sz);
119 if (buf == NULL) return -1;
120 if (sz != 768) { free(buf); return -1; }
121 for (int f = 0; f < 768; ++f) {
122 buf[f] <<= 2;
123 if (buf[f] != 0) buf[f] |= 3;
125 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]);
126 free(buf);
127 return 0;
131 static SDL_Surface *loadImage (ResFile *resfile, int idx) {
132 int sz;
133 static Uint8 *img;
134 SDL_Surface *res;
136 img = loadResFile(resfile, idx, &sz);
137 if (img == NULL) return NULL;
138 if (sz < 320*200) { free(img); return NULL; }
139 res = createSurface(320*2, 200*2);
140 if (res == NULL) { free(img); return NULL; }
141 SDL_SetColorKey(res, 0, 0); // clear colorkey info (it's the fullscreen image after all!)
142 for (int f = 0; f < 320*200; ++f) putPixel2x(res, f%320, f/320, img[f]);
143 free(img);
144 return res;
148 // return # of sprites loaded or <0 on error
149 static int loadSpriteBank (SpriteBank *bank, ResFile *resfile, int idx) {
150 Uint8 *buf;
151 int sz, pos, cnt;
153 if ((buf = loadResFile(resfile, idx, &sz)) == NULL) return -1;
154 if (sz < 3) { free(buf); return -1; }
155 pos = 0;
156 cnt = 0;
157 bank->count = 0;
158 while (pos+4 <= sz) {
159 int h = ((Uint32)buf[pos+0])+256*((Uint32)buf[pos+1]);
160 int w = ((Uint32)buf[pos+2])+256*((Uint32)buf[pos+3]);
162 pos += 4;
163 if (h > 0 && w > 0) {
164 SDL_Surface *s0 = createSurface(w*2, h*2);
165 SDL_Surface *s1 = createSurface(w*2, h*2);
167 if (!s0 || !s1) { free(buf); return -1; }
169 for (int y = 0; y < h; ++y) {
170 for (int x = 0; x < w; ++x) {
171 if (pos < sz) {
172 putPixel2x(s0, x, y, buf[pos]);
173 putPixel2x(s1, w-x-1, y, buf[pos]);
174 ++pos;
178 bank->spr[bank->count][0] = s0;
179 bank->spr[bank->count][1] = s1;
180 ++(bank->count);
183 free(buf);
184 return cnt;
188 // return # of sprites loaded or <0 on error
189 static int loadSpriteBankFromFile (SpriteBank *bank, const char *fname) {
190 Uint8 *buf;
191 int sz, pos, cnt;
193 if ((buf = loadDiskFileEx(fname, &sz)) == NULL) return -1;
194 if (sz < 3) { free(buf); return -1; }
195 pos = 0;
196 cnt = 0;
197 bank->count = 0;
198 while (pos+4 <= sz) {
199 int h = ((Uint32)buf[pos+0])+256*((Uint32)buf[pos+1]);
200 int w = ((Uint32)buf[pos+2])+256*((Uint32)buf[pos+3]);
202 pos += 4;
203 if (h > 0 && w > 0) {
204 SDL_Surface *s0 = createSurface(w*2, h*2);
205 SDL_Surface *s1 = createSurface(w*2, h*2);
207 if (!s0 || !s1) { free(buf); return -1; }
209 for (int y = 0; y < h; ++y) {
210 for (int x = 0; x < w; ++x) {
211 if (pos < sz) {
212 putPixel2x(s0, x, y, buf[pos]);
213 putPixel2x(s1, w-x-1, y, buf[pos]);
214 ++pos;
218 bank->spr[bank->count][0] = s0;
219 bank->spr[bank->count][1] = s1;
220 ++(bank->count);
223 free(buf);
224 return cnt;
229 static void freeSpriteBank (SpriteBank *bank) {
230 for (int f = bank->count-1; f >= 0; --f) {
231 SDL_FreeSurface(bank->spr[f][1]);
232 SDL_FreeSurface(bank->spr[f][0]);
238 ////////////////////////////////////////////////////////////////////////////////
239 static void quitCleanupRes (void) {
240 vmFreeLabels();
241 unloadAllSounds();
242 deinitResFile(&sndfile);
243 deinitResFile(&resfile);
244 //for (int f = 0; f < sizeof(banks)/sizeof(SpriteBank); ++f) freeSpriteBank(&banks[f]);
248 ////////////////////////////////////////////////////////////////////////////////
249 static int awishRST (int tid, int opcode, int argc, int argv[], int *argp[]) {
250 vmGVars[GVAR_RST_RESULT] = 0;
252 if (argv[0] == CONST_FRST_ML_TITLE) {
253 setMainLoopTitle();
254 } else if (argv[0] == CONST_FRST_ML_GAME) {
255 setMainLoopGame();
256 } else if (argv[0] == CONST_FRST_GET_MAX_THREADS) {
257 vmGVars[GVAR_RST_RESULT] = VM_MAX_THREADS;
258 } else if (argv[0] == CONST_FRST_GET_MAX_THREAD_ID) {
259 vmGVars[GVAR_RST_RESULT] = vmLastThread();
260 } else if (argv[0] == CONST_FRST_GET_RAND) {
261 int nmin = 0, nmax = 32767;
263 switch (argc) {
264 case 2: nmax = argv[1]; break;
265 case 3: nmin = argv[1]; nmax = argv[2]; break;
267 if (nmin >= nmax) {
268 vmGVars[GVAR_RST_RESULT] = nmin;
269 } else {
270 vmGVars[GVAR_RST_RESULT] = nmin+(randUInt32()%(nmax-nmin+1));
272 } else if (argv[0] == CONST_FRST_GET_SEED_H) {
273 vmGVars[GVAR_RST_RESULT] = getSeedH();
274 } else if (argv[0] == CONST_FRST_GET_SEED_L) {
275 vmGVars[GVAR_RST_RESULT] = getSeedL();
276 } else if (argv[0] == CONST_FRST_SET_SEED_H) {
277 if (argc >= 2) setSeedH(argv[1]);
278 } else if (argv[0] == CONST_FRST_SET_SEED_L) {
279 if (argc >= 2) setSeedL(argv[1]);
280 } else if (argv[0] == CONST_FRST_DEBUG_PRINT_STR) {
281 int pos = 0, len = 0;
283 switch (argc) {
284 case 1:
285 pos = vmPop(tid);
286 goto dolen;
287 case 2:
288 pos = argv[1];
289 dolen: if (pos >= 0) {
290 while (pos < vmCodeSize && vmCode[pos+len]) ++len;
292 break;
293 case 3:
294 pos = argv[1];
295 len = argv[2];
296 break;
298 if (goobers) {
299 if (pos >= -255 && pos <= -1) {
300 fputc(-pos, stderr);
301 } else {
302 for (; len > 0; --len, ++pos) if (pos >= 0 && pos < vmCodeSize) fputc(vmCode[pos], stderr);
305 } else if (argv[0] == CONST_FRST_DEBUG_PRINT_NUM) {
306 switch (argc) {
307 case 1: argv[1] = vmPop(tid); // fallthru
308 case 2: if (goobers) fprintf(stderr, "%d", argv[1]); break;
309 case 3: if (goobers) fprintf(stderr, "%d,%d", argv[1], argv[2]); break;
311 } else if (argv[0] == CONST_FRST_PLAY_SOUND) {
312 // arg1: sound index; arg2 (if present) channel; rst_result: -1 or channel
313 if (argc == 1) argv[1] = vmPop(tid);
314 if (argc < 2) argv[2] = -1;
315 vmGVars[GVAR_RST_RESULT] = playSound(argv[1], argv[2]);
316 } else if (argv[0] == CONST_FRST_STOP_CHANNEL) {
317 // arg1: channel
318 if (argc == 1) argv[1] = vmPop(tid);
319 vmGVars[GVAR_RST_RESULT] = stopChannel(argv[1]);
320 } else if (argv[0] == CONST_FRST_IS_CHANNEL_PLAYING) {
321 // arg1: channel; rst_result: bool
322 if (argc == 1) argv[1] = vmPop(tid);
323 vmGVars[GVAR_RST_RESULT] = isChannelPlaying(argv[1]);
324 } else if (argv[0] == CONST_FRST_LOAD_SOUND) {
325 // arg1: sound index; rst_result: bool
326 if (argc == 1) argv[1] = vmPop(tid);
327 vmGVars[GVAR_RST_RESULT] = loadSound(argv[1]) ? 0 : 1;
328 } else if (argv[0] == CONST_FRST_UNLOAD_SOUND) {
329 // arg1: sound index; rst_result: bool
330 if (argc == 1) argv[1] = vmPop(tid);
331 vmGVars[GVAR_RST_RESULT] = unloadSound(argv[1]) ? 0 : 1;
332 } else if (argv[0] == CONST_FRST_IS_SOUND_LOADED) {
333 // arg1: sound index; rst_result: bool
334 if (argc == 1) argv[1] = vmPop(tid);
335 vmGVars[GVAR_RST_RESULT] = isSoundLoaded(argv[1]) ? 0 : 1;
336 } else {
337 if (gameRSTCB) return gameRSTCB(tid, opcode, argc, argv, argp);
338 fatal("invalid RST: %d", argv[0]);
340 return 0; // continue
344 ////////////////////////////////////////////////////////////////////////////////
345 static int checkLevelCode (const char *t) {
346 int ctrd;
348 if (!t || !t[0]) return -1;
349 ctrd = vmNewThread(0);
350 for (int level = 0; level < vmGVars[GVAR_MAX_LEVEL]; ++level) {
351 //fprintf(stderr, "%d/%d\n", level+1, vmGVars[GVAR_MAX_LEVEL]);
352 vmPush(ctrd, level);
353 if (vmExecuteBSR(ctrd, CODE_ENTRY_GET_LEVEL_CODE, 0) == 0) {
354 int pos = vmGVars[GVAR_LEVEL_CODE_OFS], len = vmGVars[GVAR_LEVEL_CODE_LEN], ok = 1;
356 if (strlen(t) == len && pos >= 0 && len > 0 && pos+len <= vmCodeSize) {
357 //fwrite(vmCode+pos, len, 1, stderr);
358 //fprintf(stderr, " [%s]\n", t);
359 for (int f = 0; f < len; ++f) {
360 //fprintf(stderr, "%c %c\n", tolower(t[f]), tolower(vmCode[pos+f]));
361 if (tolower(t[f]) != tolower(vmCode[pos+f])) { ok = 0; break; }
363 if (ok) {
364 if (goobers) fprintf(stderr, "found code for level #%02d\n", level+1);
365 return level;
368 } else {
369 if (goobers) fprintf(stderr, "sorry!\n");
370 break;
373 vmKillThread(ctrd);
374 return -1;
378 ////////////////////////////////////////////////////////////////////////////////
379 #ifdef _WIN32
380 # include "cmdline.c"
381 #endif
384 ////////////////////////////////////////////////////////////////////////////////
385 int main (int argc, char *argv[]) {
386 int csz;
388 #ifdef _WIN32
389 cmdLineParse();
390 argc = k8argc;
391 argv = k8argv;
392 #endif
394 polymodInitialize();
396 for (int f = 1; f < argc; ++f) {
397 int eaten = 1;
399 if (strcmp(argv[f], "-goobers") == 0) goobers = 1;
400 else if (strcmp(argv[f], "-dat") == 0) datfirst = 1;
401 else if (strcmp(argv[f], "-trace") == 0) vmDebugTrace = 1;
402 else if (strcmp(argv[f], "-tracelog") == 0) {
403 eaten = 1;
404 vmDebugTrace = 1;
405 vmDebugOutput = fopen("ztrace.log", "w");
406 } else {
407 eaten = 0;
409 if (eaten > 0) {
410 for (int c = f+eaten; c < argc; ++c) argv[c-1] = argv[c];
411 argv[argc -= eaten] = NULL;
412 f -= eaten;
416 initResFile(&resfile, "RESOURCE.DAT");
417 initResFile(&sndfile, "RESOURCE.SND");
418 atexit(quitCleanupRes);
420 if (loadFont() != 0) {
421 fprintf(stderr, "FATAL: can't load font!\n");
422 exit(1);
425 sdlInit();
426 initVideo();
428 if (loadPalette() != 0) {
429 fprintf(stderr, "FATAL: can't load palette!\n");
430 exit(1);
434 SurfaceLock lock;
436 lockSurface(&lock, screen);
437 drawString(screen, "loading...", 2, 200-10, 1);
438 unlockSurface(&lock);
439 SDL_Flip(screen);
442 for (int f = 0; f < 8; ++f) {
443 if ((backs[(f+1)%8] = loadImage(&resfile, f)) == NULL) {
444 fprintf(stderr, "FATAL: can't load image #%d!\n", f);
445 exit(1);
449 backs[0] = loadCFScreen("CFTITLE.DAT", backs[0], 320, 200, 1);
450 backs[0] = loadCFScreen("cftitle.dat", backs[0], 320, 200, 1);
452 memset(banks, 0, sizeof(banks));
453 for (int f = 84; f <= 90; ++f) {
454 if (loadSpriteBank(&banks[f], &resfile, f)) {
455 fprintf(stderr, "FATAL: can't load sprite bank #%d!\n", f);
456 exit(1);
459 if (loadSpriteBankFromFile(&banks[256], "sprites/cursors.spr")) {
460 fprintf(stderr, "FATAL: can't load 'cursors' sprite bank!\n");
461 exit(1);
464 if (vmInitialize() != 0) fatal("can't init VM");
466 memset(vmGVars, 0, sizeof(vmGVars));
467 vmRSTCB = awishRST;
469 vmAddLabel("flag_skip_title", LB_GVAR, 120, 1); // 1: public
470 vmGVars[120] = 0;
472 vmCodeSize = 0;
473 if ((csz = loadCodeFile(&resfile, vmCodeSize, 93, 0)) < 1) {
474 fprintf(stderr, "FATAL: can't load VM code!\n");
475 exit(1);
477 vmCodeSize += csz;
478 initLabels();
481 int rsz = 0;
482 uint8_t *buf;
484 if ((buf = loadDiskFileEx("goobers.vmd", &rsz)) != NULL) {
485 if (buf != NULL) {
486 csz = vmLoadCodeFileFromDump(buf, rsz, vmCodeSize, vmMaxGVar, vmMaxTVar, &vmMaxGVar, &vmMaxTVar);
487 free(buf);
488 if (csz > 0) vmCodeSize += csz;
493 //vmGVars[GVAR_KEY_QUIT] = 0;
494 //vmGVars[GVAR_KEY_START] = 0;
495 vmGVars[GVAR_GOOBERS] = goobers;
497 vmSetPC(0, CODE_ENTRY_TITLE);
498 if (vmExecuteBSR(0, CODE_ENTRY_MAIN_INIT, 0) != 0) fatal("can't initialize game");
499 //if (goobers) fprintf(stderr, "MAX LEVEL: %d\n", vmGVars[GVAR_MAX_LEVEL]);
502 VMLabelInfo *l = vmFindLabel("entry_goobers_init");
504 if (l != NULL && l->type == LB_CODE) {
505 if (vmExecuteBSR(0, l->value, 0) != 0) fatal("can't initialize game");
509 if (argc > 1) {
510 // check level code
511 for (int f = 1; f < argc; ++f) {
512 int lvl = checkLevelCode(argv[f]);
514 if (lvl >= 0) {
515 vmGVars[GVAR_START_LEVEL] = lvl;
516 break;
521 mainLoop();
523 return 0;
527 #ifdef WIN32
528 int CALLBACK WinMain (HINSTANCE hInstance, HINSTANCE unused__, LPSTR lpszCmdLine, int nCmdShow) {
529 char *shit[] = { (char *)"shit", NULL };
530 return SDL_main(1, shit);
532 #endif