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