]> git.jsancho.org Git - lugaru.git/blob - Source/OpenGL_Windows.cpp
b12cf8b74ebf7d50af9fc1d06f5cea0992ed5682
[lugaru.git] / Source / OpenGL_Windows.cpp
1 /*
2 Copyright (C) 2003, 2010 - Wolfire Games
3
4 This file is part of Lugaru.
5
6 Lugaru is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
15 See the GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20 */
21
22
23 #ifdef WIN32
24 #include <windows.h>
25 #endif
26
27 #include "Game.h"
28
29 #include <jpeglib.h>
30 #include <png.h>
31 #include <zlib.h>
32
33 using namespace Game;
34
35 static bool load_image(const char * fname, TGAImageRec & tex);
36 static bool load_png(const char * fname, TGAImageRec & tex);
37 static bool load_jpg(const char * fname, TGAImageRec & tex);
38 bool save_image(const char * fname);
39 static bool save_png(const char * fname);
40
41
42 #include "openal_wrapper.h"
43
44 extern float multiplier;
45 extern float sps;
46 extern float realmultiplier;
47 extern int slomo;
48 extern bool cellophane;
49 extern float texdetail;
50
51 extern bool osx;
52 extern bool freeze;
53 extern bool stillloading;
54 extern int mainmenu;
55 /*extern*/
56 bool gameFocused;
57
58 extern float slomospeed;
59 extern float slomofreq;
60
61
62
63 #include <math.h>
64 #include <stdio.h>
65 #include <string.h>
66 #include <fstream>
67 #include <iostream>
68 #include "gamegl.h"
69 #include "MacCompatibility.h"
70 #include "Settings.h"
71
72 #ifdef WIN32
73 #include <shellapi.h>
74 #include "win-res/resource.h"
75 #endif
76
77 extern SDL_Window *sdlwindow;
78
79 using namespace std;
80
81 SDL_Rect **resolutions = NULL;
82
83 Boolean SetUp ();
84 void DoUpdate ();
85
86 void CleanUp (void);
87
88 // statics/globals (internal only) ------------------------------------------
89
90 #ifdef _MSC_VER
91 #pragma warning(push)
92 #pragma warning(disable: 4273)
93 #endif
94
95 #define GL_FUNC(ret,fn,params,call,rt) \
96     extern "C" { \
97         static ret (GLAPIENTRY *p##fn) params = NULL; \
98         ret GLAPIENTRY fn params { rt p##fn call; } \
99     }
100 #include "glstubs.h"
101 #undef GL_FUNC
102
103 #ifdef _MSC_VER
104 #pragma warning(pop)
105 #endif
106
107 static bool lookup_glsym(const char *funcname, void **func)
108 {
109     *func = SDL_GL_GetProcAddress(funcname);
110     if (*func == NULL) {
111         fprintf(stderr, "Failed to find OpenGL symbol \"%s\"\n", funcname);
112         return false;
113     }
114     return true;
115 }
116
117 static bool lookup_all_glsyms(void)
118 {
119     bool retval = true;
120 #define GL_FUNC(ret,fn,params,call,rt) \
121         if (!lookup_glsym(#fn, (void **) &p##fn)) retval = false;
122 #include "glstubs.h"
123 #undef GL_FUNC
124     return retval;
125 }
126
127 static void GLAPIENTRY glDeleteTextures_doNothing(GLsizei n, const GLuint *textures)
128 {
129     // no-op.
130 }
131
132 #ifdef MessageBox
133 #undef MessageBox
134 #endif
135 #define MessageBox(hwnd,text,title,flags) STUBBED("msgbox")
136
137 // Menu defs
138
139 int kContextWidth;
140 int kContextHeight;
141
142 Boolean gDone = false;
143
144 static int _argc = 0;
145 static char **_argv = NULL;
146
147 bool cmdline(const char *cmd)
148 {
149     for (int i = 1; i < _argc; i++) {
150         char *arg = _argv[i];
151         while (*arg == '-')
152             arg++;
153         if (strcasecmp(arg, cmd) == 0)
154             return true;
155     }
156
157     return false;
158 }
159
160 //-----------------------------------------------------------------------------------------------------------------------
161
162 // OpenGL Drawing
163
164 void initGL()
165 {
166     glClear( GL_COLOR_BUFFER_BIT );
167     swap_gl_buffers();
168
169     // clear all states
170     glDisable( GL_ALPHA_TEST);
171     glDisable( GL_BLEND);
172     glDisable( GL_DEPTH_TEST);
173     //glDisable( GL_DITHER);
174     glDisable( GL_FOG);
175     glDisable( GL_LIGHTING);
176     glDisable( GL_LOGIC_OP);
177     glDisable( GL_TEXTURE_1D);
178     glDisable( GL_TEXTURE_2D);
179     glPixelTransferi( GL_MAP_COLOR, GL_FALSE);
180     glPixelTransferi( GL_RED_SCALE, 1);
181     glPixelTransferi( GL_RED_BIAS, 0);
182     glPixelTransferi( GL_GREEN_SCALE, 1);
183     glPixelTransferi( GL_GREEN_BIAS, 0);
184     glPixelTransferi( GL_BLUE_SCALE, 1);
185     glPixelTransferi( GL_BLUE_BIAS, 0);
186     glPixelTransferi( GL_ALPHA_SCALE, 1);
187     glPixelTransferi( GL_ALPHA_BIAS, 0);
188
189     // set initial rendering states
190     glShadeModel( GL_SMOOTH);
191     glClearDepth( 1.0f);
192     glDepthFunc( GL_LEQUAL);
193     glDepthMask( GL_TRUE);
194     //glDepthRange( FRONT_CLIP, BACK_CLIP);
195     glEnable( GL_DEPTH_TEST);
196     glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
197     glCullFace( GL_FRONT);
198     glEnable( GL_CULL_FACE);
199     glEnable( GL_LIGHTING);
200     //glEnable( GL_LIGHT_MODEL_AMBIENT);
201     glEnable( GL_DITHER);
202     glEnable( GL_COLOR_MATERIAL);
203     glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
204     glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
205     glAlphaFunc( GL_GREATER, 0.5f);
206
207     if ( CanInitStereo(stereomode) ) {
208         InitStereo(stereomode);
209     } else {
210         fprintf(stderr, "Failed to initialize stereo, disabling.\n");
211         stereomode = stereoNone;
212     }
213 }
214
215 static void toggleFullscreen()
216 {
217     Uint32 flags = SDL_GetWindowFlags(sdlwindow);
218     if (flags & SDL_WINDOW_FULLSCREEN) {
219         flags &= ~SDL_WINDOW_FULLSCREEN;
220     } else {
221         flags |= SDL_WINDOW_FULLSCREEN;
222     }
223     SDL_SetWindowFullscreen(sdlwindow, flags);
224 }
225
226 static SDL_bool sdlEventProc(const SDL_Event &e)
227 {
228     switch (e.type) {
229         case SDL_QUIT:
230             return SDL_FALSE;
231
232         case SDL_WINDOWEVENT:
233             if (e.window.event == SDL_WINDOWEVENT_CLOSE) {
234                 return SDL_FALSE;
235             }
236         break;
237
238         case SDL_MOUSEMOTION:
239             deltah += e.motion.xrel;
240             deltav += e.motion.yrel;
241         break;
242
243         case SDL_KEYDOWN:
244             if ((e.key.keysym.scancode == SDL_SCANCODE_G) &&
245                 (e.key.keysym.mod & KMOD_CTRL)) {
246                 SDL_bool mode = SDL_TRUE;
247                 if ((SDL_GetWindowFlags(sdlwindow) & SDL_WINDOW_FULLSCREEN) == 0)
248                     mode = (SDL_GetWindowGrab(sdlwindow) ? SDL_FALSE : SDL_TRUE);
249                 SDL_SetWindowGrab(sdlwindow, mode);
250                 SDL_SetRelativeMouseMode(mode);
251             } else if ( (e.key.keysym.scancode == SDL_SCANCODE_RETURN) && (e.key.keysym.mod & KMOD_ALT) ) {
252                 toggleFullscreen();
253             }
254         break;
255     }
256     return SDL_TRUE;
257 }
258
259
260
261 // --------------------------------------------------------------------------
262
263 static Point gMidPoint;
264
265 Boolean SetUp ()
266 {
267     char string[10];
268
269     LOGFUNC;
270
271     osx = 0;
272     cellophane = 0;
273     texdetail = 4;
274     slomospeed = 0.25;
275     slomofreq = 8012;
276
277     DefaultSettings();
278
279     const int displayIdx = 0;  // !!! FIXME: other monitors?
280
281     if (!SDL_WasInit(SDL_INIT_VIDEO))
282         if (SDL_Init(SDL_INIT_VIDEO) == -1) {
283             fprintf(stderr, "SDL_Init() failed: %s\n", SDL_GetError());
284             return false;
285         }
286     if (!LoadSettings()) {
287         fprintf(stderr, "Failed to load config, creating default\n");
288         SaveSettings();
289     }
290     if (kBitsPerPixel != 32 && kBitsPerPixel != 16) {
291         kBitsPerPixel = 16;
292     }
293
294     if (SDL_GL_LoadLibrary(NULL) == -1) {
295         fprintf(stderr, "SDL_GL_LoadLibrary() failed: %s\n", SDL_GetError());
296         SDL_Quit();
297         return false;
298     }
299
300     int count = 0;
301     const int nummodes = SDL_GetNumDisplayModes(displayIdx);
302     for (int i = 0; i < nummodes; i++)
303     {
304         SDL_DisplayMode mode;
305         if (SDL_GetDisplayMode(displayIdx, i, &mode) == -1)
306             continue;
307         if ((mode.w < 640) || (mode.h < 480))
308             continue;  // sane lower limit.
309         count++;
310     }
311
312     if (count == 0) {
313         const std::string error = "No suitable video resolutions found.";
314         cerr << error << endl;
315         SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Lugaru init failed!", error.c_str(), NULL);
316         SDL_Quit();
317         return false;
318     }
319
320     static SDL_Rect *resolutions_block = NULL;
321     resolutions_block = (SDL_Rect*) realloc(resolutions_block, sizeof (SDL_Rect) * count);
322     resolutions = (SDL_Rect**) realloc(resolutions, sizeof (SDL_Rect *) * (count + 1));
323     if ((resolutions_block == NULL) || (resolutions == NULL)) {
324         SDL_Quit();
325         fprintf(stderr, "Out of memory!\n");
326         return false;
327     }
328
329     resolutions[count--] = NULL;
330     for (int i = 0; count >= 0; i++, count--) {
331         /* FIXME - Pretty sure this should use nummodes and not count */
332         SDL_DisplayMode mode;
333         if (SDL_GetDisplayMode(displayIdx, i, &mode) == -1)
334             continue;
335         if ((mode.w < 640) || (mode.h < 480))
336             continue;  // sane lower limit.
337         resolutions_block[count].x = resolutions_block[count].y = 0;
338         resolutions_block[count].w = mode.w;
339         resolutions_block[count].h = mode.h;
340         resolutions[count] = &resolutions_block[count];
341     }
342
343     if (cmdline("showresolutions")) {
344         printf("Resolutions we think are okay:\n");
345         for (int i = 0; resolutions[i]; i++)
346             printf("  %d x %d\n", (int) resolutions[i]->w, (int) resolutions[i]->h);
347     }
348
349     SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
350     SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 1);
351
352     Uint32 sdlflags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN;
353     if (!cmdline("windowed"))
354         sdlflags |= SDL_WINDOW_FULLSCREEN;
355     if (!cmdline("nomousegrab"))
356         sdlflags |= SDL_WINDOW_INPUT_GRABBED;
357
358     sdlwindow = SDL_CreateWindow("Lugaru", SDL_WINDOWPOS_CENTERED_DISPLAY(displayIdx), SDL_WINDOWPOS_CENTERED_DISPLAY(displayIdx),
359                                  kContextWidth, kContextHeight, sdlflags);
360
361     if (!sdlwindow) {
362         fprintf(stderr, "SDL_CreateWindow() failed: %s\n", SDL_GetError());
363         fprintf(stderr, "forcing 640x480...\n");
364         kContextWidth = 640;
365         kContextHeight = 480;
366         sdlwindow = SDL_CreateWindow("Lugaru", SDL_WINDOWPOS_CENTERED_DISPLAY(displayIdx), SDL_WINDOWPOS_CENTERED_DISPLAY(displayIdx),
367                                      kContextWidth, kContextHeight, sdlflags);
368         if (!sdlwindow) {
369             fprintf(stderr, "SDL_CreateWindow() failed: %s\n", SDL_GetError());
370             fprintf(stderr, "forcing 640x480 windowed mode...\n");
371             sdlflags &= ~SDL_WINDOW_FULLSCREEN;
372             sdlwindow = SDL_CreateWindow("Lugaru", SDL_WINDOWPOS_CENTERED_DISPLAY(displayIdx), SDL_WINDOWPOS_CENTERED_DISPLAY(displayIdx),
373                                          kContextWidth, kContextHeight, sdlflags);
374
375             if (!sdlwindow) {
376                 fprintf(stderr, "SDL_CreateWindow() failed: %s\n", SDL_GetError());
377                 return false;
378             }
379         }
380     }
381
382     SDL_GLContext glctx = SDL_GL_CreateContext(sdlwindow);
383     if (!glctx) {
384         fprintf(stderr, "SDL_GL_CreateContext() failed: %s\n", SDL_GetError());
385         SDL_Quit();
386         return false;
387     }
388
389     SDL_GL_MakeCurrent(sdlwindow, glctx);
390
391     if (!lookup_all_glsyms()) {
392         fprintf(stderr, "Missing required OpenGL functions.\n");
393         SDL_Quit();
394         return false;
395     }
396
397     int dblbuf = 0;
398     if ((SDL_GL_GetAttribute(SDL_GL_DOUBLEBUFFER, &dblbuf) == -1) || (!dblbuf))
399     {
400         fprintf(stderr, "Failed to get a double-buffered context.\n");
401         SDL_Quit();
402         return false;
403     }
404
405     if (SDL_GL_SetSwapInterval(-1) == -1)  // try swap_tear first.
406         SDL_GL_SetSwapInterval(1);
407
408     SDL_ShowCursor(0);
409     SDL_SetWindowGrab(sdlwindow, SDL_TRUE);
410     SDL_SetRelativeMouseMode(SDL_TRUE);
411
412
413     initGL();
414
415     GLint width = kContextWidth;
416     GLint height = kContextHeight;
417     gMidPoint.h = width / 2;
418     gMidPoint.v = height / 2;
419     screenwidth = width;
420     screenheight = height;
421
422     newdetail = detail;
423     newscreenwidth = screenwidth;
424     newscreenheight = screenheight;
425
426     InitGame();
427
428     return true;
429 }
430
431
432 static void DoMouse()
433 {
434
435     if (mainmenu || ( (abs(deltah) < 10 * realmultiplier * 1000) && (abs(deltav) < 10 * realmultiplier * 1000) )) {
436         deltah *= usermousesensitivity;
437         deltav *= usermousesensitivity;
438         mousecoordh += deltah;
439         mousecoordv += deltav;
440         if (mousecoordh < 0)
441             mousecoordh = 0;
442         else if (mousecoordh >= kContextWidth)
443             mousecoordh = kContextWidth - 1;
444         if (mousecoordv < 0)
445             mousecoordv = 0;
446         else if (mousecoordv >= kContextHeight)
447             mousecoordv = kContextHeight - 1;
448     }
449
450 }
451
452 void DoFrameRate (int update)
453 {
454     static long frames = 0;
455
456     static AbsoluteTime time = {0, 0};
457     static AbsoluteTime frametime = {0, 0};
458     AbsoluteTime currTime = UpTime ();
459     double deltaTime = (float) AbsoluteDeltaToDuration (currTime, frametime);
460
461     if (0 > deltaTime) // if negative microseconds
462         deltaTime /= -1000000.0;
463     else // else milliseconds
464         deltaTime /= 1000.0;
465
466     multiplier = deltaTime;
467     if (multiplier < .001)
468         multiplier = .001;
469     if (multiplier > 10)
470         multiplier = 10;
471     if (update)
472         frametime = currTime; // reset for next time interval
473
474     deltaTime = (float) AbsoluteDeltaToDuration (currTime, time);
475
476     if (0 > deltaTime) // if negative microseconds
477         deltaTime /= -1000000.0;
478     else // else milliseconds
479         deltaTime /= 1000.0;
480     frames++;
481     if (0.001 <= deltaTime) { // has update interval passed
482         if (update) {
483             time = currTime; // reset for next time interval
484             frames = 0;
485         }
486     }
487 }
488
489
490 void DoUpdate ()
491 {
492     static float sps = 200;
493     static int count;
494     static float oldmult;
495
496     DoFrameRate(1);
497     if (multiplier > .6)
498         multiplier = .6;
499
500     fps = 1 / multiplier;
501
502     count = multiplier * sps;
503     if (count < 2)
504         count = 2;
505
506     realmultiplier = multiplier;
507     multiplier *= gamespeed;
508     if (difficulty == 1)
509         multiplier *= .9;
510     if (difficulty == 0)
511         multiplier *= .8;
512
513     if (loading == 4)
514         multiplier *= .00001;
515     if (slomo && !mainmenu)
516         multiplier *= slomospeed;
517     oldmult = multiplier;
518     multiplier /= (float)count;
519
520     DoMouse();
521
522     TickOnce();
523
524     for (int i = 0; i < count; i++) {
525         Tick();
526     }
527     multiplier = oldmult;
528
529     TickOnceAfter();
530     /* - Debug code to test how many channels were active on average per frame
531         static long frames = 0;
532
533         static AbsoluteTime start = {0,0};
534         AbsoluteTime currTime = UpTime ();
535         static int num_channels = 0;
536
537         num_channels += OPENAL_GetChannelsPlaying();
538         double deltaTime = (float) AbsoluteDeltaToDuration (currTime, start);
539
540         if (0 > deltaTime)  // if negative microseconds
541             deltaTime /= -1000000.0;
542         else                // else milliseconds
543             deltaTime /= 1000.0;
544
545         ++frames;
546
547         if (deltaTime >= 1)
548         {
549             start = currTime;
550             float avg_channels = (float)num_channels / (float)frames;
551
552             ofstream opstream("log.txt",ios::app);
553             opstream << "Average frame count: ";
554             opstream << frames;
555             opstream << " frames - ";
556             opstream << avg_channels;
557             opstream << " per frame.\n";
558             opstream.close();
559
560             frames = 0;
561             num_channels = 0;
562         }
563     */
564     if ( stereomode == stereoNone ) {
565         DrawGLScene(stereoCenter);
566     } else {
567         DrawGLScene(stereoLeft);
568         DrawGLScene(stereoRight);
569     }
570 }
571
572 // --------------------------------------------------------------------------
573
574
575 void CleanUp (void)
576 {
577     LOGFUNC;
578
579     SDL_Quit();
580 #define GL_FUNC(ret,fn,params,call,rt) p##fn = NULL;
581 #include "glstubs.h"
582 #undef GL_FUNC
583     // cheat here...static destructors are calling glDeleteTexture() after
584     //  the context is destroyed and libGL unloaded by SDL_Quit().
585     pglDeleteTextures = glDeleteTextures_doNothing;
586
587 }
588
589 // --------------------------------------------------------------------------
590
591 static bool IsFocused()
592 {
593     return ((SDL_GetWindowFlags(sdlwindow) & SDL_WINDOW_INPUT_FOCUS) != 0);
594 }
595
596
597
598 #ifndef WIN32
599 // (code lifted from physfs: http://icculus.org/physfs/ ... zlib license.)
600 static char *findBinaryInPath(const char *bin, char *envr)
601 {
602     size_t alloc_size = 0;
603     char *exe = NULL;
604     char *start = envr;
605     char *ptr;
606
607     do {
608         size_t size;
609         ptr = strchr(start, ':');  /* find next $PATH separator. */
610         if (ptr)
611             *ptr = '\0';
612
613         size = strlen(start) + strlen(bin) + 2;
614         if (size > alloc_size) {
615             char *x = (char *) realloc(exe, size);
616             if (x == NULL) {
617                 if (exe != NULL)
618                     free(exe);
619                 return(NULL);
620             } /* if */
621
622             alloc_size = size;
623             exe = x;
624         } /* if */
625
626         /* build full binary path... */
627         strcpy(exe, start);
628         if ((exe[0] == '\0') || (exe[strlen(exe) - 1] != '/'))
629             strcat(exe, "/");
630         strcat(exe, bin);
631
632         if (access(exe, X_OK) == 0) { /* Exists as executable? We're done. */
633             strcpy(exe, start);  /* i'm lazy. piss off. */
634             return(exe);
635         } /* if */
636
637         start = ptr + 1;  /* start points to beginning of next element. */
638     } while (ptr != NULL);
639
640     if (exe != NULL)
641         free(exe);
642
643     return(NULL);  /* doesn't exist in path. */
644 } /* findBinaryInPath */
645
646
647 char *calcBaseDir(const char *argv0)
648 {
649     /* If there isn't a path on argv0, then look through the $PATH for it. */
650     char *retval;
651     char *envr;
652
653     const char *ptr = strrchr((char *)argv0, '/');
654     if (strchr(argv0, '/')) {
655         retval = strdup(argv0);
656         if (retval)
657             *((char *) strrchr(retval, '/')) = '\0';
658         return(retval);
659     }
660
661     envr = getenv("PATH");
662     if (!envr)
663         return NULL;
664     envr = strdup(envr);
665     if (!envr)
666         return NULL;
667     retval = findBinaryInPath(argv0, envr);
668     free(envr);
669     return(retval);
670 }
671
672 static inline void chdirToAppPath(const char *argv0)
673 {
674     char *dir = calcBaseDir(argv0);
675     if (dir) {
676 #if (defined(__APPLE__) && defined(__MACH__))
677         // Chop off /Contents/MacOS if it's at the end of the string, so we
678         //  land in the base of the app bundle.
679         const size_t len = strlen(dir);
680         const char *bundledirs = "/Contents/MacOS";
681         const size_t bundledirslen = strlen(bundledirs);
682         if (len > bundledirslen) {
683             char *ptr = (dir + len) - bundledirslen;
684             if (strcasecmp(ptr, bundledirs) == 0)
685                 *ptr = '\0';
686         }
687 #endif
688         chdir(dir);
689         free(dir);
690     }
691 }
692 #endif
693
694
695 int main(int argc, char **argv)
696 {
697     _argc = argc;
698     _argv = argv;
699
700     // !!! FIXME: we could use a Win32 API for this.  --ryan.
701 #ifndef WIN32
702     chdirToAppPath(argv[0]);
703 #endif
704
705     LOGFUNC;
706
707     try {
708         {
709             newGame();
710
711             //ofstream os("error.txt");
712             //os.close();
713             //ofstream os("log.txt");
714             //os.close();
715
716             if (!SetUp ())
717                 return 42;
718
719             while (!gDone && !tryquit) {
720                 if (IsFocused()) {
721                     gameFocused = true;
722
723                     // check windows messages
724
725                     deltah = 0;
726                     deltav = 0;
727                     SDL_Event e;
728                     if (!waiting) {
729                         // message pump
730                         while ( SDL_PollEvent( &e ) ) {
731                             if (!sdlEventProc(e)) {
732                                 gDone = true;
733                                 break;
734                             }
735                         }
736                     }
737
738                     // game
739                     DoUpdate();
740                 } else {
741                     if (gameFocused) {
742                         // allow game chance to pause
743                         gameFocused = false;
744                         DoUpdate();
745                     }
746
747                     // game is not in focus, give CPU time to other apps by waiting for messages instead of 'peeking'
748                     SDL_WaitEvent(0);
749                 }
750             }
751
752             deleteGame();
753         }
754
755         CleanUp ();
756
757         return 0;
758     } catch (const std::exception& error) {
759         CleanUp();
760
761         std::string e = "Caught exception: ";
762         e += error.what();
763
764         LOG(e);
765
766         MessageBox(g_windowHandle, error.what(), "ERROR", MB_OK | MB_ICONEXCLAMATION);
767     }
768
769     CleanUp();
770
771     return -1;
772 }
773
774
775
776 // --------------------------------------------------------------------------
777
778
779 bool LoadImage(const char * fname, TGAImageRec & tex)
780 {
781     if ( tex.data == NULL )
782         return false;
783     else
784         return load_image(fname, tex);
785 }
786
787 void ScreenShot(const char * fname)
788 {
789
790 }
791
792
793
794 static bool load_image(const char *file_name, TGAImageRec &tex)
795 {
796     const char *ptr = strrchr((char *)file_name, '.');
797     if (ptr) {
798         if (strcasecmp(ptr + 1, "png") == 0)
799             return load_png(file_name, tex);
800         else if (strcasecmp(ptr + 1, "jpg") == 0)
801             return load_jpg(file_name, tex);
802     }
803
804     STUBBED("Unsupported image type");
805     return false;
806 }
807
808
809 struct my_error_mgr {
810     struct jpeg_error_mgr pub; /* "public" fields */
811     jmp_buf setjmp_buffer; /* for return to caller */
812 };
813 typedef struct my_error_mgr * my_error_ptr;
814
815
816 static void my_error_exit(j_common_ptr cinfo)
817 {
818     struct my_error_mgr *err = (struct my_error_mgr *)cinfo->err;
819     longjmp(err->setjmp_buffer, 1);
820 }
821
822 /* stolen from public domain example.c code in libjpg distribution. */
823 static bool load_jpg(const char *file_name, TGAImageRec &tex)
824 {
825     struct jpeg_decompress_struct cinfo;
826     struct my_error_mgr jerr;
827     JSAMPROW buffer[1]; /* Output row buffer */
828     int row_stride; /* physical row width in output buffer */
829     FILE *infile = fopen(file_name, "rb");
830
831     if (infile == NULL)
832         return false;
833
834     cinfo.err = jpeg_std_error(&jerr.pub);
835     jerr.pub.error_exit = my_error_exit;
836     if (setjmp(jerr.setjmp_buffer)) {
837         jpeg_destroy_decompress(&cinfo);
838         fclose(infile);
839         return false;
840     }
841
842     jpeg_create_decompress(&cinfo);
843     jpeg_stdio_src(&cinfo, infile);
844     (void) jpeg_read_header(&cinfo, TRUE);
845
846     cinfo.out_color_space = JCS_RGB;
847     cinfo.quantize_colors = 0;
848     (void) jpeg_calc_output_dimensions(&cinfo);
849     (void) jpeg_start_decompress(&cinfo);
850
851     row_stride = cinfo.output_width * cinfo.output_components;
852     tex.sizeX = cinfo.output_width;
853     tex.sizeY = cinfo.output_height;
854     tex.bpp = 24;
855
856     while (cinfo.output_scanline < cinfo.output_height) {
857         buffer[0] = (JSAMPROW)(char *)tex.data +
858                     ((cinfo.output_height - 1) - cinfo.output_scanline) * row_stride;
859         (void) jpeg_read_scanlines(&cinfo, buffer, 1);
860     }
861
862     (void) jpeg_finish_decompress(&cinfo);
863     jpeg_destroy_decompress(&cinfo);
864     fclose(infile);
865
866     return true;
867 }
868
869
870 /* stolen from public domain example.c code in libpng distribution. */
871 static bool load_png(const char *file_name, TGAImageRec &tex)
872 {
873     bool hasalpha = false;
874     png_structp png_ptr = NULL;
875     png_infop info_ptr = NULL;
876     png_uint_32 width, height;
877     int bit_depth, color_type, interlace_type;
878     png_byte **rows = NULL;
879     bool retval = false;
880     png_byte **row_pointers = NULL;
881     FILE *fp = fopen(file_name, "rb");
882
883     if (fp == NULL) {
884         cerr << file_name << " not found" << endl;
885         return(NULL);
886     }
887
888     png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
889     if (png_ptr == NULL)
890         goto png_done;
891
892     info_ptr = png_create_info_struct(png_ptr);
893     if (info_ptr == NULL)
894         goto png_done;
895
896     if (setjmp(png_jmpbuf(png_ptr)))
897         goto png_done;
898
899     png_init_io(png_ptr, fp);
900     png_read_png(png_ptr, info_ptr,
901                  PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_PACKING,
902                  NULL);
903     png_get_IHDR(png_ptr, info_ptr, &width, &height,
904                  &bit_depth, &color_type, &interlace_type, NULL, NULL);
905
906     if (bit_depth != 8)  // transform SHOULD handle this...
907         goto png_done;
908
909     if (color_type & PNG_COLOR_MASK_PALETTE)  // !!! FIXME?
910         goto png_done;
911
912     if ((color_type & PNG_COLOR_MASK_COLOR) == 0)  // !!! FIXME?
913         goto png_done;
914
915     hasalpha = ((color_type & PNG_COLOR_MASK_ALPHA) != 0);
916     row_pointers = png_get_rows(png_ptr, info_ptr);
917     if (!row_pointers)
918         goto png_done;
919
920     if (!hasalpha) {
921         png_byte *dst = tex.data;
922         for (int i = height - 1; i >= 0; i--) {
923             png_byte *src = row_pointers[i];
924             for (int j = 0; j < width; j++) {
925                 dst[0] = src[0];
926                 dst[1] = src[1];
927                 dst[2] = src[2];
928                 dst[3] = 0xFF;
929                 src += 3;
930                 dst += 4;
931             }
932         }
933     }
934
935     else {
936         png_byte *dst = tex.data;
937         int pitch = width * 4;
938         for (int i = height - 1; i >= 0; i--, dst += pitch)
939             memcpy(dst, row_pointers[i], pitch);
940     }
941
942     tex.sizeX = width;
943     tex.sizeY = height;
944     tex.bpp = 32;
945     retval = true;
946
947 png_done:
948     if (!retval) {
949         cerr << "There was a problem loading " << file_name << endl;
950     }
951     png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
952     if (fp)
953         fclose(fp);
954     return (retval);
955 }
956
957
958 bool save_image(const char *file_name)
959 {
960     const char *ptr = strrchr((char *)file_name, '.');
961     if (ptr) {
962         if (strcasecmp(ptr + 1, "png") == 0)
963             return save_png(file_name);
964     }
965
966     STUBBED("Unsupported image type");
967     return false;
968 }
969
970
971 static bool save_png(const char *file_name)
972 {
973     FILE *fp = NULL;
974     png_structp png_ptr = NULL;
975     png_infop info_ptr = NULL;
976     bool retval = false;
977
978     fp = fopen(file_name, "wb");
979     if (fp == NULL)
980         return false;
981
982     png_bytep *row_pointers = new png_bytep[kContextHeight];
983     png_bytep screenshot = new png_byte[kContextWidth * kContextHeight * 3];
984     if ((!screenshot) || (!row_pointers))
985         goto save_png_done;
986
987     glGetError();
988     glReadPixels(0, 0, kContextWidth, kContextHeight,
989                  GL_RGB, GL_UNSIGNED_BYTE, screenshot);
990     if (glGetError() != GL_NO_ERROR)
991         goto save_png_done;
992
993     for (int i = 0; i < kContextHeight; i++)
994         row_pointers[i] = screenshot + ((kContextWidth * ((kContextHeight - 1) - i)) * 3);
995
996     png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
997     if (png_ptr == NULL)
998         goto save_png_done;
999
1000     info_ptr = png_create_info_struct(png_ptr);
1001     if (info_ptr == NULL)
1002         goto save_png_done;
1003
1004     if (setjmp(png_jmpbuf(png_ptr)))
1005         goto save_png_done;
1006
1007     png_init_io(png_ptr, fp);
1008
1009     if (setjmp(png_jmpbuf(png_ptr)))
1010         goto save_png_done;
1011
1012     png_set_IHDR(png_ptr, info_ptr, kContextWidth, kContextHeight,
1013                  8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
1014                  PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
1015
1016     png_write_info(png_ptr, info_ptr);
1017
1018     if (setjmp(png_jmpbuf(png_ptr)))
1019         goto save_png_done;
1020
1021     png_write_image(png_ptr, row_pointers);
1022
1023     if (setjmp(png_jmpbuf(png_ptr)))
1024         goto save_png_done;
1025
1026     png_write_end(png_ptr, NULL);
1027     retval = true;
1028
1029 save_png_done:
1030     png_destroy_write_struct(&png_ptr, &info_ptr);
1031     delete[] screenshot;
1032     delete[] row_pointers;
1033     if (fp)
1034         fclose(fp);
1035     if (!retval)
1036         unlink(ConvertFileName(file_name));
1037     return retval;
1038 }
1039
1040
1041