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