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