]> git.jsancho.org Git - lugaru.git/blob - Source/main.cpp
Avoid using Variable Length Arrays (which are refused by clang)
[lugaru.git] / Source / main.cpp
1 /*
2 Copyright (C) 2003, 2010 - Wolfire Games
3 Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file)
4
5 This file is part of Lugaru.
6
7 Lugaru is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 Lugaru is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Lugaru.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include <math.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <fstream>
25 #include <iostream>
26 #include <zlib.h>
27 #include <set>
28 #include "gamegl.h"
29 #include "MacCompatibility.h"
30 #include "Settings.h"
31
32 #include "Game.h"
33
34 using namespace Game;
35
36 #include "openal_wrapper.h"
37
38 #ifdef WIN32
39 #include <windows.h>
40 #include <shellapi.h>
41 #include "win-res/resource.h"
42 #endif
43
44 extern float multiplier;
45 extern float realmultiplier;
46 extern int slomo;
47 extern bool cellophane;
48 extern float texdetail;
49
50 extern bool freeze;
51 extern bool stillloading;
52 extern int mainmenu;
53
54 extern float slomospeed;
55 extern float slomofreq;
56 extern bool visibleloading;
57
58 extern SDL_Window *sdlwindow;
59
60 using namespace std;
61
62 set<pair<int,int>> resolutions;
63
64 // statics/globals (internal only) ------------------------------------------
65
66 #ifdef _MSC_VER
67 #pragma warning(push)
68 #pragma warning(disable: 4273)
69 #endif
70
71 #ifndef __MINGW32__ // FIXME: Temporary workaround for GL-8
72 #define GL_FUNC(ret,fn,params,call,rt) \
73     extern "C" { \
74         static ret (GLAPIENTRY *p##fn) params = NULL; \
75         ret GLAPIENTRY fn params { rt p##fn call; } \
76     }
77 #include "glstubs.h"
78 #undef GL_FUNC
79 #endif // __MINGW32__
80
81 #ifdef _MSC_VER
82 #pragma warning(pop)
83 #endif
84
85 static bool lookup_glsym(const char *funcname, void **func)
86 {
87     *func = SDL_GL_GetProcAddress(funcname);
88     if (*func == NULL) {
89         fprintf(stderr, "Failed to find OpenGL symbol \"%s\"\n", funcname);
90         return false;
91     }
92     return true;
93 }
94
95 static bool lookup_all_glsyms(void)
96 {
97     bool retval = true;
98 #ifndef __MINGW32__ // FIXME: Temporary workaround for GL-8
99 #define GL_FUNC(ret,fn,params,call,rt) \
100         if (!lookup_glsym(#fn, (void **) &p##fn)) retval = false;
101 #include "glstubs.h"
102 #undef GL_FUNC
103 #endif // __MINGW32__
104     return retval;
105 }
106
107 #ifndef __MINGW32__ // FIXME: Temporary workaround for GL-8
108 static void GLAPIENTRY glDeleteTextures_doNothing(GLsizei n, const GLuint *textures)
109 {
110     // no-op.
111 }
112 #endif // __MINGW32__
113
114 #ifdef MessageBox
115 #undef MessageBox
116 #endif
117 #define MessageBox(hwnd,text,title,flags) STUBBED("msgbox")
118
119 // Menu defs
120
121 int kContextWidth;
122 int kContextHeight;
123
124 //-----------------------------------------------------------------------------------------------------------------------
125
126 // OpenGL Drawing
127
128 void initGL()
129 {
130     glClear( GL_COLOR_BUFFER_BIT );
131     swap_gl_buffers();
132
133     // clear all states
134     glDisable( GL_ALPHA_TEST);
135     glDisable( GL_BLEND);
136     glDisable( GL_DEPTH_TEST);
137     glDisable( GL_FOG);
138     glDisable( GL_LIGHTING);
139     glDisable( GL_LOGIC_OP);
140     glDisable( GL_TEXTURE_1D);
141     glDisable( GL_TEXTURE_2D);
142     glPixelTransferi( GL_MAP_COLOR, GL_FALSE);
143     glPixelTransferi( GL_RED_SCALE, 1);
144     glPixelTransferi( GL_RED_BIAS, 0);
145     glPixelTransferi( GL_GREEN_SCALE, 1);
146     glPixelTransferi( GL_GREEN_BIAS, 0);
147     glPixelTransferi( GL_BLUE_SCALE, 1);
148     glPixelTransferi( GL_BLUE_BIAS, 0);
149     glPixelTransferi( GL_ALPHA_SCALE, 1);
150     glPixelTransferi( GL_ALPHA_BIAS, 0);
151
152     // set initial rendering states
153     glShadeModel( GL_SMOOTH);
154     glClearDepth( 1.0f);
155     glDepthFunc( GL_LEQUAL);
156     glDepthMask( GL_TRUE);
157     glEnable( GL_DEPTH_TEST);
158     glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
159     glCullFace( GL_FRONT);
160     glEnable( GL_CULL_FACE);
161     glEnable( GL_LIGHTING);
162     glEnable( GL_DITHER);
163     glEnable( GL_COLOR_MATERIAL);
164     glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
165     glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
166     glAlphaFunc( GL_GREATER, 0.5f);
167
168     if ( CanInitStereo(stereomode) ) {
169         InitStereo(stereomode);
170     } else {
171         fprintf(stderr, "Failed to initialize stereo, disabling.\n");
172         stereomode = stereoNone;
173     }
174 }
175
176 void toggleFullscreen()
177 {
178     fullscreen = !fullscreen;
179     Uint32 flags = SDL_GetWindowFlags(sdlwindow);
180     if (flags & SDL_WINDOW_FULLSCREEN) {
181         flags &= ~SDL_WINDOW_FULLSCREEN;
182     } else {
183         flags |= SDL_WINDOW_FULLSCREEN;
184     }
185     SDL_SetWindowFullscreen(sdlwindow, flags);
186 }
187
188 SDL_bool sdlEventProc(const SDL_Event &e)
189 {
190     switch (e.type) {
191         case SDL_QUIT:
192             return SDL_FALSE;
193
194         case SDL_WINDOWEVENT:
195             if (e.window.event == SDL_WINDOWEVENT_CLOSE) {
196                 return SDL_FALSE;
197             }
198         break;
199
200         case SDL_MOUSEMOTION:
201             deltah += e.motion.xrel;
202             deltav += e.motion.yrel;
203         break;
204
205         case SDL_KEYDOWN:
206             if ((e.key.keysym.scancode == SDL_SCANCODE_G) &&
207                 (e.key.keysym.mod & KMOD_CTRL)) {
208                 SDL_bool mode = SDL_TRUE;
209                 if ((SDL_GetWindowFlags(sdlwindow) & SDL_WINDOW_FULLSCREEN) == 0)
210                     mode = (SDL_GetWindowGrab(sdlwindow) ? SDL_FALSE : SDL_TRUE);
211                 SDL_SetWindowGrab(sdlwindow, mode);
212                 SDL_SetRelativeMouseMode(mode);
213             } else if ( (e.key.keysym.scancode == SDL_SCANCODE_RETURN) && (e.key.keysym.mod & KMOD_ALT) ) {
214                 toggleFullscreen();
215             }
216         break;
217     }
218     return SDL_TRUE;
219 }
220
221 // --------------------------------------------------------------------------
222
223 static Point gMidPoint;
224
225 bool SetUp ()
226 {
227     LOGFUNC;
228
229     cellophane = 0;
230     texdetail = 4;
231     slomospeed = 0.25;
232     slomofreq = 8012;
233
234     DefaultSettings();
235
236     if (!SDL_WasInit(SDL_INIT_VIDEO))
237         if (SDL_Init(SDL_INIT_VIDEO) == -1) {
238             fprintf(stderr, "SDL_Init() failed: %s\n", SDL_GetError());
239             return false;
240         }
241     if (!LoadSettings()) {
242         fprintf(stderr, "Failed to load config, creating default\n");
243         SaveSettings();
244     }
245
246     if (SDL_GL_LoadLibrary(NULL) == -1) {
247         fprintf(stderr, "SDL_GL_LoadLibrary() failed: %s\n", SDL_GetError());
248         SDL_Quit();
249         return false;
250     }
251
252     for (int displayIdx = 0; displayIdx < SDL_GetNumVideoDisplays(); ++displayIdx) {
253         for (int i = 0; i < SDL_GetNumDisplayModes(displayIdx); ++i) {
254             SDL_DisplayMode mode;
255             if (SDL_GetDisplayMode(displayIdx, i, &mode) == -1)
256                 continue;
257             if ((mode.w < 640) || (mode.h < 480))
258                 continue;  // sane lower limit.
259             pair<int,int> resolution(mode.w, mode.h);
260             resolutions.insert(resolution);
261         }
262     }
263
264     if (resolutions.empty()) {
265         const std::string error = "No suitable video resolutions found.";
266         cerr << error << endl;
267         SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Lugaru init failed!", error.c_str(), NULL);
268         SDL_Quit();
269         return false;
270     }
271
272     if (commandLineOptions[SHOWRESOLUTIONS]) {
273         printf("Available resolutions:\n");
274         for (auto resolution = resolutions.begin(); resolution != resolutions.end(); resolution++) {
275             printf("  %d x %d\n", (int) resolution->first, (int) resolution->second);
276         }
277     }
278
279     SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
280     SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 1);
281
282     Uint32 sdlflags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN;
283     if (commandLineOptions[FULLSCREEN]) {
284         fullscreen = commandLineOptions[FULLSCREEN].last()->type();
285     }
286     if (fullscreen) {
287         sdlflags |= SDL_WINDOW_FULLSCREEN;
288     }
289     if (!commandLineOptions[NOMOUSEGRAB].last()->type()) {
290         sdlflags |= SDL_WINDOW_INPUT_GRABBED;
291     }
292
293     sdlwindow = SDL_CreateWindow("Lugaru", SDL_WINDOWPOS_CENTERED_DISPLAY(0), SDL_WINDOWPOS_CENTERED_DISPLAY(0),
294                                  kContextWidth, kContextHeight, sdlflags);
295
296     if (!sdlwindow) {
297         fprintf(stderr, "SDL_CreateWindow() failed: %s\n", SDL_GetError());
298         fprintf(stderr, "forcing 640x480...\n");
299         kContextWidth = 640;
300         kContextHeight = 480;
301         sdlwindow = SDL_CreateWindow("Lugaru", SDL_WINDOWPOS_CENTERED_DISPLAY(0), SDL_WINDOWPOS_CENTERED_DISPLAY(0),
302                                      kContextWidth, kContextHeight, sdlflags);
303         if (!sdlwindow) {
304             fprintf(stderr, "SDL_CreateWindow() failed: %s\n", SDL_GetError());
305             fprintf(stderr, "forcing 640x480 windowed mode...\n");
306             sdlflags &= ~SDL_WINDOW_FULLSCREEN;
307             sdlwindow = SDL_CreateWindow("Lugaru", SDL_WINDOWPOS_CENTERED_DISPLAY(0), SDL_WINDOWPOS_CENTERED_DISPLAY(0),
308                                          kContextWidth, kContextHeight, sdlflags);
309
310             if (!sdlwindow) {
311                 fprintf(stderr, "SDL_CreateWindow() failed: %s\n", SDL_GetError());
312                 return false;
313             }
314         }
315     }
316
317     SDL_GLContext glctx = SDL_GL_CreateContext(sdlwindow);
318     if (!glctx) {
319         fprintf(stderr, "SDL_GL_CreateContext() failed: %s\n", SDL_GetError());
320         SDL_Quit();
321         return false;
322     }
323
324     SDL_GL_MakeCurrent(sdlwindow, glctx);
325
326     if (!lookup_all_glsyms()) {
327         fprintf(stderr, "Missing required OpenGL functions.\n");
328         SDL_Quit();
329         return false;
330     }
331
332     int dblbuf = 0;
333     if ((SDL_GL_GetAttribute(SDL_GL_DOUBLEBUFFER, &dblbuf) == -1) || (!dblbuf))
334     {
335         fprintf(stderr, "Failed to get a double-buffered context.\n");
336         SDL_Quit();
337         return false;
338     }
339
340     if (SDL_GL_SetSwapInterval(-1) == -1)  // try swap_tear first.
341         SDL_GL_SetSwapInterval(1);
342
343     SDL_ShowCursor(0);
344     if (!commandLineOptions[NOMOUSEGRAB].last()->type()) {
345         SDL_SetRelativeMouseMode(SDL_TRUE);
346     }
347
348     initGL();
349
350     GLint width = kContextWidth;
351     GLint height = kContextHeight;
352     gMidPoint.h = width / 2;
353     gMidPoint.v = height / 2;
354     screenwidth = width;
355     screenheight = height;
356
357     newdetail = detail;
358     newscreenwidth = screenwidth;
359     newscreenheight = screenheight;
360
361     /* If saved resolution is not in the list, add it to the list (so that it’s selectable in the options) */
362     pair<int,int> startresolution(width,height);
363     if (resolutions.find(startresolution) == resolutions.end()) {
364         resolutions.insert(startresolution);
365     }
366
367     InitGame();
368
369     return true;
370 }
371
372
373 static void DoMouse()
374 {
375
376     if (mainmenu || ( (abs(deltah) < 10 * realmultiplier * 1000) && (abs(deltav) < 10 * realmultiplier * 1000) )) {
377         deltah *= usermousesensitivity;
378         deltav *= usermousesensitivity;
379         mousecoordh += deltah;
380         mousecoordv += deltav;
381         if (mousecoordh < 0)
382             mousecoordh = 0;
383         else if (mousecoordh >= kContextWidth)
384             mousecoordh = kContextWidth - 1;
385         if (mousecoordv < 0)
386             mousecoordv = 0;
387         else if (mousecoordv >= kContextHeight)
388             mousecoordv = kContextHeight - 1;
389     }
390
391 }
392
393 void DoFrameRate (int update)
394 {
395     static long frames = 0;
396
397     static AbsoluteTime time = {0, 0};
398     static AbsoluteTime frametime = {0, 0};
399     AbsoluteTime currTime = UpTime ();
400     double deltaTime = (float) AbsoluteDeltaToDuration (currTime, frametime);
401
402     if (0 > deltaTime) // if negative microseconds
403         deltaTime /= -1000000.0;
404     else // else milliseconds
405         deltaTime /= 1000.0;
406
407     multiplier = deltaTime;
408     if (multiplier < .001)
409         multiplier = .001;
410     if (multiplier > 10)
411         multiplier = 10;
412     if (update)
413         frametime = currTime; // reset for next time interval
414
415     deltaTime = (float) AbsoluteDeltaToDuration (currTime, time);
416
417     if (0 > deltaTime) // if negative microseconds
418         deltaTime /= -1000000.0;
419     else // else milliseconds
420         deltaTime /= 1000.0;
421     frames++;
422     if (0.001 <= deltaTime) { // has update interval passed
423         if (update) {
424             time = currTime; // reset for next time interval
425             frames = 0;
426         }
427     }
428 }
429
430
431 void DoUpdate ()
432 {
433     static float sps = 200;
434     static int count;
435     static float oldmult;
436
437     DoFrameRate(1);
438     if (multiplier > .6)
439         multiplier = .6;
440
441     fps = 1 / multiplier;
442
443     count = multiplier * sps;
444     if (count < 2)
445         count = 2;
446
447     realmultiplier = multiplier;
448     multiplier *= gamespeed;
449     if (difficulty == 1)
450         multiplier *= .9;
451     if (difficulty == 0)
452         multiplier *= .8;
453
454     if (loading == 4)
455         multiplier *= .00001;
456     if (slomo && !mainmenu)
457         multiplier *= slomospeed;
458     oldmult = multiplier;
459     multiplier /= (float)count;
460
461     DoMouse();
462
463     TickOnce();
464
465     for (int i = 0; i < count; i++) {
466         Tick();
467     }
468     multiplier = oldmult;
469
470     TickOnceAfter();
471     /* - Debug code to test how many channels were active on average per frame
472         static long frames = 0;
473
474         static AbsoluteTime start = {0,0};
475         AbsoluteTime currTime = UpTime ();
476         static int num_channels = 0;
477
478         num_channels += OPENAL_GetChannelsPlaying();
479         double deltaTime = (float) AbsoluteDeltaToDuration (currTime, start);
480
481         if (0 > deltaTime)  // if negative microseconds
482             deltaTime /= -1000000.0;
483         else                // else milliseconds
484             deltaTime /= 1000.0;
485
486         ++frames;
487
488         if (deltaTime >= 1)
489         {
490             start = currTime;
491             float avg_channels = (float)num_channels / (float)frames;
492
493             ofstream opstream("log.txt",ios::app);
494             opstream << "Average frame count: ";
495             opstream << frames;
496             opstream << " frames - ";
497             opstream << avg_channels;
498             opstream << " per frame.\n";
499             opstream.close();
500
501             frames = 0;
502             num_channels = 0;
503         }
504     */
505     if ( stereomode == stereoNone ) {
506         DrawGLScene(stereoCenter);
507     } else {
508         DrawGLScene(stereoLeft);
509         DrawGLScene(stereoRight);
510     }
511 }
512
513 // --------------------------------------------------------------------------
514
515
516 void CleanUp (void)
517 {
518     LOGFUNC;
519
520     delete[] commandLineOptionsBuffer;
521
522     SDL_Quit();
523 #ifndef __MINGW32__ // FIXME: Temporary workaround for GL-8
524 #define GL_FUNC(ret,fn,params,call,rt) p##fn = NULL;
525 #include "glstubs.h"
526 #undef GL_FUNC
527     // cheat here...static destructors are calling glDeleteTexture() after
528     //  the context is destroyed and libGL unloaded by SDL_Quit().
529     pglDeleteTextures = glDeleteTextures_doNothing;
530 #endif // __MINGW32__
531 }
532
533 // --------------------------------------------------------------------------
534
535 static bool IsFocused()
536 {
537     return ((SDL_GetWindowFlags(sdlwindow) & SDL_WINDOW_INPUT_FOCUS) != 0);
538 }
539
540
541
542 #ifndef WIN32
543 // (code lifted from physfs: http://icculus.org/physfs/ ... zlib license.)
544 static char *findBinaryInPath(const char *bin, char *envr)
545 {
546     size_t alloc_size = 0;
547     char *exe = NULL;
548     char *start = envr;
549     char *ptr;
550
551     do {
552         size_t size;
553         ptr = strchr(start, ':');  /* find next $PATH separator. */
554         if (ptr)
555             *ptr = '\0';
556
557         size = strlen(start) + strlen(bin) + 2;
558         if (size > alloc_size) {
559             char *x = (char *) realloc(exe, size);
560             if (x == NULL) {
561                 if (exe != NULL)
562                     free(exe);
563                 return(NULL);
564             } /* if */
565
566             alloc_size = size;
567             exe = x;
568         } /* if */
569
570         /* build full binary path... */
571         strcpy(exe, start);
572         if ((exe[0] == '\0') || (exe[strlen(exe) - 1] != '/'))
573             strcat(exe, "/");
574         strcat(exe, bin);
575
576         if (access(exe, X_OK) == 0) { /* Exists as executable? We're done. */
577             strcpy(exe, start);  /* i'm lazy. piss off. */
578             return(exe);
579         } /* if */
580
581         start = ptr + 1;  /* start points to beginning of next element. */
582     } while (ptr != NULL);
583
584     if (exe != NULL)
585         free(exe);
586
587     return(NULL);  /* doesn't exist in path. */
588 } /* findBinaryInPath */
589
590
591 char *calcBaseDir(const char *argv0)
592 {
593     /* If there isn't a path on argv0, then look through the $PATH for it. */
594     char *retval;
595     char *envr;
596
597     if (strchr(argv0, '/')) {
598         retval = strdup(argv0);
599         if (retval)
600             *((char *) strrchr(retval, '/')) = '\0';
601         return(retval);
602     }
603
604     envr = getenv("PATH");
605     if (!envr)
606         return NULL;
607     envr = strdup(envr);
608     if (!envr)
609         return NULL;
610     retval = findBinaryInPath(argv0, envr);
611     free(envr);
612     return(retval);
613 }
614
615 static inline void chdirToAppPath(const char *argv0)
616 {
617     char *dir = calcBaseDir(argv0);
618     if (dir) {
619 #if (defined(__APPLE__) && defined(__MACH__))
620         // Chop off /Contents/MacOS if it's at the end of the string, so we
621         //  land in the base of the app bundle.
622         const size_t len = strlen(dir);
623         const char *bundledirs = "/Contents/MacOS";
624         const size_t bundledirslen = strlen(bundledirs);
625         if (len > bundledirslen) {
626             char *ptr = (dir + len) - bundledirslen;
627             if (strcasecmp(ptr, bundledirs) == 0)
628                 *ptr = '\0';
629         }
630 #endif
631         chdir(dir);
632         free(dir);
633     }
634 }
635 #endif
636
637 const option::Descriptor usage[] =
638 {
639     {UNKNOWN,           0,                      "",     "",                 option::Arg::None,  "USAGE: lugaru [options]\n\n"
640                                                                                                 "Options:" },
641     {HELP,              0,                      "h",    "help",             option::Arg::None,  " -h, --help        Print usage and exit." },
642     {FULLSCREEN,        1,                      "f",    "fullscreen",       option::Arg::None,  " -f, --fullscreen  Start the game in fullscreen mode." },
643     {FULLSCREEN,        0,                      "w",    "windowed",         option::Arg::None,  " -w, --windowed    Start the game in windowed mode (default)." },
644     {NOMOUSEGRAB,       1,                      "",     "nomousegrab",      option::Arg::None,  " --nomousegrab     Disable mousegrab." },
645     {NOMOUSEGRAB,       0,                      "",     "mousegrab",        option::Arg::None,  " --mousegrab       Enable mousegrab (default)." },
646     {SOUND,             OPENAL_OUTPUT_NOSOUND,  "",     "nosound",          option::Arg::None,  " --nosound         Disable sound." },
647     {SOUND,             OPENAL_OUTPUT_ALSA,     "",     "force-alsa",       option::Arg::None,  " --force-alsa      Force use of ALSA back-end." },
648     {SOUND,             OPENAL_OUTPUT_OSS,      "",     "force-oss",        option::Arg::None,  " --force-oss       Force use of OSS back-end." },
649     {OPENALINFO,        0,                      "",     "openal-info",      option::Arg::None,  " --openal-info     Print info about OpenAL at launch." },
650     {SHOWRESOLUTIONS,   0,                      "",     "showresolutions",  option::Arg::None,  " --showresolutions List the resolutions found by SDL at launch." },
651     {0,0,0,0,0,0}
652 };
653
654 option::Option commandLineOptions[commandLineOptionsNumber];
655 option::Option* commandLineOptionsBuffer;
656
657 int main(int argc, char **argv)
658 {
659     argc-=(argc>0); argv+=(argc>0); // skip program name argv[0] if present
660     option::Stats  stats(true, usage, argc, argv);
661     if (commandLineOptionsNumber != stats.options_max) {
662         std::cerr << "Found incorrect command line option number" << std::endl;
663         return 1;
664     }
665     commandLineOptionsBuffer = new option::Option[stats.buffer_max];
666     option::Parser parse(true, usage, argc, argv, commandLineOptions, commandLineOptionsBuffer);
667
668     if (parse.error()) {
669         delete[] commandLineOptionsBuffer;
670         return 1;
671     }
672
673     if (commandLineOptions[HELP]) {
674         option::printUsage(std::cout, usage);
675         delete[] commandLineOptionsBuffer;
676         return 0;
677     }
678
679     if (option::Option* opt = commandLineOptions[UNKNOWN]) {
680         std::cerr << "Unknown option: " << opt->name << "\n";
681         option::printUsage(std::cerr, usage);
682         delete[] commandLineOptionsBuffer;
683         return 1;
684     }
685
686     // !!! FIXME: we could use a Win32 API for this.  --ryan.
687 #ifndef WIN32
688     chdirToAppPath(argv[0]);
689 #endif
690
691     LOGFUNC;
692
693     try {
694         {
695             newGame();
696
697             if (!SetUp ()) {
698                 delete[] commandLineOptionsBuffer;
699                 return 42;
700             }
701
702             bool gameDone = false;
703             bool gameFocused = true;
704
705             while (!gameDone && !tryquit) {
706                 if (IsFocused()) {
707                     gameFocused = true;
708
709                     // check windows messages
710
711                     deltah = 0;
712                     deltav = 0;
713                     SDL_Event e;
714                     if (!waiting) {
715                         // message pump
716                         while ( SDL_PollEvent( &e ) ) {
717                             if (!sdlEventProc(e)) {
718                                 gameDone = true;
719                                 break;
720                             }
721                         }
722                     }
723
724                     // game
725                     DoUpdate();
726                 } else {
727                     if (gameFocused) {
728                         // allow game chance to pause
729                         gameFocused = false;
730                         DoUpdate();
731                     }
732
733                     // game is not in focus, give CPU time to other apps by waiting for messages instead of 'peeking'
734                     SDL_WaitEvent(0);
735                 }
736             }
737
738             deleteGame();
739         }
740
741         CleanUp ();
742
743         return 0;
744     } catch (const std::exception& error) {
745         CleanUp();
746
747         std::string e = "Caught exception: ";
748         e += error.what();
749
750         LOG(e);
751
752         MessageBox(g_windowHandle, error.what(), "ERROR", MB_OK | MB_ICONEXCLAMATION);
753
754         return -1;
755     }
756 }