1 // main.cpp
2
3 #include "main.h"
4
5
6 cGame cMain::game;
7
8 unsigned int* gInstantfont = NULL;
9
cGame()10 cGame::cGame()
11 : pad1(NULL),
12 map1(map_zedwise),
13
14 camera(NULL),
15
16 world(NULL),
17
18 mission(0),
19 bgm("/usr/local/share/linwarrior/planet/ambient/ambient.wav"),
20
21 paused(false),
22 fullscreen(DEFAULT_FULLSCREEN),
23 wireframe(false),
24 nightvision(false),
25
26 printpad(false),
27 fps(DEFAULT_FPS),
28 width(DEFAULT_XRES),
29 height(DEFAULT_YRES),
30 depth(DEFAULT_BPP),
31 fov(DEFAULT_FOV) {
32 }
33
printHelp()34 void cGame::printHelp() {
35 std::cout << std::endl
36 << "LinWarrior" << std::endl
37 << " by Benjamin Pickhardt (benjamin.pickhardt*udo.edu)" << std::endl
38 << std::endl
39 << "Build:" << std::endl
40 << " " << __DATE__ << std::endl
41 << " " << __TIME__ << std::endl
42 << std::endl
43 << "Options:" << std::endl
44 << std::endl
45 << " --help prints this help screen" << std::endl
46 << " --resolution followed by resolution WIDTH or WIDTHxHEIGHT" << std::endl
47 << " --window start in windowed mode" << std::endl
48 << " --joypad followed by a (default) or b." << std::endl
49 << " The a-Type Gamepad is noticeable by zedwise" << std::endl
50 << " ordered buttons and b-Type by clockwise buttons" << std::endl
51 //<< " --mission followed by mission name (todo)" << std::endl
52 << " --bgm play background music wav file" << std::endl
53 << std::endl
54 << " All options can be abbreviated by their first letter:" << std::endl
55 << " --help is the same as -h" << std::endl
56 << std::endl
57 << std::endl;
58 }
59
parseArgs(int argc,char ** args)60 int cGame::parseArgs(int argc, char** args) {
61 // Walk through arguments until all consumed or an error occured.
62
63 loopi(argc) {
64 if (arg_i("--help")) {
65 return 1;
66 } else if (arg_i("--resolution")) {
67 advance_i();
68 std::string s = args[i];
69 int x0 = s.find('x', 0);
70 int x1 = s.find('x', x0 + 1);
71 // Is it WIDTHxHEIGHT? or else just Width?
72 if (x0 > 0) {
73 // Width
74 std::string w = s.substr(0, x0 - 0);
75 this->width = atoi(w.c_str());
76 // Height
77 std::string h = s.substr(x0 + 1, x1 - (x0 + 1));
78 this->height = atoi(h.c_str());
79 // Depth
80 if (x1 > 0) {
81 std::string d = s.substr(x1 + 1);
82 this->depth = atoi(d.c_str());
83 // unused value
84 }
85 } else {
86 // Just take the width and find a fitting height.
87 this->width = atoi(s.c_str());
88 double aspect = 4.0f / 3.0f;
89 if (this->width > 1024) aspect = 16.0f / 9.0f;
90 this->height = (int) (this->width / aspect);
91 this->height += (this->height % 2); // make even just in case.
92 }
93 std::cout << "Resolution: " << this->width << " x " << this->height << std::endl;
94 } else if (arg_i("--joypad")) {
95 advance_i();
96 std::string s = args[i];
97 if (s.compare("a")) {
98 this->map1 = map_zedwise;
99 } else if (s.compare("b")) {
100 this->map1 = map_clockwise;
101 } else {
102 printHelp();
103 }
104 } else if (arg_i("--window")) {
105 this->fullscreen = false;
106 } else if (arg_i("--mission")) {
107 advance_i();
108 std::string s = args[i];
109 this->mission = atoi(s.c_str());
110 } else if (arg_i("--bgm")) {
111 advance_i();
112 std::string s = args[i];
113 this->bgm = s;
114 }
115 }
116 return 0;
117 }
118
initMission()119 void cGame::initMission() {
120 this->world = new cWorld();
121 assert(this->world != NULL);
122 if (this->mission == 1) {
123 this->world->mMission = new cEmptyMission();
124 assert(this->world->mMission != NULL);
125 this->camera = this->world->mMission->init(this->world);
126 this->pad1 = this->camera->entity->pad;
127 } else {
128 this->world->mMission = new cTestMission();
129 assert(this->world->mMission != NULL);
130 this->camera = this->world->mMission->init(this->world);
131 this->pad1 = this->camera->entity->pad;
132 }
133 assert(this->camera != NULL);
134 assert(this->pad1 != NULL);
135 }
136
initGL(int width,int height)137 void cMain::initGL(int width, int height) {
138 cout << "Initialising initial OpenGL settings.\n";
139
140 #ifdef __WIN32
141 glTexImage3D = SDL_GL_GetProcAddress("glTexImage3D");
142 assert(glTexImage3D != NULL);
143 #endif
144
145 if (true) {
146 std::string glinfo;
147 glinfo = (const char*) glGetString(GL_RENDERER);
148 std::cout << glinfo << std::endl << std::endl;
149 glinfo = (const char*) glGetString(GL_VENDOR);
150 std::cout << glinfo << std::endl << std::endl;
151 glinfo = (const char*) glGetString(GL_VERSION);
152 std::cout << glinfo << std::endl << std::endl;
153 glinfo = (const char*) glGetString(GL_EXTENSIONS);
154 std::cout << glinfo << std::endl << std::endl;
155 if (glinfo.find("GL_EXT_texture3D", 0) == std::string::npos) {
156 cObject::ENABLE_TEXTURE_3D = 0;
157 }
158 }
159
160 glViewport(0, 0, width, height);
161
162 float fogColor[4] = {1.0, 1.0, 1.0, 1.0};
163 float fogDensity = 0.003;
164
165 glEnable(GL_BLEND);
166 glEnable(GL_DEPTH_TEST);
167
168 glClearAccum(0.0, 0.0, 0.0, 0.0);
169 glClearColor(0.0, 0.125, 0.0, 0.0);
170 //glClearDepth(1.0);
171 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_ACCUM_BUFFER_BIT);
172
173 glShadeModel(GL_SMOOTH);
174 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
175 //glDepthMask(GL_TRUE);
176 //glDepthFunc(GL_LEQUAL);
177
178 glFogi(GL_FOG_MODE, GL_EXP);
179 glFogfv(GL_FOG_COLOR, fogColor);
180 glFogf(GL_FOG_DENSITY, fogDensity);
181
182 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
183
184 // Lighting
185 {
186 glEnable(GL_LIGHTING);
187 float p[] = {0, 500, 0, 1};
188 float a[] = {0.5, 0.5, 0.5, 1};
189 glLightfv(GL_LIGHT0, GL_POSITION, p);
190 glLightfv(GL_LIGHT0, GL_AMBIENT, a);
191 glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, 1.0);
192 glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, 0.001);
193 glLightf(GL_LIGHT0, GL_QUADRATIC_ATTENUATION, 0.00001);
194 glEnable(GL_COLOR_MATERIAL);
195 glEnable(GL_NORMALIZE);
196 }
197
198 // Init instant console font.
199 glUploadFont();
200 }
201
drawFrame(int elapsed_msec)202 void cMain::drawFrame(int elapsed_msec) {
203 //std::cout << "elapsed: " << (elapsed_msec / 1000.0f) << std::endl;
204 //if (hurlmotion) elapsed = int(elapsed * 1.0f);
205
206 if (!game.paused) {
207 // Re-Cluster spatial index.
208 game.world->clusterObjects();
209
210 int subframes = 1;
211
212 loopi(subframes) {
213 // calculate the time that the last frame took.
214 game.world->advanceTime(elapsed_msec / subframes);
215
216 // Deliver overdue messages to objects and groups of objects.
217 game.world->dispatchMessages();
218
219 // Calculate new animation state and the like (if not paused).
220 game.world->animateObjects();
221
222 // Calculate positions through transformations.
223 glPushMatrix();
224 {
225 glLoadIdentity();
226 game.world->transformObjects();
227 }
228 glPopMatrix();
229 }
230 } else {
231 // Delete Fragged Objects of previous frames.
232 game.world->bagFragged();
233 }
234
235 // Draw fewer frames?
236 //static int cnt = 0;
237 //cnt++;
238 //if (cnt % 2 == 0) return;
239
240 // SOLID / WIREFRAME drawing scheme.
241 if (!game.wireframe) {
242 glClearColor(0.2, 0.2, 0.2, 1.0);
243 glEnable(GL_FOG);
244 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
245 glEnable(GL_CULL_FACE);
246 glEnable(GL_LIGHTING);
247 glEnable(GL_LIGHT0);
248 glClear(GL_DEPTH_BUFFER_BIT); // With Background.
249 } else {
250 glClearColor(0.2, 0.2, 0.2, 1.0);
251 glEnable(GL_FOG);
252 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
253 //glDisable(GL_CULL_FACE);
254 glDisable(GL_LIGHTING);
255 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // W/o background.
256 }
257
258
259 glPushPerspectiveProjection(game.fov);
260 {
261
262 // Setup camera.
263 glLoadIdentity();
264 if (game.camera) {
265 game.camera->multEyeMatrix();
266 game.camera->setAsAudioListener();
267 }
268
269 // Find objects in visible range.
270 float* origin = game.camera->traceable->pos.data();
271 float maxrange = game.world->mViewdistance;
272 float min[] = {origin[0] - maxrange, origin[2] - maxrange};
273 float max[] = {origin[0] + maxrange, origin[2] + maxrange};
274 std::list<cObject*>* objects = game.world->mCluster->findUsersetInterval(min, max);
275 assert(objects != NULL);
276 //cout << "vis:" << objects->size() << " vs " << mObjects.size() << endl;
277
278 // Draw the surounding Sky- and Ground-Dome/Box
279 game.world->drawBack();
280
281 // Draw the Objects themselves.
282 game.world->drawSolid(game.camera, objects);
283
284 // Draw the Object's translucent effects.
285 game.world->drawEffect(game.camera, objects);
286
287 // Delete list of visible objects.
288 delete objects;
289
290 // Draw the Head-Up-Display of the currently spectating Object.
291 game.camera->drawHUD();
292
293 }
294 glPopProjection();
295
296 glFlush();
297
298 if (game.nightvision) {
299 glAccumBlurInverse(0.83);
300 }
301 }
302
updateKey(Uint8 keysym)303 void cMain::updateKey(Uint8 keysym) {
304 if (keysym == _TEXTURE_KEY) {
305 cObject::ENABLE_TEXTURE_3D = !cObject::ENABLE_TEXTURE_3D;
306 } else if (keysym == _WIREFRAME_KEY) {
307 game.wireframe = !game.wireframe;
308 } else if (keysym == _NIGHTVISION_KEY) {
309 game.nightvision = !game.nightvision;
310 } else if (keysym == _PAUSE_KEY) {
311 game.paused = !game.paused;
312 } else {
313 if (keysym == SDLK_1) cWorld::instance->mViewdistance = 100;
314 else if (keysym == SDLK_2) cWorld::instance->mViewdistance = 150;
315 else if (keysym == SDLK_3) cWorld::instance->mViewdistance = 200;
316 else if (keysym == SDLK_4) cWorld::instance->mViewdistance = 250;
317 else if (keysym == SDLK_5) cWorld::instance->mViewdistance = 300;
318 else if (keysym == SDLK_6) cWorld::instance->mViewdistance = 350;
319 else if (keysym == SDLK_7) cWorld::instance->mViewdistance = 400;
320 else if (keysym == SDLK_8) cWorld::instance->mViewdistance = 450;
321 else if (keysym == SDLK_9) cWorld::instance->mViewdistance = 500;
322 else if (keysym == SDLK_0) cWorld::instance->mViewdistance = 5000;
323 }
324 }
325
updatePad(cPad * pad,SDL_Joystick * joy,int * mapping)326 void cMain::updatePad(cPad* pad, SDL_Joystick* joy, int* mapping) {
327 if (pad == NULL) return;
328
329 int default_mapping[] = {
330 0, 1, 2, 3,
331 4, 5, 6, 7,
332 8, 9, 10, 11,
333 12, 13, 14, 15,
334 16, 17, 18, 19,
335 20, 21, 22, 23,
336 24, 25, 26, 27,
337 28, 29, 30, 31
338 };
339 int* map = default_mapping;
340 if (mapping != NULL) map = mapping;
341
342 pad->reset();
343 if (joy != NULL) {
344
345 loopi(4) {
346 pad->setAxis((cPad::Axes) i, SDL_JoystickGetAxis(joy, i) / 32000.0f);
347 }
348
349 loopi(32) {
350 if (SDL_JoystickGetButton(joy, map[i])) pad->setButton((cPad::Buttons)i, true);
351 }
352 //pad->print();
353 }
354
355 Uint8 *keystate = SDL_GetKeyState(NULL);
356 if (keystate == NULL) return;
357 // First Analogue Control Stick
358 if (keystate[SDLK_LEFT] == 1) pad->setAxis(cPad::AX_LR1, -1.0f);
359 if (keystate[SDLK_RIGHT] == 1) pad->setAxis(cPad::AX_LR1, +1.0f);
360 if (keystate[SDLK_UP] == 1) pad->setAxis(cPad::AX_UD1, -1.0f);
361 if (keystate[SDLK_DOWN] == 1) pad->setAxis(cPad::AX_UD1, +1.0f);
362 // Start and Select
363 if (keystate[SDLK_RETURN] == 1) pad->setButton(cPad::BT_START, true);
364 if (keystate[SDLK_BACKSPACE] == 1) pad->setButton(cPad::BT_SELECT, true);
365 // Lx and Rx Buttons
366 if (keystate[SDLK_w] == 1) pad->setButton(cPad::BT_L1, true);
367 if (keystate[SDLK_a] == 1) pad->setButton(cPad::BT_L2, true);
368 if (keystate[SDLK_r] == 1) pad->setButton(cPad::BT_R1, true);
369 if (keystate[SDLK_g] == 1) pad->setButton(cPad::BT_R2, true);
370 // Primary Buttons
371 if (keystate[SDLK_s] == 1) pad->setButton(cPad::BT_SQUARE, true);
372 if (keystate[SDLK_e] == 1) pad->setButton(cPad::BT_DELTA, true);
373 if (keystate[SDLK_d] == 1) pad->setButton(cPad::BT_CROSS, true);
374 if (keystate[SDLK_f] == 1) pad->setButton(cPad::BT_CIRCLE, true);
375 // Second Analogue Stick or Coolie-Hat:
376 if (keystate[SDLK_LALT] == 0) {
377 if (keystate[SDLK_j]) pad->setAxis(cPad::AX_LR2, -1.0f);
378 if (keystate[SDLK_l]) pad->setAxis(cPad::AX_LR2, +1.0f);
379 if (keystate[SDLK_i]) pad->setAxis(cPad::AX_UD2, -1.0f);
380 if (keystate[SDLK_k]) pad->setAxis(cPad::AX_UD2, +1.0f);
381 } else {
382 if (keystate[SDLK_j]) pad->setButton(cPad::BT_HL, true);
383 if (keystate[SDLK_l]) pad->setButton(cPad::BT_HR, true);
384 if (keystate[SDLK_i]) pad->setButton(cPad::BT_HU, true);
385 if (keystate[SDLK_k]) pad->setButton(cPad::BT_HD, true);
386 };
387 // Stick Buttons
388 if (keystate[SDLK_PERIOD]) pad->setButton(cPad::BT_J1B, true);
389 if (keystate[SDLK_COMMA]) pad->setButton(cPad::BT_J2B, true);
390 }
391
alEnableSystem(bool en)392 int alEnableSystem(bool en) {
393 static ALCdevice *dev = NULL;
394 static ALCcontext *ctx = NULL;
395 static bool isenabled = false;
396
397 if (en && !isenabled) {
398 cout << "Enabling OpenAL\n";
399 dev = alcOpenDevice(NULL);
400 if (!dev) {
401 //fprintf(stderr, "Could not open OpenAL device.\n");
402 cout << "Could not open OpenAL device.\n";
403 return 1;
404 }
405 ctx = alcCreateContext(dev, NULL);
406 alcMakeContextCurrent(ctx);
407 if (!ctx) {
408 //fprintf(stderr, "Could not make OpenAL context current.\n");
409 cout << "Could not make OpenAL context current.\n";
410 return 1;
411 }
412 isenabled = true;
413 return 0;
414 } else if (!en && isenabled) {
415 cout << "Disabling OpenAL\n";
416 alcMakeContextCurrent(NULL);
417 alcDestroyContext(ctx);
418 alcCloseDevice(dev);
419 isenabled = false;
420 return 0;
421 } else {
422 return 1;
423 }
424 }
425
cleanup()426 void cleanup() {
427 SDL_Quit();
428 alutExit();
429 alEnableSystem(false);
430 }
431
sdlmain(int argc,char ** args)432 int cMain::sdlmain(int argc, char** args) {
433 std::cout << "LinWarrior 3D (Build " __DATE__ ") by hackcraft.de" << std::endl << std::endl;
434
435 if (game.parseArgs(argc, args)) {
436 game.printHelp();
437 return 1;
438 }
439
440 double spf = 1.0 / (double) game.fps;
441
442 // Initialize SDL video and input
443 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) {
444 std::cout << "Unable to init SDL: " << SDL_GetError() << std::endl;
445 return 1;
446 }
447
448 // Init Video-Mode
449 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
450 SDL_Surface* screen = NULL;
451 if (game.fullscreen) screen = SDL_SetVideoMode(game.width, game.height, 0, SDL_OPENGLBLIT | SDL_HWSURFACE | SDL_DOUBLEBUF | SDL_FULLSCREEN);
452 else screen = SDL_SetVideoMode(game.width, game.height, 0, SDL_OPENGLBLIT | SDL_HWSURFACE | SDL_DOUBLEBUF);
453 if (!screen) {
454 std::cout << "Unable to set " << game.width << " x " << game.height << "video: " << SDL_GetError() << std::endl;
455 return 1;
456 }
457
458 // Set custom window-title.
459 char* title;
460 char* icon;
461 SDL_WM_GetCaption(&title, &icon);
462 SDL_WM_SetCaption("LinWarrior 3D (Build " __DATE__ ") by hackcraft.de", icon);
463
464 cout << "Trying to open audio.\n";
465
466 if (alEnableSystem(true)) {
467 cout << "Could not enable OpenAL.\n";
468 }
469
470 //if (!alutInit(&argc, args)) {
471 if (!alutInitWithoutContext(NULL, NULL)) {
472 std::cout << "Unable to init ALUT: " << alGetError() << std::endl;
473 // Maybe just let it run without sound? Hope for no followup errors.
474 //return 1;
475 }
476 cout << "\nALUT supported file formats:\n" << alutGetMIMETypes(ALUT_LOADER_BUFFER) << "\n\n";
477 cout << "\nALUT supported memory formats:\n" << alutGetMIMETypes(ALUT_LOADER_MEMORY) << "\n\n";
478 alGetError();
479 //alDopplerFactor(2.0);
480 //alSpeedOfSound(343.3*0.1);
481
482 // Ambient noise loop.
483 if (1) {
484 cout << "Loading Background Music.\n";
485 try {
486 ALuint buffer;
487 ALuint source;
488
489 //buffer = alutCreateBufferHelloWorld();
490 buffer = alutCreateBufferFromFile(game.bgm.c_str());
491 if (buffer == AL_NONE) throw "Could not create buffer from file for background music.";
492 alGenSources(1, &source);
493 if (alGetError() != AL_NO_ERROR) throw "Could not generate background music source.";
494 alSourcei(source, AL_BUFFER, buffer);
495 alSourcef(source, AL_PITCH, 1.0f);
496 alSourcef(source, AL_GAIN, 1.0f);
497
498 float zero[] = {0.0f, 0.0f, 0.0f};
499 alSourcefv(source, AL_POSITION, zero);
500 alSourcefv(source, AL_VELOCITY, zero);
501 alSourcei(source, AL_LOOPING, AL_TRUE);
502 alSourcei(source, AL_SOURCE_RELATIVE, AL_TRUE);
503 alSourcePlay(source);
504 } catch (const char* s) {
505 cout << "Sorry no bgm possible.\n" << s << "\n";
506 }
507 }
508
509 initGL(screen->w, screen->h);
510 game.initMission();
511
512 cout << "back in sdl main.\n";
513
514 SDL_ShowCursor(SDL_DISABLE);
515 SDL_Joystick* joy0 = SDL_JoystickOpen(0);
516
517 // Cleanup afterwards.
518 atexit(cleanup);
519
520 // program main loop
521 bool done = false;
522 unsigned long start_ms = SDL_GetTicks();
523 while (!done) {
524 //cout << "loop\n";
525
526 // message processing loop
527 SDL_Event event;
528 while (SDL_PollEvent(&event)) {
529 // check for messages
530 switch (event.type) {
531 // exit if the window is closed
532 case SDL_QUIT:
533 {
534 done = true;
535 break;
536 }
537 // check for keypresses
538 case SDL_KEYDOWN:
539 {
540 // Keymapping for virtual gamepad.
541 updateKey(event.key.keysym.sym);
542 // exit if ESCAPE is pressed
543 if (event.key.keysym.sym == SDLK_ESCAPE) done = true;
544 break;
545 }
546 } // switch
547 } // while poll event
548
549 // Possibly print out info on pushed buttons.
550 if (game.printpad) loopi(32) {
551 if (SDL_JoystickGetButton(joy0, i)) std::cout << "Button" << i << std::endl;
552 }
553 // Transfer real Joystick-/Gamepad-Input into the virtual gamepad
554 // of the player by using a (re-)mapping.
555 updatePad(game.pad1, joy0, game.map1);
556 // After securing Matrix: update world and draw frame.
557 //glPushMatrix();
558 {
559 //cout << "[\n";
560 drawFrame(spf * 1000.0f);
561 //cout << "]\n";
562 }
563 //glPopMatrix();
564
565 // Lock framerate and give back idle time to the os,
566 // other processes or threads.
567 {
568 spf = (1.0f / game.fps);
569 // msec since frame started (end of last frame).
570 long delta_ms = SDL_GetTicks() - start_ms;
571 // Necessary delay to lock frame to that frame rate.
572 const unsigned long frame_ms = (long) (spf * 1000);
573 long delay_ms = frame_ms - delta_ms;
574 if (delay_ms > 0) {
575 // Wake me up *before* time is up
576 const long min_delay_ms = 3;
577 if (delay_ms >= min_delay_ms) { // Some can only sleep >= x ms.
578 long os_delay_ms = delay_ms - min_delay_ms;
579 alutSleep(os_delay_ms * 0.001);
580 //SDL_Delay(os_delay_ms);
581 //std::cout << os_delay_ms << std::endl;
582 }
583 // debug timing
584 //std::cout << "framebudget " << frame_ms << "ms of which " << frame_ms - delay_ms << "ms were used, remaining to waste " << delay_ms << "ms." << std::endl;
585 //std::cout << "framebudget " << frame_ms << "ms - " << frame_ms - delay_ms << "ms = " << delay_ms << "ms" << std::endl;
586 // Syncing in the last msecs by hand:
587 int c = 0;
588 while (true) {
589 if (SDL_GetTicks() - start_ms >= frame_ms) break;
590 c++;
591 }
592 }
593 }
594 start_ms = SDL_GetTicks();
595
596 SDL_GL_SwapBuffers();
597
598 } // end main loop
599
600 std::cout << "Thank you for playing." << std::endl;
601 return 0;
602 }
603
604
605 //
606 // GOD begins below!
607 //
608
609 #ifdef n_WIN32
610
WinMain(void * hInstance,void * hPrevInstance,char * lpCmdLine,int nCmdShow)611 extern "C" int __stdcall WinMain(void* hInstance, void* hPrevInstance, char* lpCmdLine, int nCmdShow) {
612 int argc = 2;
613 char *args[] = {"Nothing", "Nothing", "Nothing"};
614 #else
615
616 int main(int argc, char **args) {
617 #endif
618 try {
619 return cMain::sdlmain(argc, args);
620 } catch (char* s) {
621 cout << "Fatal exception caught:\n" << s << endl;
622 }
623 }
624
625