]> git.jsancho.org Git - lugaru.git/blob - Source/OpenGL_Windows.cpp
b0dc01dd60d1dd839ca5bdc8448ee8d07acd643f
[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     LOGFUNC;
276
277     osx = 0;
278     cellophane = 0;
279     texdetail = 4;
280     slomospeed = 0.25;
281     slomofreq = 8012;
282
283     DefaultSettings();
284
285     if (!SDL_WasInit(SDL_INIT_VIDEO))
286         if (SDL_Init(SDL_INIT_VIDEO) == -1) {
287             fprintf(stderr, "SDL_Init() failed: %s\n", SDL_GetError());
288             return false;
289         }
290     if (!LoadSettings()) {
291         fprintf(stderr, "Failed to load config, creating default\n");
292         SaveSettings();
293     }
294     if (kBitsPerPixel != 32 && kBitsPerPixel != 16) {
295         kBitsPerPixel = 16;
296     }
297
298     if (SDL_GL_LoadLibrary(NULL) == -1) {
299         fprintf(stderr, "SDL_GL_LoadLibrary() failed: %s\n", SDL_GetError());
300         SDL_Quit();
301         return false;
302     }
303
304     for (int displayIdx = 0; displayIdx < SDL_GetNumVideoDisplays(); ++displayIdx) {
305         for (int i = 0; i < SDL_GetNumDisplayModes(displayIdx); ++i) {
306             SDL_DisplayMode mode;
307             if (SDL_GetDisplayMode(displayIdx, i, &mode) == -1)
308                 continue;
309             if ((mode.w < 640) || (mode.h < 480))
310                 continue;  // sane lower limit.
311             pair<int,int> resolution(mode.w, mode.h);
312             resolutions.insert(resolution);
313         }
314     }
315
316     if (resolutions.empty()) {
317         const std::string error = "No suitable video resolutions found.";
318         cerr << error << endl;
319         SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Lugaru init failed!", error.c_str(), NULL);
320         SDL_Quit();
321         return false;
322     }
323
324     if (cmdline("showresolutions")) {
325         printf("Available resolutions:\n");
326         for (auto resolution = resolutions.begin(); resolution != resolutions.end(); resolution++) {
327             printf("  %d x %d\n", (int) resolution->first, (int) resolution->second);
328         }
329     }
330
331     SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
332     SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 1);
333
334     Uint32 sdlflags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN;
335     if ((fullscreen || cmdline("fullscreen")) && !cmdline("windowed")) {
336         fullscreen = 1;
337         sdlflags |= SDL_WINDOW_FULLSCREEN;
338     }
339     if (!cmdline("nomousegrab"))
340         sdlflags |= SDL_WINDOW_INPUT_GRABBED;
341
342     sdlwindow = SDL_CreateWindow("Lugaru", SDL_WINDOWPOS_CENTERED_DISPLAY(0), SDL_WINDOWPOS_CENTERED_DISPLAY(0),
343                                  kContextWidth, kContextHeight, sdlflags);
344
345     if (!sdlwindow) {
346         fprintf(stderr, "SDL_CreateWindow() failed: %s\n", SDL_GetError());
347         fprintf(stderr, "forcing 640x480...\n");
348         kContextWidth = 640;
349         kContextHeight = 480;
350         sdlwindow = SDL_CreateWindow("Lugaru", SDL_WINDOWPOS_CENTERED_DISPLAY(0), SDL_WINDOWPOS_CENTERED_DISPLAY(0),
351                                      kContextWidth, kContextHeight, sdlflags);
352         if (!sdlwindow) {
353             fprintf(stderr, "SDL_CreateWindow() failed: %s\n", SDL_GetError());
354             fprintf(stderr, "forcing 640x480 windowed mode...\n");
355             sdlflags &= ~SDL_WINDOW_FULLSCREEN;
356             sdlwindow = SDL_CreateWindow("Lugaru", SDL_WINDOWPOS_CENTERED_DISPLAY(0), SDL_WINDOWPOS_CENTERED_DISPLAY(0),
357                                          kContextWidth, kContextHeight, sdlflags);
358
359             if (!sdlwindow) {
360                 fprintf(stderr, "SDL_CreateWindow() failed: %s\n", SDL_GetError());
361                 return false;
362             }
363         }
364     }
365
366     SDL_GLContext glctx = SDL_GL_CreateContext(sdlwindow);
367     if (!glctx) {
368         fprintf(stderr, "SDL_GL_CreateContext() failed: %s\n", SDL_GetError());
369         SDL_Quit();
370         return false;
371     }
372
373     SDL_GL_MakeCurrent(sdlwindow, glctx);
374
375     if (!lookup_all_glsyms()) {
376         fprintf(stderr, "Missing required OpenGL functions.\n");
377         SDL_Quit();
378         return false;
379     }
380
381     int dblbuf = 0;
382     if ((SDL_GL_GetAttribute(SDL_GL_DOUBLEBUFFER, &dblbuf) == -1) || (!dblbuf))
383     {
384         fprintf(stderr, "Failed to get a double-buffered context.\n");
385         SDL_Quit();
386         return false;
387     }
388
389     if (SDL_GL_SetSwapInterval(-1) == -1)  // try swap_tear first.
390         SDL_GL_SetSwapInterval(1);
391
392     SDL_ShowCursor(0);
393     if (!cmdline("nomousegrab"))
394         SDL_SetRelativeMouseMode(SDL_TRUE);
395
396     initGL();
397
398     GLint width = kContextWidth;
399     GLint height = kContextHeight;
400     gMidPoint.h = width / 2;
401     gMidPoint.v = height / 2;
402     screenwidth = width;
403     screenheight = height;
404
405     newdetail = detail;
406     newscreenwidth = screenwidth;
407     newscreenheight = screenheight;
408
409     /* If saved resolution is not in the list, add it to the list (so that it’s selectable in the options) */
410     pair<int,int> startresolution(width,height);
411     if (resolutions.find(startresolution) == resolutions.end()) {
412         resolutions.insert(startresolution);
413     }
414
415     InitGame();
416
417     return true;
418 }
419
420
421 static void DoMouse()
422 {
423
424     if (mainmenu || ( (abs(deltah) < 10 * realmultiplier * 1000) && (abs(deltav) < 10 * realmultiplier * 1000) )) {
425         deltah *= usermousesensitivity;
426         deltav *= usermousesensitivity;
427         mousecoordh += deltah;
428         mousecoordv += deltav;
429         if (mousecoordh < 0)
430             mousecoordh = 0;
431         else if (mousecoordh >= kContextWidth)
432             mousecoordh = kContextWidth - 1;
433         if (mousecoordv < 0)
434             mousecoordv = 0;
435         else if (mousecoordv >= kContextHeight)
436             mousecoordv = kContextHeight - 1;
437     }
438
439 }
440
441 void DoFrameRate (int update)
442 {
443     static long frames = 0;
444
445     static AbsoluteTime time = {0, 0};
446     static AbsoluteTime frametime = {0, 0};
447     AbsoluteTime currTime = UpTime ();
448     double deltaTime = (float) AbsoluteDeltaToDuration (currTime, frametime);
449
450     if (0 > deltaTime) // if negative microseconds
451         deltaTime /= -1000000.0;
452     else // else milliseconds
453         deltaTime /= 1000.0;
454
455     multiplier = deltaTime;
456     if (multiplier < .001)
457         multiplier = .001;
458     if (multiplier > 10)
459         multiplier = 10;
460     if (update)
461         frametime = currTime; // reset for next time interval
462
463     deltaTime = (float) AbsoluteDeltaToDuration (currTime, time);
464
465     if (0 > deltaTime) // if negative microseconds
466         deltaTime /= -1000000.0;
467     else // else milliseconds
468         deltaTime /= 1000.0;
469     frames++;
470     if (0.001 <= deltaTime) { // has update interval passed
471         if (update) {
472             time = currTime; // reset for next time interval
473             frames = 0;
474         }
475     }
476 }
477
478
479 void DoUpdate ()
480 {
481     static float sps = 200;
482     static int count;
483     static float oldmult;
484
485     DoFrameRate(1);
486     if (multiplier > .6)
487         multiplier = .6;
488
489     fps = 1 / multiplier;
490
491     count = multiplier * sps;
492     if (count < 2)
493         count = 2;
494
495     realmultiplier = multiplier;
496     multiplier *= gamespeed;
497     if (difficulty == 1)
498         multiplier *= .9;
499     if (difficulty == 0)
500         multiplier *= .8;
501
502     if (loading == 4)
503         multiplier *= .00001;
504     if (slomo && !mainmenu)
505         multiplier *= slomospeed;
506     oldmult = multiplier;
507     multiplier /= (float)count;
508
509     DoMouse();
510
511     TickOnce();
512
513     for (int i = 0; i < count; i++) {
514         Tick();
515     }
516     multiplier = oldmult;
517
518     TickOnceAfter();
519     /* - Debug code to test how many channels were active on average per frame
520         static long frames = 0;
521
522         static AbsoluteTime start = {0,0};
523         AbsoluteTime currTime = UpTime ();
524         static int num_channels = 0;
525
526         num_channels += OPENAL_GetChannelsPlaying();
527         double deltaTime = (float) AbsoluteDeltaToDuration (currTime, start);
528
529         if (0 > deltaTime)  // if negative microseconds
530             deltaTime /= -1000000.0;
531         else                // else milliseconds
532             deltaTime /= 1000.0;
533
534         ++frames;
535
536         if (deltaTime >= 1)
537         {
538             start = currTime;
539             float avg_channels = (float)num_channels / (float)frames;
540
541             ofstream opstream("log.txt",ios::app);
542             opstream << "Average frame count: ";
543             opstream << frames;
544             opstream << " frames - ";
545             opstream << avg_channels;
546             opstream << " per frame.\n";
547             opstream.close();
548
549             frames = 0;
550             num_channels = 0;
551         }
552     */
553     if ( stereomode == stereoNone ) {
554         DrawGLScene(stereoCenter);
555     } else {
556         DrawGLScene(stereoLeft);
557         DrawGLScene(stereoRight);
558     }
559 }
560
561 // --------------------------------------------------------------------------
562
563
564 void CleanUp (void)
565 {
566     LOGFUNC;
567
568     SDL_Quit();
569 #ifndef __MINGW32__ // FIXME: Temporary workaround for GL-8
570 #define GL_FUNC(ret,fn,params,call,rt) p##fn = NULL;
571 #include "glstubs.h"
572 #undef GL_FUNC
573     // cheat here...static destructors are calling glDeleteTexture() after
574     //  the context is destroyed and libGL unloaded by SDL_Quit().
575     pglDeleteTextures = glDeleteTextures_doNothing;
576 #endif // __MINGW32__
577
578 }
579
580 // --------------------------------------------------------------------------
581
582 static bool IsFocused()
583 {
584     return ((SDL_GetWindowFlags(sdlwindow) & SDL_WINDOW_INPUT_FOCUS) != 0);
585 }
586
587
588
589 #ifndef WIN32
590 // (code lifted from physfs: http://icculus.org/physfs/ ... zlib license.)
591 static char *findBinaryInPath(const char *bin, char *envr)
592 {
593     size_t alloc_size = 0;
594     char *exe = NULL;
595     char *start = envr;
596     char *ptr;
597
598     do {
599         size_t size;
600         ptr = strchr(start, ':');  /* find next $PATH separator. */
601         if (ptr)
602             *ptr = '\0';
603
604         size = strlen(start) + strlen(bin) + 2;
605         if (size > alloc_size) {
606             char *x = (char *) realloc(exe, size);
607             if (x == NULL) {
608                 if (exe != NULL)
609                     free(exe);
610                 return(NULL);
611             } /* if */
612
613             alloc_size = size;
614             exe = x;
615         } /* if */
616
617         /* build full binary path... */
618         strcpy(exe, start);
619         if ((exe[0] == '\0') || (exe[strlen(exe) - 1] != '/'))
620             strcat(exe, "/");
621         strcat(exe, bin);
622
623         if (access(exe, X_OK) == 0) { /* Exists as executable? We're done. */
624             strcpy(exe, start);  /* i'm lazy. piss off. */
625             return(exe);
626         } /* if */
627
628         start = ptr + 1;  /* start points to beginning of next element. */
629     } while (ptr != NULL);
630
631     if (exe != NULL)
632         free(exe);
633
634     return(NULL);  /* doesn't exist in path. */
635 } /* findBinaryInPath */
636
637
638 char *calcBaseDir(const char *argv0)
639 {
640     /* If there isn't a path on argv0, then look through the $PATH for it. */
641     char *retval;
642     char *envr;
643
644     if (strchr(argv0, '/')) {
645         retval = strdup(argv0);
646         if (retval)
647             *((char *) strrchr(retval, '/')) = '\0';
648         return(retval);
649     }
650
651     envr = getenv("PATH");
652     if (!envr)
653         return NULL;
654     envr = strdup(envr);
655     if (!envr)
656         return NULL;
657     retval = findBinaryInPath(argv0, envr);
658     free(envr);
659     return(retval);
660 }
661
662 static inline void chdirToAppPath(const char *argv0)
663 {
664     char *dir = calcBaseDir(argv0);
665     if (dir) {
666 #if (defined(__APPLE__) && defined(__MACH__))
667         // Chop off /Contents/MacOS if it's at the end of the string, so we
668         //  land in the base of the app bundle.
669         const size_t len = strlen(dir);
670         const char *bundledirs = "/Contents/MacOS";
671         const size_t bundledirslen = strlen(bundledirs);
672         if (len > bundledirslen) {
673             char *ptr = (dir + len) - bundledirslen;
674             if (strcasecmp(ptr, bundledirs) == 0)
675                 *ptr = '\0';
676         }
677 #endif
678         chdir(dir);
679         free(dir);
680     }
681 }
682 #endif
683
684
685 int main(int argc, char **argv)
686 {
687     _argc = argc;
688     _argv = argv;
689
690     // !!! FIXME: we could use a Win32 API for this.  --ryan.
691 #ifndef WIN32
692     chdirToAppPath(argv[0]);
693 #endif
694
695     LOGFUNC;
696
697     try {
698         {
699             newGame();
700
701             //ofstream os("error.txt");
702             //os.close();
703             //ofstream os("log.txt");
704             //os.close();
705
706             if (!SetUp ())
707                 return 42;
708
709             while (!gDone && !tryquit) {
710                 if (IsFocused()) {
711                     gameFocused = true;
712
713                     // check windows messages
714
715                     deltah = 0;
716                     deltav = 0;
717                     SDL_Event e;
718                     if (!waiting) {
719                         // message pump
720                         while ( SDL_PollEvent( &e ) ) {
721                             if (!sdlEventProc(e)) {
722                                 gDone = true;
723                                 break;
724                             }
725                         }
726                     }
727
728                     // game
729                     DoUpdate();
730                 } else {
731                     if (gameFocused) {
732                         // allow game chance to pause
733                         gameFocused = false;
734                         DoUpdate();
735                     }
736
737                     // game is not in focus, give CPU time to other apps by waiting for messages instead of 'peeking'
738                     SDL_WaitEvent(0);
739                 }
740             }
741
742             deleteGame();
743         }
744
745         CleanUp ();
746
747         return 0;
748     } catch (const std::exception& error) {
749         CleanUp();
750
751         std::string e = "Caught exception: ";
752         e += error.what();
753
754         LOG(e);
755
756         MessageBox(g_windowHandle, error.what(), "ERROR", MB_OK | MB_ICONEXCLAMATION);
757     }
758
759     CleanUp();
760
761     return -1;
762 }
763
764
765
766 // --------------------------------------------------------------------------
767
768
769 bool LoadImage(const char * fname, TGAImageRec & tex)
770 {
771     if ( tex.data == NULL )
772         return false;
773     else
774         return load_image(fname, tex);
775 }
776
777 void ScreenShot(const char * fname)
778 {
779
780 }
781
782
783
784 static bool load_image(const char *file_name, TGAImageRec &tex)
785 {
786     const char *ptr = strrchr((char *)file_name, '.');
787     if (ptr) {
788         if (strcasecmp(ptr + 1, "png") == 0)
789             return load_png(file_name, tex);
790         else if (strcasecmp(ptr + 1, "jpg") == 0)
791             return load_jpg(file_name, tex);
792     }
793
794     STUBBED("Unsupported image type");
795     return false;
796 }
797
798
799 struct my_error_mgr {
800     struct jpeg_error_mgr pub; /* "public" fields */
801     jmp_buf setjmp_buffer; /* for return to caller */
802 };
803 typedef struct my_error_mgr * my_error_ptr;
804
805
806 static void my_error_exit(j_common_ptr cinfo)
807 {
808     struct my_error_mgr *err = (struct my_error_mgr *)cinfo->err;
809     longjmp(err->setjmp_buffer, 1);
810 }
811
812 /* stolen from public domain example.c code in libjpg distribution. */
813 static bool load_jpg(const char *file_name, TGAImageRec &tex)
814 {
815     struct jpeg_decompress_struct cinfo;
816     struct my_error_mgr jerr;
817     JSAMPROW buffer[1]; /* Output row buffer */
818     int row_stride; /* physical row width in output buffer */
819     FILE *infile = fopen(file_name, "rb");
820
821     if (infile == NULL)
822         return false;
823
824     cinfo.err = jpeg_std_error(&jerr.pub);
825     jerr.pub.error_exit = my_error_exit;
826     if (setjmp(jerr.setjmp_buffer)) {
827         jpeg_destroy_decompress(&cinfo);
828         fclose(infile);
829         return false;
830     }
831
832     jpeg_create_decompress(&cinfo);
833     jpeg_stdio_src(&cinfo, infile);
834     (void) jpeg_read_header(&cinfo, TRUE);
835
836     cinfo.out_color_space = JCS_RGB;
837     cinfo.quantize_colors = 0;
838     (void) jpeg_calc_output_dimensions(&cinfo);
839     (void) jpeg_start_decompress(&cinfo);
840
841     row_stride = cinfo.output_width * cinfo.output_components;
842     tex.sizeX = cinfo.output_width;
843     tex.sizeY = cinfo.output_height;
844     tex.bpp = 24;
845
846     while (cinfo.output_scanline < cinfo.output_height) {
847         buffer[0] = (JSAMPROW)(char *)tex.data +
848                     ((cinfo.output_height - 1) - cinfo.output_scanline) * row_stride;
849         (void) jpeg_read_scanlines(&cinfo, buffer, 1);
850     }
851
852     (void) jpeg_finish_decompress(&cinfo);
853     jpeg_destroy_decompress(&cinfo);
854     fclose(infile);
855
856     return true;
857 }
858
859
860 /* stolen from public domain example.c code in libpng distribution. */
861 static bool load_png(const char *file_name, TGAImageRec &tex)
862 {
863     bool hasalpha = false;
864     png_structp png_ptr = NULL;
865     png_infop info_ptr = NULL;
866     png_uint_32 width, height;
867     int bit_depth, color_type, interlace_type;
868     bool retval = false;
869     png_byte **row_pointers = NULL;
870     FILE *fp = fopen(file_name, "rb");
871
872     if (fp == NULL) {
873         cerr << file_name << " not found" << endl;
874         return(NULL);
875     }
876
877     png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
878     if (png_ptr == NULL)
879         goto png_done;
880
881     info_ptr = png_create_info_struct(png_ptr);
882     if (info_ptr == NULL)
883         goto png_done;
884
885     if (setjmp(png_jmpbuf(png_ptr)))
886         goto png_done;
887
888     png_init_io(png_ptr, fp);
889     png_read_png(png_ptr, info_ptr,
890                  PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_PACKING,
891                  NULL);
892     png_get_IHDR(png_ptr, info_ptr, &width, &height,
893                  &bit_depth, &color_type, &interlace_type, NULL, NULL);
894
895     if (bit_depth != 8)  // transform SHOULD handle this...
896         goto png_done;
897
898     if (color_type & PNG_COLOR_MASK_PALETTE)  // !!! FIXME?
899         goto png_done;
900
901     if ((color_type & PNG_COLOR_MASK_COLOR) == 0)  // !!! FIXME?
902         goto png_done;
903
904     hasalpha = ((color_type & PNG_COLOR_MASK_ALPHA) != 0);
905     row_pointers = png_get_rows(png_ptr, info_ptr);
906     if (!row_pointers)
907         goto png_done;
908
909     if (!hasalpha) {
910         png_byte *dst = tex.data;
911         for (int i = height - 1; i >= 0; i--) {
912             png_byte *src = row_pointers[i];
913             for (unsigned j = 0; j < width; j++) {
914                 dst[0] = src[0];
915                 dst[1] = src[1];
916                 dst[2] = src[2];
917                 dst[3] = 0xFF;
918                 src += 3;
919                 dst += 4;
920             }
921         }
922     }
923
924     else {
925         png_byte *dst = tex.data;
926         int pitch = width * 4;
927         for (int i = height - 1; i >= 0; i--, dst += pitch)
928             memcpy(dst, row_pointers[i], pitch);
929     }
930
931     tex.sizeX = width;
932     tex.sizeY = height;
933     tex.bpp = 32;
934     retval = true;
935
936 png_done:
937     if (!retval) {
938         cerr << "There was a problem loading " << file_name << endl;
939     }
940     png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
941     if (fp)
942         fclose(fp);
943     return (retval);
944 }
945
946
947 bool save_image(const char *file_name)
948 {
949     const char *ptr = strrchr((char *)file_name, '.');
950     if (ptr) {
951         if (strcasecmp(ptr + 1, "png") == 0)
952             return save_png(file_name);
953     }
954
955     STUBBED("Unsupported image type");
956     return false;
957 }
958
959
960 static bool save_png(const char *file_name)
961 {
962     FILE *fp = NULL;
963     png_structp png_ptr = NULL;
964     png_infop info_ptr = NULL;
965     bool retval = false;
966
967     fp = fopen(file_name, "wb");
968     if (fp == NULL)
969         return false;
970
971     png_bytep *row_pointers = new png_bytep[kContextHeight];
972     png_bytep screenshot = new png_byte[kContextWidth * kContextHeight * 3];
973     if ((!screenshot) || (!row_pointers))
974         goto save_png_done;
975
976     glGetError();
977     glReadPixels(0, 0, kContextWidth, kContextHeight,
978                  GL_RGB, GL_UNSIGNED_BYTE, screenshot);
979     if (glGetError() != GL_NO_ERROR)
980         goto save_png_done;
981
982     for (int i = 0; i < kContextHeight; i++)
983         row_pointers[i] = screenshot + ((kContextWidth * ((kContextHeight - 1) - i)) * 3);
984
985     png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
986     if (png_ptr == NULL)
987         goto save_png_done;
988
989     info_ptr = png_create_info_struct(png_ptr);
990     if (info_ptr == NULL)
991         goto save_png_done;
992
993     if (setjmp(png_jmpbuf(png_ptr)))
994         goto save_png_done;
995
996     png_init_io(png_ptr, fp);
997
998     if (setjmp(png_jmpbuf(png_ptr)))
999         goto save_png_done;
1000
1001     png_set_IHDR(png_ptr, info_ptr, kContextWidth, kContextHeight,
1002                  8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
1003                  PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
1004
1005     png_write_info(png_ptr, info_ptr);
1006
1007     if (setjmp(png_jmpbuf(png_ptr)))
1008         goto save_png_done;
1009
1010     png_write_image(png_ptr, row_pointers);
1011
1012     if (setjmp(png_jmpbuf(png_ptr)))
1013         goto save_png_done;
1014
1015     png_write_end(png_ptr, NULL);
1016     retval = true;
1017
1018 save_png_done:
1019     png_destroy_write_struct(&png_ptr, &info_ptr);
1020     delete[] screenshot;
1021     delete[] row_pointers;
1022     if (fp)
1023         fclose(fp);
1024     if (!retval)
1025         unlink(ConvertFileName(file_name));
1026     return retval;
1027 }
1028
1029
1030