]> git.jsancho.org Git - lugaru.git/blob - Source/OpenGL_Windows.cpp
HUGE refactoring:
[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 static 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 // MODIFIED GWC
70 //extern int terraindetail;
71 //extern int texdetail;
72 extern float terraindetail;
73 extern float texdetail;
74
75 extern bool osx;
76 extern int numplayers;
77 extern bool freeze;
78 extern Person player[maxplayers];
79 extern bool stillloading;
80 extern int mainmenu;
81 /*extern*/ bool gameFocused;
82
83 extern float slomospeed;
84 extern float slomofreq;
85
86
87
88 #include <math.h>
89 #include <stdio.h>
90 #include <string.h>
91 #include <fstream>
92 #include <iostream>
93 #include "gamegl.h"
94 #include "MacCompatibility.h"
95 #include "Settings.h"
96
97 #ifdef WIN32
98 #include <shellapi.h>
99 #include "win-res/resource.h"
100 #endif
101
102 using namespace std;
103
104
105
106 SDL_Rect **resolutions = NULL;
107 static SDL_Rect rect_1024_768 = { 0, 0, 1024, 768 };
108 static SDL_Rect rect_800_600  = { 0, 0, 800,  600 };
109 static SDL_Rect rect_640_480  = { 0, 0, 640,  480 };
110 static SDL_Rect *hardcoded_resolutions[] = {
111     &rect_1024_768,
112     &rect_800_600,
113     &rect_640_480,
114     NULL
115 };
116
117
118
119 unsigned int resolutionDepths[8][2] = {0};
120
121 int closestResolution(int width, int height);
122 int resolutionID(int width, int height);
123
124 void ReportError (char * strError);
125
126 void DrawGL(Game & game);
127
128 void CreateGLWindow (void);
129 Boolean SetUp (Game & game);
130 void DoUpdate (Game & game);
131
132 void DoEvent (void);
133 void CleanUp (void);
134
135
136 // statics/globals (internal only) ------------------------------------------
137 #ifndef WIN32
138 typedef struct tagPOINT { 
139   int x;
140   int y;
141 } POINT, *PPOINT; 
142 #endif
143
144
145
146 #ifdef _MSC_VER
147 #pragma warning(push)
148 #pragma warning(disable: 4273)
149 #endif
150
151 #define GL_FUNC(ret,fn,params,call,rt) \
152     extern "C" { \
153         static ret (GLAPIENTRY *p##fn) params = NULL; \
154         ret GLAPIENTRY fn params { rt p##fn call; } \
155     }
156 #include "glstubs.h"
157 #undef GL_FUNC
158
159 #ifdef _MSC_VER
160 #pragma warning(pop)
161 #endif
162
163 static bool lookup_glsym(const char *funcname, void **func)
164 {
165     *func = SDL_GL_GetProcAddress(funcname);
166     if (*func == NULL)
167     {
168         fprintf(stderr, "Failed to find OpenGL symbol \"%s\"\n", funcname);
169         return false;
170     }
171     return true;
172 }
173
174 static bool lookup_all_glsyms(void)
175 {
176     bool retval = true;
177     #define GL_FUNC(ret,fn,params,call,rt) \
178         if (!lookup_glsym(#fn, (void **) &p##fn)) retval = false;
179     #include "glstubs.h"
180     #undef GL_FUNC
181     return retval;
182 }
183
184 static void GLAPIENTRY glDeleteTextures_doNothing(GLsizei n, const GLuint *textures)
185 {
186     // no-op.
187 }
188
189
190
191 void sdlGetCursorPos(POINT *pt)
192 {
193     SDL_GetMouseState(&(pt->x), &(pt->y));
194 }
195 #define GetCursorPos(x) sdlGetCursorPos(x)
196 #define SetCursorPos(x, y) SDL_WarpMouse(x, y)
197 #define ScreenToClient(x, pt)
198 #define ClientToScreen(x, pt)
199 #ifdef MessageBox
200 #undef MessageBox
201 #endif
202 #define MessageBox(hwnd,text,title,flags) STUBBED("msgbox")
203
204
205 Point delta;
206
207 static bool g_button, fullscreen = true;
208
209
210 // Menu defs
211 enum 
212 {
213         kFileQuit = 1
214 };
215
216 enum 
217 {
218         kForegroundSleep = 10,
219         kBackgroundSleep = 10000
220 };
221
222
223 int kContextWidth;
224 int kContextHeight;
225
226 const RGBColor rgbBlack = { 0x0000, 0x0000, 0x0000 };
227
228 GLuint gFontList;
229 char gcstrMode [256] = "";
230
231 UInt32 gSleepTime = kForegroundSleep;
232 Boolean gDone = false, gfFrontProcess = true;
233
234 Game * pgame = 0;
235
236 #ifndef __MINGW32__
237 static int _argc = 0;
238 static char **_argv = NULL;
239 #endif
240
241 bool cmdline(const char *cmd)
242 {
243     for (int i = 1; i < _argc; i++)
244     {
245         char *arg = _argv[i];
246         while (*arg == '-')
247             arg++;
248         if (strcasecmp(arg, cmd) == 0)
249             return true;
250     }
251
252     return false;
253 }
254
255
256 // --------------------------------------------------------------------------
257
258 void ReportError (char * strError)
259 {
260 #ifdef _MSC_VER  // !!! FIXME.  --ryan.
261         throw std::exception( strError);
262 #endif
263
264         /*      char errMsgCStr [256];
265         Str255 strErr;
266
267         sprintf (errMsgCStr, "%s", strError); 
268
269         // out as debug string
270         CToPStr (strErr, errMsgCStr);
271         DebugStr (strErr);
272         */
273 }
274
275 //-----------------------------------------------------------------------------------------------------------------------
276
277 // OpenGL Drawing
278
279 void DrawGL (Game & game)
280 {
281         if ( stereomode == stereoNone ) {
282                 game.DrawGLScene(stereoCenter);
283         } else {
284                 game.DrawGLScene(stereoLeft);
285                 game.DrawGLScene(stereoRight);
286         }
287 }
288
289
290 /*static KeyMap g_theKeys;
291
292 void SetKey( int key)
293 {
294     g_theKeys[ key >> 3] |= (1 << (key & 7));
295 }
296
297 void ClearKey( int key)
298 {
299     g_theKeys[ key >> 3] &= (0xff ^ (1 << (key & 7)));
300 }
301
302 void GetKeys(  unsigned char theKeys[16])
303 {
304     memcpy( theKeys, &g_theKeys, 16);
305 }*/
306
307 Boolean Button()
308 {
309     return SDL_GetMouseState(NULL,NULL)&SDL_BUTTON(SDL_BUTTON_LEFT);
310 }
311
312 static inline int clamp_sdl_mouse_button(Uint8 button)
313 {
314     if (button == 2)   // right mouse button is button 3 in SDL.
315         button = 3;
316     else if (button == 3)
317         button = 2;
318
319     if ((button >= 1) && (button <= 3))
320         return button - 1;
321     return -1;
322 }
323
324 static void sdlEventProc(const SDL_Event &e, Game &game)
325 {
326     int val;
327     SDLMod mod;
328
329     switch(e.type)
330         {
331         case SDL_MOUSEMOTION:
332             game.deltah += e.motion.xrel;
333             game.deltav += e.motion.yrel;
334             return;
335
336         case SDL_KEYDOWN:
337             if ((e.key.keysym.sym == SDLK_g) &&
338                                 (e.key.keysym.mod & KMOD_CTRL) &&
339                                 !(SDL_GetVideoSurface()->flags & SDL_FULLSCREEN) ) {
340                                 SDL_WM_GrabInput( ((SDL_WM_GrabInput(SDL_GRAB_QUERY)==SDL_GRAB_ON) ? SDL_GRAB_OFF:SDL_GRAB_ON) );
341                         } else if ( (e.key.keysym.sym == SDLK_RETURN) && (e.key.keysym.mod & KMOD_ALT) ) {
342                                 SDL_WM_ToggleFullScreen(SDL_GetVideoSurface());
343             }
344             return;
345
346         case SDL_KEYUP:
347             return;
348     }
349 }
350
351
352 // --------------------------------------------------------------------------
353
354 static Point gMidPoint;
355
356 Boolean SetUp (Game & game)
357 {
358         char string[10];
359
360         LOGFUNC;
361
362         randSeed = UpTime().lo;
363
364         osx = 0;
365         cellophane=0;
366         texdetail=4;
367         terraindetail=2;
368         slomospeed=0.25;
369         slomofreq=8012;
370         numplayers=1;
371         
372         DefaultSettings(game);
373
374     if (!SDL_WasInit(SDL_INIT_VIDEO))
375         if (SDL_Init(SDL_INIT_VIDEO) == -1)
376         {
377             fprintf(stderr, "SDL_Init() failed: %s\n", SDL_GetError());
378             return false;
379         }
380         if(!LoadSettings(game)) {
381                 fprintf(stderr, "Failed to load config, creating default\n");
382                 SaveSettings(game);
383         }
384         if(kBitsPerPixel!=32&&kBitsPerPixel!=16){
385                 kBitsPerPixel=16;
386         }
387
388         if (SDL_GL_LoadLibrary(NULL) == -1)
389         {
390                 fprintf(stderr, "SDL_GL_LoadLibrary() failed: %s\n", SDL_GetError());
391                 SDL_Quit();
392                 return false;
393         }
394
395         SDL_Rect **res = SDL_ListModes(NULL, SDL_FULLSCREEN|SDL_OPENGL);
396         if ( (res == NULL) || (res == ((SDL_Rect **)-1)) || (res[0] == NULL) || (res[0]->w < 640) || (res[0]->h < 480) )
397                 res = hardcoded_resolutions;
398
399         // reverse list (it was sorted biggest to smallest by SDL)...
400         int count;
401         for (count = 0; res[count]; count++)
402         {
403                 if ((res[count]->w < 640) || (res[count]->h < 480))
404                         break;   // sane lower limit.
405         }
406
407         static SDL_Rect *resolutions_block = NULL;
408         resolutions_block = (SDL_Rect*) realloc(resolutions_block, sizeof (SDL_Rect) * count);
409         resolutions = (SDL_Rect**) realloc(resolutions, sizeof (SDL_Rect *) * (count + 1));
410         if ((resolutions_block == NULL) || (resolutions == NULL))
411         {
412                 SDL_Quit();
413                 fprintf(stderr, "Out of memory!\n");
414                 return false;
415         }
416
417         resolutions[count--] = NULL;
418         for (int i = 0; count >= 0; i++, count--)
419         {
420                 memcpy(&resolutions_block[count], res[i], sizeof (SDL_Rect));
421                 resolutions[count] = &resolutions_block[count];
422         }
423
424         if (cmdline("showresolutions"))
425         {
426                 printf("Resolutions we think are okay:\n");
427                 for (int i = 0; resolutions[i]; i++)
428                         printf("  %d x %d\n", (int) resolutions[i]->w, (int) resolutions[i]->h);
429         }
430
431     Uint32 sdlflags = SDL_OPENGL;
432     if (!cmdline("windowed"))
433         sdlflags |= SDL_FULLSCREEN;
434
435     SDL_WM_SetCaption("Lugaru", "Lugaru");
436
437     SDL_ShowCursor(0);
438
439     SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
440     SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 1);
441     
442     if (SDL_SetVideoMode(kContextWidth, kContextHeight, 0, sdlflags) == NULL)
443     {
444         fprintf(stderr, "SDL_SetVideoMode() failed: %s\n", SDL_GetError());
445         fprintf(stderr, "forcing 640x480...\n");
446         kContextWidth = 640;
447         kContextHeight = 480;
448         if (SDL_SetVideoMode(kContextWidth, kContextHeight, 0, sdlflags) == NULL)
449         {
450             fprintf(stderr, "SDL_SetVideoMode() failed: %s\n", SDL_GetError());
451             fprintf(stderr, "forcing 640x480 windowed mode...\n");
452             sdlflags &= ~SDL_FULLSCREEN;
453             if (SDL_SetVideoMode(kContextWidth, kContextHeight, 0, sdlflags) == NULL)
454             {
455                 fprintf(stderr, "SDL_SetVideoMode() failed: %s\n", SDL_GetError());
456                 return false;
457             }
458         }
459     }
460
461     int dblbuf = 0;
462     if ((SDL_GL_GetAttribute(SDL_GL_DOUBLEBUFFER, &dblbuf) == -1) || (!dblbuf))
463     {
464         fprintf(stderr, "Failed to get double buffered GL context!\n");
465         SDL_Quit();
466         return false;
467     }
468
469     if (!lookup_all_glsyms())
470     {
471         SDL_Quit();
472         return false;
473     }
474
475     if (!cmdline("nomousegrab"))
476         SDL_WM_GrabInput(SDL_GRAB_ON);
477
478
479         glClear( GL_COLOR_BUFFER_BIT );
480         swap_gl_buffers();
481
482         // clear all states
483         glDisable( GL_ALPHA_TEST);
484         glDisable( GL_BLEND);
485         glDisable( GL_DEPTH_TEST);
486         //      glDisable( GL_DITHER);
487         glDisable( GL_FOG);
488         glDisable( GL_LIGHTING);
489         glDisable( GL_LOGIC_OP);
490         glDisable( GL_TEXTURE_1D);
491         glDisable( GL_TEXTURE_2D);
492         glPixelTransferi( GL_MAP_COLOR, GL_FALSE);
493         glPixelTransferi( GL_RED_SCALE, 1);
494         glPixelTransferi( GL_RED_BIAS, 0);
495         glPixelTransferi( GL_GREEN_SCALE, 1);
496         glPixelTransferi( GL_GREEN_BIAS, 0);
497         glPixelTransferi( GL_BLUE_SCALE, 1);
498         glPixelTransferi( GL_BLUE_BIAS, 0);
499         glPixelTransferi( GL_ALPHA_SCALE, 1);
500         glPixelTransferi( GL_ALPHA_BIAS, 0);
501
502         // set initial rendering states
503         glShadeModel( GL_SMOOTH);
504         glClearDepth( 1.0f);
505         glDepthFunc( GL_LEQUAL);
506         glDepthMask( GL_TRUE);
507         //      glDepthRange( FRONT_CLIP, BACK_CLIP);
508         glEnable( GL_DEPTH_TEST);
509         glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
510         glCullFace( GL_FRONT);
511         glEnable( GL_CULL_FACE);
512         glEnable( GL_LIGHTING);
513 //      glEnable( GL_LIGHT_MODEL_AMBIENT);
514         glEnable( GL_DITHER);
515         glEnable( GL_COLOR_MATERIAL);
516         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
517         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
518         glAlphaFunc( GL_GREATER, 0.5f);
519
520         GLint width = kContextWidth;
521         GLint height = kContextHeight;
522         gMidPoint.h = width / 2;
523         gMidPoint.v = height / 2;
524         screenwidth=width;
525         screenheight=height;
526
527         game.newdetail=detail;
528         game.newscreenwidth=screenwidth;
529         game.newscreenheight=screenheight;
530
531         if ( CanInitStereo(stereomode) ) {
532                 InitStereo(stereomode);
533         } else {
534                 fprintf(stderr, "Failed to initialize stereo, disabling.\n");
535                 stereomode = stereoNone;
536         }
537
538         game.InitGame();
539
540         return true;
541 }
542
543
544 static void DoMouse(Game & game)
545 {
546
547         if(mainmenu||(abs(game.deltah)<10*realmultiplier*1000&&abs(game.deltav)<10*realmultiplier*1000))
548         {
549                 game.deltah *= usermousesensitivity;
550                 game.deltav *= usermousesensitivity;
551                 game.mousecoordh += game.deltah;
552                 game.mousecoordv += game.deltav;
553         if (game.mousecoordh < 0)
554             game.mousecoordh = 0;
555         else if (game.mousecoordh >= kContextWidth)
556             game.mousecoordh = kContextWidth - 1;
557         if (game.mousecoordv < 0)
558             game.mousecoordv = 0;
559         else if (game.mousecoordv >= kContextHeight)
560             game.mousecoordv = kContextHeight - 1;
561         }
562
563 }
564
565 void DoFrameRate (int update)
566 {       
567         static long frames = 0;
568
569         static AbsoluteTime time = {0,0};
570         static AbsoluteTime frametime = {0,0};
571         AbsoluteTime currTime = UpTime ();
572         double deltaTime = (float) AbsoluteDeltaToDuration (currTime, frametime);
573
574         if (0 > deltaTime)      // if negative microseconds
575                 deltaTime /= -1000000.0;
576         else                            // else milliseconds
577                 deltaTime /= 1000.0;
578
579         multiplier=deltaTime;
580         if(multiplier<.001) multiplier=.001;
581         if(multiplier>10) multiplier=10;
582         if(update) frametime = currTime;        // reset for next time interval
583
584         deltaTime = (float) AbsoluteDeltaToDuration (currTime, time);
585
586         if (0 > deltaTime)      // if negative microseconds
587                 deltaTime /= -1000000.0;
588         else                            // else milliseconds
589                 deltaTime /= 1000.0;
590         frames++;
591         if (0.001 <= deltaTime) // has update interval passed
592         {
593                 if(update){
594                         time = currTime;        // reset for next time interval
595                         frames = 0;
596                 }
597         }
598 }
599
600
601 void DoUpdate (Game & game)
602 {
603         static float sps=200;
604         static int count;
605         static float oldmult;
606
607         DoFrameRate(1);
608         if(multiplier>.6)multiplier=.6;
609
610         game.fps=1/multiplier;
611
612         count = multiplier*sps;
613         if(count<2)count=2;
614         //if(count>10)count=10;
615
616         realmultiplier=multiplier;
617         multiplier*=gamespeed;
618         if(difficulty==1)multiplier*=.9;
619         if(difficulty==0)multiplier*=.8;
620
621         if(game.loading==4)multiplier*=.00001;
622         //multiplier*.9;
623         if(slomo&&!mainmenu)multiplier*=slomospeed;
624         //if(freeze)multiplier*=0.00001;
625         oldmult=multiplier;
626         multiplier/=(float)count;
627
628         DoMouse(game);
629
630         game.TickOnce();
631
632         for(int i=0;i<count;i++)
633         {
634                 game.Tick();
635         }
636         multiplier=oldmult;
637
638         game.TickOnceAfter();
639 /* - Debug code to test how many channels were active on average per frame
640         static long frames = 0;
641
642         static AbsoluteTime start = {0,0};
643         AbsoluteTime currTime = UpTime ();
644         static int num_channels = 0;
645         
646         num_channels += OPENAL_GetChannelsPlaying();
647         double deltaTime = (float) AbsoluteDeltaToDuration (currTime, start);
648
649         if (0 > deltaTime)      // if negative microseconds
650                 deltaTime /= -1000000.0;
651         else                            // else milliseconds
652                 deltaTime /= 1000.0;
653
654         ++frames;
655
656         if (deltaTime >= 1)
657         {
658                 start = currTime;
659                 float avg_channels = (float)num_channels / (float)frames;
660
661                 ofstream opstream("log.txt",ios::app); 
662                 opstream << "Average frame count: ";
663                 opstream << frames;
664                 opstream << " frames - ";
665                 opstream << avg_channels;
666                 opstream << " per frame.\n";
667                 opstream.close();
668
669                 frames = 0;
670                 num_channels = 0;
671         }
672 */
673         DrawGL (game);
674 }
675
676 // --------------------------------------------------------------------------
677
678
679 void CleanUp (void)
680 {
681         LOGFUNC;
682
683 //      game.Dispose();
684
685
686
687
688     SDL_Quit();
689     #define GL_FUNC(ret,fn,params,call,rt) p##fn = NULL;
690     #include "glstubs.h"
691     #undef GL_FUNC
692     // cheat here...static destructors are calling glDeleteTexture() after
693     //  the context is destroyed and libGL unloaded by SDL_Quit().
694     pglDeleteTextures = glDeleteTextures_doNothing;
695
696 }
697
698 // --------------------------------------------------------------------------
699
700 static bool IsFocused()
701 {
702     return ((SDL_GetAppState() & SDL_APPINPUTFOCUS) != 0);
703 }
704
705
706 static void launch_web_browser(const char *url)
707 {
708 #ifdef WIN32
709     ShellExecute(NULL, "open", url, NULL, NULL, SW_SHOWNORMAL);
710
711 #elif (defined(__APPLE__) && defined(__MACH__))
712     const char *fmt = "open '%s'";
713     const size_t len = strlen(fmt) + strlen(url) + 16;
714     char *buf = new char[len];
715     snprintf(buf, len, fmt, url);
716     system(buf);
717     delete[] buf;
718
719 #elif PLATFORM_LINUX
720     const char *fmt = "PATH=$PATH:. xdg-open '%s'";
721     const size_t len = strlen(fmt) + strlen(url) + 16;
722     char *buf = new char[len];
723     snprintf(buf, len, fmt, url);
724     system(buf);
725     delete[] buf;
726 #endif
727 }
728
729
730 #ifndef WIN32
731 // (code lifted from physfs: http://icculus.org/physfs/ ... zlib license.)
732 static char *findBinaryInPath(const char *bin, char *envr)
733 {
734     size_t alloc_size = 0;
735     char *exe = NULL;
736     char *start = envr;
737     char *ptr;
738
739     do
740     {
741         size_t size;
742         ptr = strchr(start, ':');  /* find next $PATH separator. */
743         if (ptr)
744             *ptr = '\0';
745
746         size = strlen(start) + strlen(bin) + 2;
747         if (size > alloc_size)
748         {
749             char *x = (char *) realloc(exe, size);
750             if (x == NULL)
751             {
752                 if (exe != NULL)
753                     free(exe);
754                 return(NULL);
755             } /* if */
756
757             alloc_size = size;
758             exe = x;
759         } /* if */
760
761         /* build full binary path... */
762         strcpy(exe, start);
763         if ((exe[0] == '\0') || (exe[strlen(exe) - 1] != '/'))
764             strcat(exe, "/");
765         strcat(exe, bin);
766
767         if (access(exe, X_OK) == 0)  /* Exists as executable? We're done. */
768         {
769             strcpy(exe, start);  /* i'm lazy. piss off. */
770             return(exe);
771         } /* if */
772
773         start = ptr + 1;  /* start points to beginning of next element. */
774     } while (ptr != NULL);
775
776     if (exe != NULL)
777         free(exe);
778
779     return(NULL);  /* doesn't exist in path. */
780 } /* findBinaryInPath */
781
782
783 char *calcBaseDir(const char *argv0)
784 {
785     /* If there isn't a path on argv0, then look through the $PATH for it. */
786     char *retval;
787     char *envr;
788
789     const char *ptr = strrchr((char *)argv0, '/');
790     if (strchr(argv0, '/'))
791     {
792         retval = strdup(argv0);
793         if (retval)
794             *((char *) strrchr(retval, '/')) = '\0';
795         return(retval);
796     }
797
798     envr = getenv("PATH");
799     if (!envr) return NULL;
800     envr = strdup(envr);
801     if (!envr) return NULL;
802     retval = findBinaryInPath(argv0, envr);
803     free(envr);
804     return(retval);
805 }
806
807 static inline void chdirToAppPath(const char *argv0)
808 {
809     char *dir = calcBaseDir(argv0);
810     if (dir)
811     {
812         #if (defined(__APPLE__) && defined(__MACH__))
813         // Chop off /Contents/MacOS if it's at the end of the string, so we
814         //  land in the base of the app bundle.
815         const size_t len = strlen(dir);
816         const char *bundledirs = "/Contents/MacOS";
817         const size_t bundledirslen = strlen(bundledirs);
818         if (len > bundledirslen)
819         {
820             char *ptr = (dir + len) - bundledirslen;
821             if (strcasecmp(ptr, bundledirs) == 0)
822                 *ptr = '\0';
823         }
824         #endif
825         chdir(dir);
826         free(dir);
827     }
828 }
829 #endif
830
831
832 int main(int argc, char **argv)
833 {
834 #ifndef __MINGW32__
835     _argc = argc;
836     _argv = argv;
837 #endif
838
839     // !!! FIXME: we could use a Win32 API for this.  --ryan.
840 #ifndef WIN32
841     chdirToAppPath(argv[0]);
842 #endif
843
844         LOGFUNC;
845
846         //memset( &g_theKeys, 0, sizeof( KeyMap));
847
848     //initSDLKeyTable();
849
850         try
851         {
852                 bool regnow = false;
853                 {
854                         Game game;
855                         pgame = &game;
856
857                         //ofstream os("error.txt");
858                         //os.close();
859                         //ofstream os("log.txt");
860                         //os.close();
861
862                         if (!SetUp (game))
863                 return 42;
864
865                         while (!gDone&&!game.quit&&(!game.tryquit))
866                         {
867                                 if (IsFocused())
868                                 {
869                                         gameFocused = true;
870
871                                         // check windows messages
872                         
873                                         game.deltah = 0;
874                                         game.deltav = 0;
875                                         SDL_Event e;
876                                         if(!game.isWaiting()) {
877                                                 // message pump
878                                                 while( SDL_PollEvent( &e ) )
879                                                 {
880                                                         if( e.type == SDL_QUIT )
881                                                         {
882                                                                 gDone=true;
883                                                                 break;
884                                                         }
885                                                         sdlEventProc(e, game);
886                                                 }
887                                         }
888
889                                         // game
890                                         DoUpdate(game);
891                                 }
892                                 else
893                                 {
894                                         if (gameFocused)
895                                         {
896                                                 // allow game chance to pause
897                                                 gameFocused = false;
898                                                 DoUpdate(game);
899                                         }
900
901                                         // game is not in focus, give CPU time to other apps by waiting for messages instead of 'peeking'
902                     STUBBED("give up CPU but sniff the event queue");
903                                 }
904                         }
905
906                         regnow = game.registernow;
907                 }
908                 pgame = 0;
909
910                 CleanUp ();
911                 
912                 return 0;
913         }
914         catch (const std::exception& error)
915         {
916                 CleanUp();
917
918                 std::string e = "Caught exception: ";
919                 e += error.what();
920
921                 LOG(e);
922
923                 MessageBox(g_windowHandle, error.what(), "ERROR", MB_OK | MB_ICONEXCLAMATION);
924         }
925
926         CleanUp();
927
928         return -1;
929 }
930
931
932
933         // --------------------------------------------------------------------------
934
935         extern int channels[100];
936         extern OPENAL_SAMPLE * samp[100];
937         extern OPENAL_STREAM * strm[20];
938
939         extern "C" void PlaySoundEx(int chan, OPENAL_SAMPLE *sptr, OPENAL_DSPUNIT *dsp, signed char startpaused)
940         {
941                 const OPENAL_SAMPLE * currSample = OPENAL_GetCurrentSample(channels[chan]);
942                 if (currSample && currSample == samp[chan])
943                 {
944                         if (OPENAL_GetPaused(channels[chan]))
945                         {
946                                 OPENAL_StopSound(channels[chan]);
947                                 channels[chan] = OPENAL_FREE;
948                         }
949                         else if (OPENAL_IsPlaying(channels[chan]))
950                         {
951                                 int loop_mode = OPENAL_GetLoopMode(channels[chan]);
952                                 if (loop_mode & OPENAL_LOOP_OFF)
953                                 {
954                                         channels[chan] = OPENAL_FREE;
955                                 }
956                         }
957                 }
958                 else
959                 {
960                         channels[chan] = OPENAL_FREE;
961                 }
962
963                 channels[chan] = OPENAL_PlaySoundEx(channels[chan], sptr, dsp, startpaused);
964                 if (channels[chan] < 0)
965                 {
966                         channels[chan] = OPENAL_PlaySoundEx(OPENAL_FREE, sptr, dsp, startpaused);
967                 }
968         }
969
970         extern "C" void PlayStreamEx(int chan, OPENAL_STREAM *sptr, OPENAL_DSPUNIT *dsp, signed char startpaused)
971         {
972                 const OPENAL_SAMPLE * currSample = OPENAL_GetCurrentSample(channels[chan]);
973                 if (currSample && currSample == OPENAL_Stream_GetSample(sptr))
974                 {
975                                 OPENAL_StopSound(channels[chan]);
976                                 OPENAL_Stream_Stop(sptr);
977                 }
978                 else
979                 {
980                         OPENAL_Stream_Stop(sptr);
981                         channels[chan] = OPENAL_FREE;
982                 }
983
984                 channels[chan] = OPENAL_Stream_PlayEx(channels[chan], sptr, dsp, startpaused);
985                 if (channels[chan] < 0)
986                 {
987                         channels[chan] = OPENAL_Stream_PlayEx(OPENAL_FREE, sptr, dsp, startpaused);
988                 }
989         }
990
991
992         bool LoadImage(const char * fname, TGAImageRec & tex)
993         {
994                 if ( tex.data == NULL )
995                         return false;
996                 else
997                         return load_image(fname, tex);
998         }
999
1000         void ScreenShot(const char * fname)
1001         {
1002         save_image(fname);
1003         }
1004
1005
1006
1007 static bool load_image(const char *file_name, TGAImageRec &tex)
1008 {
1009     const char *ptr = strrchr((char *)file_name, '.');
1010     if (ptr)
1011     {
1012         if (strcasecmp(ptr+1, "png") == 0)
1013             return load_png(file_name, tex);
1014         else if (strcasecmp(ptr+1, "jpg") == 0)
1015             return load_jpg(file_name, tex);
1016     }
1017
1018     STUBBED("Unsupported image type");
1019     return false;
1020 }
1021
1022
1023 struct my_error_mgr {
1024   struct jpeg_error_mgr pub;    /* "public" fields */
1025   jmp_buf setjmp_buffer;        /* for return to caller */
1026 };
1027 typedef struct my_error_mgr * my_error_ptr;
1028
1029
1030 static void my_error_exit(j_common_ptr cinfo)
1031 {
1032         struct my_error_mgr *err = (struct my_error_mgr *)cinfo->err;
1033         longjmp(err->setjmp_buffer, 1);
1034 }
1035
1036 /* stolen from public domain example.c code in libjpg distribution. */
1037 static bool load_jpg(const char *file_name, TGAImageRec &tex)
1038 {
1039     struct jpeg_decompress_struct cinfo;
1040     struct my_error_mgr jerr;
1041     JSAMPROW buffer[1];         /* Output row buffer */
1042     int row_stride;             /* physical row width in output buffer */
1043     FILE *infile = fopen(file_name, "rb");
1044
1045     if (infile == NULL)
1046         return false;
1047
1048     cinfo.err = jpeg_std_error(&jerr.pub);
1049     jerr.pub.error_exit = my_error_exit;
1050     if (setjmp(jerr.setjmp_buffer)) {
1051         jpeg_destroy_decompress(&cinfo);
1052         fclose(infile);
1053         return false;
1054     }
1055
1056     jpeg_create_decompress(&cinfo);
1057     jpeg_stdio_src(&cinfo, infile);
1058     (void) jpeg_read_header(&cinfo, TRUE);
1059
1060     cinfo.out_color_space = JCS_RGB;
1061     cinfo.quantize_colors = 0;
1062     (void) jpeg_calc_output_dimensions(&cinfo);
1063     (void) jpeg_start_decompress(&cinfo);
1064
1065     row_stride = cinfo.output_width * cinfo.output_components;
1066     tex.sizeX = cinfo.output_width;
1067     tex.sizeY = cinfo.output_height;
1068     tex.bpp = 24;
1069
1070     while (cinfo.output_scanline < cinfo.output_height) {
1071         buffer[0] = (JSAMPROW)(char *)tex.data +
1072                         ((cinfo.output_height-1) - cinfo.output_scanline) * row_stride;
1073         (void) jpeg_read_scanlines(&cinfo, buffer, 1);
1074     }
1075
1076     (void) jpeg_finish_decompress(&cinfo);
1077     jpeg_destroy_decompress(&cinfo);
1078     fclose(infile);
1079
1080     return true;
1081 }
1082
1083
1084 /* stolen from public domain example.c code in libpng distribution. */
1085 static bool load_png(const char *file_name, TGAImageRec &tex)
1086 {
1087     bool hasalpha = false;
1088     png_structp png_ptr = NULL;
1089     png_infop info_ptr = NULL;
1090     png_uint_32 width, height;
1091     int bit_depth, color_type, interlace_type;
1092     png_byte **rows = NULL;
1093     bool retval = false;
1094     png_byte **row_pointers = NULL;
1095     FILE *fp = fopen(file_name, "rb");
1096
1097     if (fp == NULL)
1098         return(NULL);
1099
1100     png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
1101     if (png_ptr == NULL)
1102         goto png_done;
1103
1104     info_ptr = png_create_info_struct(png_ptr);
1105     if (info_ptr == NULL)
1106         goto png_done;
1107
1108     if (setjmp(png_jmpbuf(png_ptr)))
1109         goto png_done;
1110
1111     png_init_io(png_ptr, fp);
1112     png_read_png(png_ptr, info_ptr,
1113                  PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_PACKING,
1114                  NULL);
1115     png_get_IHDR(png_ptr, info_ptr, &width, &height,
1116                  &bit_depth, &color_type, &interlace_type, NULL, NULL);
1117
1118     if (bit_depth != 8)  // transform SHOULD handle this...
1119         goto png_done;
1120
1121     if (color_type & PNG_COLOR_MASK_PALETTE)  // !!! FIXME?
1122         goto png_done;
1123
1124     if ((color_type & PNG_COLOR_MASK_COLOR) == 0)  // !!! FIXME?
1125         goto png_done;
1126
1127     hasalpha = ((color_type & PNG_COLOR_MASK_ALPHA) != 0);
1128     row_pointers = png_get_rows(png_ptr, info_ptr);
1129     if (!row_pointers)
1130         goto png_done;
1131
1132     if (!hasalpha)
1133     {
1134         png_byte *dst = tex.data;
1135         for (int i = height-1; i >= 0; i--)
1136         {
1137             png_byte *src = row_pointers[i];
1138             for (int j = 0; j < width; j++)
1139             {
1140                 dst[0] = src[0];
1141                 dst[1] = src[1];
1142                 dst[2] = src[2];
1143                 dst[3] = 0xFF;
1144                 src += 3;
1145                 dst += 4;
1146             }
1147         }
1148     }
1149
1150     else
1151     {
1152         png_byte *dst = tex.data;
1153         int pitch = width * 4;
1154         for (int i = height-1; i >= 0; i--, dst += pitch)
1155             memcpy(dst, row_pointers[i], pitch);
1156     }
1157
1158     tex.sizeX = width;
1159     tex.sizeY = height;
1160     tex.bpp = 32;
1161     retval = true;
1162
1163 png_done:
1164     png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
1165     if (fp)
1166         fclose(fp);
1167     return (retval);
1168 }
1169
1170
1171 static bool save_image(const char *file_name)
1172 {
1173     const char *ptr = strrchr((char *)file_name, '.');
1174     if (ptr)
1175     {
1176         if (strcasecmp(ptr+1, "png") == 0)
1177             return save_png(file_name);
1178     }
1179
1180     STUBBED("Unsupported image type");
1181     return false;
1182 }
1183
1184
1185 static bool save_png(const char *file_name)
1186 {
1187     FILE *fp = NULL;
1188     png_structp png_ptr = NULL;
1189     png_infop info_ptr = NULL;
1190     bool retval = false;
1191
1192     fp = fopen(file_name, "wb");
1193     if (fp == NULL)
1194         return false;
1195
1196     png_bytep *row_pointers = new png_bytep[kContextHeight];
1197     png_bytep screenshot = new png_byte[kContextWidth * kContextHeight * 3];
1198     if ((!screenshot) || (!row_pointers))
1199         goto save_png_done;
1200
1201     glGetError();
1202     glReadPixels(0, 0, kContextWidth, kContextHeight,
1203                  GL_RGB, GL_UNSIGNED_BYTE, screenshot);
1204     if (glGetError() != GL_NO_ERROR)
1205         goto save_png_done;
1206
1207     for (int i = 0; i < kContextHeight; i++)
1208         row_pointers[i] = screenshot + ((kContextWidth * ((kContextHeight-1) - i)) * 3);
1209
1210     png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
1211     if (png_ptr == NULL)
1212         goto save_png_done;
1213
1214     info_ptr = png_create_info_struct(png_ptr);
1215     if (info_ptr == NULL)
1216         goto save_png_done;
1217
1218     if (setjmp(png_jmpbuf(png_ptr)))
1219         goto save_png_done;
1220
1221     png_init_io(png_ptr, fp);
1222
1223     if (setjmp(png_jmpbuf(png_ptr)))
1224         goto save_png_done;
1225
1226     png_set_IHDR(png_ptr, info_ptr, kContextWidth, kContextHeight,
1227                  8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
1228                  PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
1229
1230     png_write_info(png_ptr, info_ptr);
1231
1232     if (setjmp(png_jmpbuf(png_ptr)))
1233         goto save_png_done;
1234
1235         png_write_image(png_ptr, row_pointers);
1236
1237         if (setjmp(png_jmpbuf(png_ptr)))
1238         goto save_png_done;
1239
1240     png_write_end(png_ptr, NULL);
1241     retval = true;
1242
1243 save_png_done:
1244     png_destroy_write_struct(&png_ptr, &info_ptr);
1245     delete[] screenshot;
1246     delete[] row_pointers;
1247     if (fp)
1248         fclose(fp);
1249     if (!retval)
1250         unlink(ConvertFileName(file_name));
1251     return retval;
1252 }
1253
1254
1255