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