added 'common include file'
[awish.git] / src / awish.c
blob83e5adef897923a3dc205a757644448052fbb636
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 "awishcommon.h"
28 #include "resfile.h"
29 #include "video.h"
30 #include "mainloop.h"
31 #include "vm.h"
32 #include "gameglobals.h"
33 #include "game.h"
34 #include "title.h"
35 #include "polymod.h"
38 int goobers = 0;
39 int datfirst = 0;
42 ////////////////////////////////////////////////////////////////////////////////
43 static SDL_Surface *loadCFScreen (const char *fname, SDL_Surface *old, int w, int h, int chained) {
44 SDL_Surface *new;
45 static Uint8 pal[256][3];
46 static Uint32 opal[256];
47 static Uint8 scr[64000];
48 int rsz;
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);
66 free(diskdata);
68 if (chained >= 0) {
69 memcpy(opal, palette, 256*4);
70 for (int f = 0; f < 256; ++f) {
71 pal[f][0] <<= 2;
72 if (pal[f][0] != 0) pal[f][0] |= 3;
73 pal[f][1] <<= 2;
74 if (pal[f][1] != 0) pal[f][1] |= 3;
75 pal[f][2] <<= 2;
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]);
81 if (chained > 0) {
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]);
89 } else {
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);
98 SDL_FreeSurface(old);
99 return new;
103 ////////////////////////////////////////////////////////////////////////////////
104 static int loadFont (void) {
105 int sz;
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);
111 free(buf);
112 return 0;
116 static int loadPalette (void) {
117 int sz;
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) {
123 buf[f] <<= 2;
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]);
127 free(buf);
128 return 0;
132 static SDL_Surface *loadImage (ResFile *resfile, int idx) {
133 int sz;
134 static Uint8 *img;
135 SDL_Surface *res;
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]);
144 free(img);
145 return res;
149 // return # of sprites loaded or <0 on error
150 static int loadSpriteBank (SpriteBank *bank, ResFile *resfile, int idx) {
151 Uint8 *buf;
152 int sz, pos;
154 if ((buf = loadResFile(resfile, idx, &sz)) == NULL) return -1;
155 if (sz < 3) { free(buf); return -1; }
156 pos = 0;
157 for (int f = bank->count-1; f >= 0; --f) {
158 SDL_FreeSurface(bank->spr[f][1]);
159 SDL_FreeSurface(bank->spr[f][0]);
161 bank->count = 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]);
166 pos += 4;
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) {
175 if (pos < sz) {
176 putPixel2x(s0, x, y, buf[pos]);
177 putPixel2x(s1, w-x-1, y, buf[pos]);
178 ++pos;
182 bank->spr[bank->count][0] = s0;
183 bank->spr[bank->count][1] = s1;
184 ++(bank->count);
187 free(buf);
188 return bank->count;
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) {
204 vmFreeLabels();
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) {
218 setMainLoopTitle();
219 } else if (argv[0] == CONST_FRST_ML_GAME) {
220 setMainLoopGame();
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;
228 switch (argc) {
229 case 2: nmax = argv[1]; break;
230 case 3: nmin = argv[1]; nmax = argv[2]; break;
232 if (nmin >= nmax) {
233 vmGVars[GVAR_RST_RESULT] = nmin;
234 } else {
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;
248 switch (argc) {
249 case 1:
250 pos = vmPop(tid);
251 goto dolen;
252 case 2:
253 pos = argv[1];
254 dolen: if (pos >= 0) {
255 while (pos < vmCodeSize && vmCode[pos+len]) ++len;
257 break;
258 case 3:
259 pos = argv[1];
260 len = argv[2];
261 break;
263 if (goobers) {
264 if (pos >= -255 && pos <= -1) {
265 fputc(-pos, stderr);
266 } else {
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) {
271 switch (argc) {
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) {
282 // arg1: 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)
303 int fnpos;
305 switch (argc) {
306 case 1:
307 argv[2] = vmPop(tid);
308 argv[1] = vmPop(tid);
309 break;
310 case 2:
311 argv[2] = vmPop(tid);
312 break;
315 fnpos = argv[2];
316 vmGVars[GVAR_RST_RESULT] = -1;
317 if (fnpos >= 0 && fnpos < vmCodeSize-1 && argv[1] >= 0 && argv[1] <= 256) {
318 int end;
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;
331 dir = vmPop(tid);
332 num = vmPop(tid);
333 bank = vmPop(tid);
334 layer = vmPop(tid);
335 switch (argc) {
336 case 1:
337 y = vmPop(tid);
338 x = vmPop(tid);
339 break;
340 case 2:
341 y = vmPop(tid);
342 x = argv[1];
343 break;
344 case 3:
345 y = argv[2];
346 x = argv[1];
347 break;
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;
356 switch (argc) {
357 case 1:
358 ofsy = vmPop(tid);
359 ofsx = vmPop(tid);
360 break;
361 case 2:
362 ofsy = vmPop(tid);
363 ofsx = argv[1];
364 break;
365 case 3:
366 ofsy = argv[2];
367 ofsx = argv[1];
368 break;
370 pmStart(ofsx, ofsy, scale, angle);
371 } else if (argv[0] == CONST_FRST_ADD_POLY_POINT) {
372 switch (argc) {
373 case 1:
374 argv[2] = vmPop(tid);
375 argv[1] = vmPop(tid);
376 break;
377 case 2:
378 argv[2] = vmPop(tid);
379 break;
381 pmAddPoint(argv[1], argv[2]);
382 } else if (argv[0] == CONST_FRST_END_POLY) {
383 switch (argc) {
384 case 1:
385 argv[2] = vmPop(tid);
386 argv[1] = vmPop(tid);
387 break;
388 case 2:
389 argv[2] = vmPop(tid);
390 break;
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;
404 switch (argc) {
405 case 1:
406 addr = vmPop(tid);
407 break;
408 case 2:
409 addr = argv[1];
410 break;
411 case 3:
412 addr = argv[1];
413 len = argv[2];
414 break;
416 if (len != 0 && addr >= 0 && addr < vmCodeSize) {
417 str += addr;
418 if (len < 0) {
419 for (len = 0; addr+len < vmCodeSize && str[len]; ++len) ;
420 } else {
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);
430 free(s);
431 } else {
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]];
441 } else {
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) {
451 int ctrd;
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]);
457 vmPush(ctrd, 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; }
468 if (ok) {
469 if (goobers) fprintf(stderr, "found code for level #%02d\n", level+1);
470 return level;
473 } else {
474 if (goobers) fprintf(stderr, "sorry!\n");
475 break;
478 vmKillThread(ctrd);
479 return -1;
483 ////////////////////////////////////////////////////////////////////////////////
484 #ifdef _WIN32
485 # include "cmdline.c"
486 #endif
489 ////////////////////////////////////////////////////////////////////////////////
490 int main (int argc, char *argv[]) {
491 int csz;
492 int disableSound = 0;
494 #ifdef _WIN32
495 cmdLineParse();
496 argc = k8argc;
497 argv = k8argv;
498 #endif
500 memset(banks, 0, sizeof(banks));
502 polymodInitialize();
504 for (int f = 1; f < argc; ++f) {
505 int eaten = 1;
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) {
518 eaten = 1;
519 vmDebugTrace = 1;
520 vmDebugOutput = fopen("ztrace.log", "w");
521 } else {
522 eaten = 0;
524 if (eaten > 0) {
525 for (int c = f+eaten; c < argc; ++c) argv[c-1] = argv[c];
526 argv[argc -= eaten] = NULL;
527 f -= eaten;
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");
537 exit(1);
540 sdlInit();
541 initVideo();
543 clearSpriteLayers();
545 if (loadPalette() != 0) {
546 fprintf(stderr, "FATAL: can't load palette!\n");
547 exit(1);
551 SurfaceLock lock;
553 lockSurface(&lock, screen);
554 drawString(screen, "loading...", 2, 200-10, 1);
555 unlockSurface(&lock);
556 SDL_Flip(screen);
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);
562 exit(1);
565 if ((backs[8] = loadImage(&resfile, 8)) == NULL) {
566 fprintf(stderr, "FATAL: can't load image #%d!\n", 8);
567 exit(1);
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);
577 exit(1);
580 if (loadSpriteBankFromFile(&banks[256], "sprites/cursors.spr") <= 0) {
581 fprintf(stderr, "FATAL: can't load 'cursors' sprite bank!\n");
582 exit(1);
585 if (vmInitialize() != 0) fatal("can't init VM");
587 memset(vmGVars, 0, sizeof(vmGVars));
588 vmRSTCB = awishRST;
590 vmAddLabel("flag_skip_title", LB_GVAR, 120, 1); // 1: public
591 vmGVars[120] = 0;
593 vmCodeSize = 0;
594 if ((csz = loadCodeFile(&resfile, vmCodeSize, 93, 0)) < 1) {
595 fprintf(stderr, "FATAL: can't load VM code!\n");
596 exit(1);
598 vmCodeSize += csz;
599 initLabels();
601 if (goobers) {
602 int rsz = 0;
603 uint8_t *buf;
605 if ((buf = loadDiskFileEx("goobers.vmd", &rsz)) != NULL) {
606 if (buf != NULL) {
607 csz = vmLoadCodeFileFromDump(buf, rsz, vmCodeSize, vmMaxGVar, vmMaxTVar, &vmMaxGVar, &vmMaxTVar);
608 free(buf);
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");
632 if (argc > 1) {
633 // check level code
634 for (int f = 1; f < argc; ++f) {
635 int lvl = checkLevelCode(argv[f]);
637 if (lvl >= 0) {
638 vmGVars[GVAR_START_LEVEL] = lvl;
639 break;
644 mainLoop();
646 return 0;
650 #ifdef WIN32
651 int CALLBACK WinMain (HINSTANCE hInstance, HINSTANCE unused__, LPSTR lpszCmdLine, int nCmdShow) {
652 char *shit[] = { (char *)"shit", NULL };
653 return SDL_main(1, shit);
655 #endif