Fix blenderplayer command line option parsing.
[blender.git] / source / gameengine / GamePlayer / ghost / GPG_ghost.cpp
1 /**
2 * $Id$
3 *
4  * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
5  *
6  * This program 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. The Blender
10  * Foundation also sells licenses for use in proprietary software under
11  * the Blender License.  See http://www.blender.org/BL/ for information
12  * about this.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  *
23  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
24  * All rights reserved.
25  *
26  * The Original Code is: all of this file.
27  *
28  * Contributor(s): none yet.
29  *
30  * ***** END GPL/BL DUAL LICENSE BLOCK *****
31 * Start up of the Blender Player on GHOST.
32 */
33
34 #include <iostream>
35 #include <math.h>
36
37 #ifdef HAVE_CONFIG_H
38 #include <config.h>
39 #endif
40
41 #ifdef __linux__
42 #ifdef __alpha__
43 #include <signal.h>
44 #endif /* __alpha__ */
45 #endif /* __linux__ */
46
47 #ifdef __APPLE__
48 // Can't use Carbon right now because of double defined type ID (In Carbon.h and DNA_ID.h, sigh)
49 //#include <Carbon/Carbon.h>
50 //#include <CFBundle.h>
51 #endif // __APPLE__
52 #include "GEN_messaging.h"
53 #include "KX_KetsjiEngine.h"
54
55 /**********************************
56 * Begin Blender include block
57 **********************************/
58 #ifdef __cplusplus
59 extern "C"
60 {
61 #endif  // __cplusplus
62         
63 #include "BLI_blenlib.h"
64 #include "DNA_scene_types.h"
65 #include "BLO_readfile.h"
66 #include "BLO_readblenfile.h"
67         
68         int GHOST_HACK_getFirstFile(char buf[]);
69         
70 #ifdef __cplusplus
71 }
72 #endif // __cplusplus
73 /**********************************
74 * End Blender include block
75 **********************************/
76
77 #include "SYS_System.h"
78 #include "GPG_Application.h"
79 #include "GPC_PolygonMaterial.h"
80
81 #include "GHOST_ISystem.h"
82 #include "RAS_IRasterizer.h"
83
84 #include "BKE_main.h"
85 #include "BKE_utildefines.h"
86
87 #ifdef WIN32
88 #ifdef NDEBUG
89 #include <windows.h>
90 #include <wincon.h>
91 #endif // NDEBUG
92 #endif // WIN32
93
94 const int kMinWindowWidth = 100;
95 const int kMinWindowHeight = 100;
96
97 char bprogname[FILE_MAXDIR+FILE_MAXFILE];
98
99 void usage(char* program)
100 {
101         char * consoleoption;
102 #ifdef _WIN32
103         consoleoption = "-c ";
104 #else
105         consoleoption = "";
106 #endif
107         
108         printf("usage:   %s [-w [-p l t w h]] %s[-g gamengineoptions] "
109                 "[-s stereomode] filename.blend\n", program, consoleoption);
110         printf("  -w: display in a window\n");
111         printf("  -p: specify window position\n");
112         printf("       l = window left coordinate\n");
113         printf("       t = window top coordinate\n");
114         printf("       w = window width\n");
115         printf("       h = window height\n");
116         printf("  -f: start game in full screen mode\n");
117         printf("       fw = full screen mode pixel width\n");
118         printf("       fh = full screen mode pixel height\n");
119         printf("       fb = full screen mode bits per pixel\n");
120         printf("       ff = full screen mode frequency\n");
121         printf("  -s: start player in stereo\n");
122         printf("       stereomode: hwpageflip       (Quad buffered shutter glasses)\n");
123         printf("                   syncdoubling     (Above Below)\n");
124         printf("                   sidebyside       (Left Right)\n");
125         printf("                   anaglyph         (Red-Blue glasses)\n");
126         printf("                             depending on the type of stereo you want\n");
127 #ifdef _WIN32
128         printf("  -c: keep console window open\n");
129 #endif
130         printf("  -g: game engine options:\n");
131         printf("       Name            Default      Description\n");
132         printf("       ----------------------------------------\n");
133         printf("       fixedtime          0         Do the same timestep each frame \"Enable all frames\"\n");
134         printf("       nomipmap           0         Disable mipmaps\n");
135         printf("       show_framerate     0         Show the frame rate\n");
136         printf("       show_properties    0         Show debug properties\n");
137         printf("       show_profile       0         Show profiling information\n");
138         printf("       vertexarrays       1         Enable vertex arrays\n");
139         printf("\n");
140         printf("example: %s -p 10 10 320 200 -g noaudio c:\\loadtest.blend\n", program);
141         printf("example: %s -g vertexarrays = 0 c:\\loadtest.blend\n", program);
142 }
143
144 char *get_filename(int argc, char **argv) {
145 #ifdef __APPLE__
146 /* On Mac we park the game file (called game.blend) in the application bundle.
147 * The executable is located in the bundle as well.
148 * Therefore, we can locate the game relative to the executable.
149         */
150         int srclen = ::strlen(argv[0]);
151         int len = 0;
152         char *filename = NULL;
153         
154         if (argc > 1) {
155                 if (BLI_exists(argv[argc-1])) {
156                         len = ::strlen(argv[argc-1]);
157                         filename = new char [len + 1];
158                         ::strcpy(filename, argv[argc-1]);
159                         return(filename);
160                 }
161                 if (::strncmp(argv[argc-1], "-psn_", 5)==0) {
162                         static char firstfilebuf[512];
163                         if (GHOST_HACK_getFirstFile(firstfilebuf)) {
164                                 len = ::strlen(firstfilebuf);
165                                 filename = new char [len + 1];
166                                 ::strcpy(filename, firstfilebuf);
167                                 return(filename);
168                         }
169                 }                        
170         }
171         
172         srclen -= ::strlen("MacOS/blenderplayer");
173         if (srclen > 0) {
174                 len = srclen + ::strlen("Resources/game.blend"); 
175                 filename = new char [len + 1];
176                 ::strcpy(filename, argv[0]);
177                 ::strcpy(filename + srclen, "Resources/game.blend");
178                 //::printf("looking for file: %s\n", filename);
179                 
180                 if (BLI_exists(filename)) {
181                         return (filename);
182                 }
183         }
184         
185         return(NULL);
186 #else
187         return (argc>1)?argv[argc-1]:NULL;
188 #endif // !_APPLE
189 }
190
191 static BlendFileData *load_game_data(char *progname, char *filename = NULL) {
192         BlendReadError error;
193         BlendFileData *bfd = NULL;
194         
195         /* try to load ourself, will only work if we are a runtime */
196         if (blo_is_a_runtime(progname)) {
197                 bfd= blo_read_runtime(progname, &error);
198                 if (bfd) {
199                         bfd->type= BLENFILETYPE_RUNTIME;
200                         strcpy(bfd->main->name, progname);
201                 }
202         } else {
203                 bfd= BLO_read_from_file(progname, &error);
204         }
205         
206         /*
207         if (bfd && bfd->type == BLENFILETYPE_BLEND) {
208                 BLO_blendfiledata_free(bfd);
209                 bfd = NULL;
210                 error = BRE_NOT_A_PUBFILE;
211         }
212         */
213         
214         if (!bfd && filename) {
215                 bfd = load_game_data(filename);
216                 if (!bfd) {
217                         printf("Loading %s failed: %s\n", filename, BLO_bre_as_string(error));
218                 }
219         }
220         
221         return bfd;
222 }
223
224 int main(int argc, char** argv)
225 {
226         int i;
227         bool error = false;
228         SYS_SystemHandle syshandle = SYS_GetSystem();
229         bool fullScreen = false;
230         bool fullScreenParFound = false;
231         bool windowParFound = false;
232         bool closeConsole = true;
233         RAS_IRasterizer::StereoMode stereomode = RAS_IRasterizer::RAS_STEREO_NOSTEREO;
234         bool stereoWindow = false;
235         bool stereoParFound = false;
236         int windowLeft = 100;
237         int windowTop = 100;
238         int windowWidth = 640;
239         int windowHeight = 480;
240         GHOST_TUns32 fullScreenWidth = 0;
241         GHOST_TUns32 fullScreenHeight= 0;
242         int fullScreenBpp = 32;
243         int fullScreenFrequency = 60;
244         
245 #ifdef __linux__
246 #ifdef __alpha__
247         signal (SIGFPE, SIG_IGN);
248 #endif /* __alpha__ */
249 #endif /* __linux__ */
250         BLI_where_am_i(bprogname, argv[0]);
251         
252 #ifdef __APPLE__
253     // Can't use Carbon right now because of double defined type ID (In Carbon.h and DNA_ID.h, sigh)
254     /*
255     IBNibRef            nibRef;
256     WindowRef           window;
257     OSStatus            err;
258         
259           // Create a Nib reference passing the name of the nib file (without the .nib extension)
260           // CreateNibReference only searches into the application bundle.
261           err = ::CreateNibReference(CFSTR("main"), &nibRef);
262           if (err) return -1;
263           
264                 // Once the nib reference is created, set the menu bar. "MainMenu" is the name of the menu bar
265                 // object. This name is set in InterfaceBuilder when the nib is created.
266                 err = ::SetMenuBarFromNib(nibRef, CFSTR("MenuBar"));
267                 if (err) return -1;
268                 
269                   // We don't need the nib reference anymore.
270                   ::DisposeNibReference(nibRef);
271     */
272 #endif // __APPLE__
273         
274         GEN_init_messaging_system();
275         
276         // Parse command line options
277 #ifndef NDEBUG
278         printf("argv[0] = '%s'\n", argv[0]);
279 #endif
280         for (i = 1; (i < argc) && !error;)
281         {
282 #ifndef NDEBUG
283                 printf("argv[%d] = '%s'\n", i, argv[i]);
284 #endif
285                 if (argv[i][0] == '-')
286                 {
287                         switch (argv[i][1])
288                         {
289                         case 'g':
290                                 // Parse game options
291                                 {
292                                         i++;
293                                         if (i < argc)
294                                         {
295                                                 char* paramname = argv[i];
296                                                 // Check for single value versus assignment
297                                                 if (i+1 < argc && (*(argv[i+1]) == '='))
298                                                 {
299                                                         i++;
300                                                         if (i + 1 < argc)
301                                                         {
302                                                                 i++;
303                                                                 // Assignment
304                                                                 SYS_WriteCommandLineInt(syshandle, paramname, atoi(argv[i]));
305                                                                 SYS_WriteCommandLineFloat(syshandle, paramname, atof(argv[i]));
306                                                                 SYS_WriteCommandLineString(syshandle, paramname, argv[i]);
307 #ifndef NDEBUG
308                                                                 printf("%s = '%s'\n", paramname, argv[i]);
309 #endif
310                                                                 i++;
311                                                         }
312                                                         else
313                                                         {
314                                                                 error = true;
315                                                                 printf("error: argument assignment %s without value.\n", paramname);
316                                                         }
317                                                 }
318                                                 else
319                                                 {
320                                                         SYS_WriteCommandLineInt(syshandle, argv[i++], 1);
321                                                 }
322                                         }
323                                 }
324                                 break;
325                                 
326                         case 'p':
327                                 // Parse window position and size options
328                                 if (argv[i][2] == 0) {
329                                         i++;
330                                         if ((i + 4) < argc)
331                                         {
332                                                 windowLeft = atoi(argv[i++]);
333                                                 windowTop = atoi(argv[i++]);
334                                                 windowWidth = atoi(argv[i++]);
335                                                 windowHeight = atoi(argv[i++]);
336                                                 windowParFound = true;
337                                         }
338                                         else
339                                         {
340                                                 error = true;
341                                                 printf("error: too few options for window argument.\n");
342                                         }
343                                 }
344                                 break;
345                         case 'f':
346                                 i++;
347                                 fullScreen = true;
348                                 fullScreenParFound = true;
349                                 if ((i + 2) < argc && argv[i][0] != '-' && argv[i+1][0] != '-')
350                                 {
351                                         fullScreenWidth = atoi(argv[i++]);
352                                         fullScreenHeight = atoi(argv[i++]);
353                                         if ((i + 1) < argc && argv[i][0] != '-')
354                                         {
355                                                 fullScreenBpp = atoi(argv[i++]);
356                                                 if ((i + 1) < argc && argv[i][0] != '-')
357                                                         fullScreenFrequency = atoi(argv[i++]);
358                                         }
359                                 }
360                                 break;
361                         case 'w':
362                                 // Parse window position and size options
363                                 {
364                                         fullScreen = false;
365                                         fullScreenParFound = true;
366                                         i++;
367                                 }
368                                 break;
369                         case 'c':
370                                 i++;
371                                 closeConsole = false;
372                                 break;
373                         case 's':  // stereo
374                                 i++;
375                                 if ((i + 1) < argc)
376                                 {
377                                         if(!strcmp(argv[i], "nostereo"))  // ok, redundant but clear
378                                                 stereomode = RAS_IRasterizer::RAS_STEREO_NOSTEREO;
379                                         
380                                         // only the hardware pageflip method needs a stereo window
381                                         if(!strcmp(argv[i], "hwpageflip")) {
382                                                 stereomode = RAS_IRasterizer::RAS_STEREO_QUADBUFFERED;
383                                                 stereoWindow = true;
384                                         }
385                                         if(!strcmp(argv[i], "syncdoubling"))
386                                                 stereomode = RAS_IRasterizer::RAS_STEREO_ABOVEBELOW;
387                                         
388                                         if(!strcmp(argv[i], "anaglyph"))
389                                                 stereomode = RAS_IRasterizer::RAS_STEREO_ANAGLYPH;
390                                         
391                                         if(!strcmp(argv[i], "sidebyside"))
392                                                 stereomode = RAS_IRasterizer::RAS_STEREO_SIDEBYSIDE;
393 #if 0
394                                         // future stuff
395                                         if(strcmp(argv[i], "stencil")
396                                                 stereomode = RAS_STEREO_STENCIL;
397 #endif
398                                         
399                                         i++;
400                                         stereoParFound = true;
401                                 }
402                                 else
403                                 {
404                                         error = true;
405                                         printf("error: too few options for stereo argument.\n");
406                                 }
407                                 break;
408                         default:
409                                 printf("Unkown argument: %s\n", argv[i++]);
410                                 break;
411                         }
412                 }
413                 else
414                 {
415                         i++;
416                 }
417         }
418         
419         if ((windowWidth < kMinWindowWidth) || (windowHeight < kMinWindowHeight))
420         {
421                 error = true;
422                 printf("error: window size too small.\n");
423         }
424         
425         if (error)
426         {
427                 usage(argv[0]);
428         }
429         else
430         {
431 #ifdef __APPLE__
432                 //SYS_WriteCommandLineInt(syshandle, "show_framerate", 1);
433                 SYS_WriteCommandLineInt(syshandle, "nomipmap", 1);
434                 //fullScreen = false;           // Can't use full screen
435 #endif
436                 if (SYS_GetCommandLineInt(syshandle, "nomipmap", 0))
437                 {
438                         GPC_PolygonMaterial::SetMipMappingEnabled(0);
439                 }
440                 
441                 // Create the system
442                 if (GHOST_ISystem::createSystem() == GHOST_kSuccess)
443                 {
444                         GHOST_ISystem* system = GHOST_ISystem::getSystem();
445                         assertd(system);
446                         
447                         if (!fullScreenWidth || !fullScreenHeight)
448                                 system->getMainDisplayDimensions(fullScreenWidth, fullScreenHeight);
449                         // process first batch of events. If the user
450                         // drops a file on top off the blenderplayer icon, we 
451                         // recieve an event with the filename
452                         
453                         system->processEvents(0);
454                         
455                         // this bracket is needed for app (see below) to get out
456                         // of scope before GHOST_ISystem::disposeSystem() is called.
457                         {
458                                 int exitcode = KX_EXIT_REQUEST_NO_REQUEST;
459                                 STR_String exitstring = "";
460                                 GPG_Application app(system, NULL, exitstring);
461                                 bool firstTimeRunning = true;
462                                 
463                                 do
464                                 {
465                                         // Read the Blender file
466                                         char *filename = get_filename(argc, argv);
467                                         char *titlename;
468                                         char pathname[160];
469                                         BlendFileData *bfd;
470                                         
471                                         // if we got an exitcode 3 (KX_EXIT_REQUEST_START_OTHER_GAME) load a different file
472                                         if (exitcode == KX_EXIT_REQUEST_START_OTHER_GAME)
473                                         {
474                                                 char basedpath[160];
475                                                 
476                                                 // base the actuator filename with respect
477                                                 // to the original file working directory
478                                                 strcpy(basedpath, exitstring.Ptr());
479                                                 BLI_convertstringcode(basedpath, pathname, 0);
480                                                 
481                                                 bfd = load_game_data(basedpath);
482                                         }
483                                         else
484                                         {
485                                                 bfd = load_game_data(argv[0], filename);
486                                         }
487                                         
488                                         //::printf("game data loaded from %s\n", filename);
489                                         
490                                         if (!bfd) {
491                                                 usage(argv[0]);
492                                                 error = true;
493                                                 exitcode = KX_EXIT_REQUEST_QUIT_GAME;
494                                         } 
495                                         else 
496                                         {
497 #ifdef WIN32
498 #ifdef NDEBUG
499                                                 if (closeConsole)
500                                                 {
501                                                         ::FreeConsole();    // Close a console window
502                                                 }
503 #endif // NDEBUG
504 #endif // WIN32
505                                                 Main *maggie = bfd->main;
506                                                 Scene *scene = bfd->curscene;
507                                                 strcpy (pathname, maggie->name);
508                                                 char *startscenename = scene->id.name + 2;
509                                                 
510                                                 titlename = maggie->name;
511                                                 
512                                                 // Check whether the game should be displayed full-screen
513                                                 if ((!fullScreenParFound) && (!windowParFound))
514                                                 {
515                                                         // Only use file settings when command line did not override
516                                                         if (scene->r.fullscreen) {
517                                                                 //printf("fullscreen option found in Blender file\n");
518                                                                 fullScreen = true;
519                                                                 fullScreenWidth= scene->r.xplay;
520                                                                 fullScreenHeight= scene->r.yplay;
521                                                                 fullScreenFrequency= scene->r.freqplay;
522                                                                 fullScreenBpp = scene->r.depth;
523                                                         }
524                                                         else
525                                                         {
526                                                                 fullScreen = false;
527                                                                 windowWidth = scene->r.xplay;
528                                                                 windowHeight = scene->r.yplay;
529                                                         }
530                                                 }
531                                                 
532                                                 
533                                                 // Check whether the game should be displayed in stereo
534                                                 if (!stereoParFound)
535                                                 {
536                                                         stereomode = (RAS_IRasterizer::StereoMode) scene->r.stereomode;
537                                                         if (stereomode == RAS_IRasterizer::RAS_STEREO_QUADBUFFERED)
538                                                                 stereoWindow = true;
539                                                 }
540                                                 
541                                                 //                                      GPG_Application app (system, maggie, startscenename);
542                                                 app.SetGameEngineData(maggie, startscenename);
543                                                 
544                                                 if (firstTimeRunning)
545                                                 {
546                                                         firstTimeRunning = false;
547                                                         
548                                                         if (fullScreen)
549                                                         {
550                                                                 app.startFullScreen(fullScreenWidth, fullScreenHeight, fullScreenBpp, fullScreenFrequency,
551                                                                         stereoWindow, stereomode);
552                                                         }
553                                                         else
554                                                         {
555 #ifdef __APPLE__
556                                                                 // on Mac's we'll show the executable name instead of the 'game.blend' name
557                                                                 char tempname[1024], *appstring;
558                                                                 ::strcpy(tempname, titlename);
559                                                                 
560                                                                 appstring = strstr(tempname, ".app/");
561                                                                 if (appstring) {
562                                                                         appstring[2] = 0;
563                                                                         titlename = &tempname[0];
564                                                                 }
565 #endif
566                                                                 // Strip the path so that we have the name of the game file
567                                                                 STR_String path = titlename;
568 #ifndef WIN32
569                                                                 vector<STR_String> parts = path.Explode('/');
570 #else  // WIN32
571                                                                 vector<STR_String> parts = path.Explode('\\');
572 #endif // WIN32                        
573                                                                 STR_String title;
574                                                                 if (parts.size())
575                                                                 {
576                                                                         title = parts[parts.size()-1];
577                                                                         parts = title.Explode('.');
578                                                                         if (parts.size() > 1)
579                                                                         {
580                                                                                 title = parts[0];
581                                                                         }
582                                                                 }
583                                                                 else
584                                                                 {
585                                                                         title = "blenderplayer";
586                                                                 }
587                                                                 app.startWindow(title, windowLeft, windowTop, windowWidth, windowHeight,
588                                                                         stereoWindow, stereomode);
589                                                         }
590                                                 }
591                                                 else
592                                                 {
593                                                         app.StartGameEngine(stereomode);
594                                                         exitcode = KX_EXIT_REQUEST_NO_REQUEST;
595                                                 }
596                                                 
597                                                 // Add the application as event consumer
598                                                 system->addEventConsumer(&app);
599                                                 
600                                                 // Enter main loop
601                                                 bool run = true;
602                                                 while (run)
603                                                 {
604                                                         system->processEvents(false);
605                                                         system->dispatchEvents();
606                                                         if ((exitcode = app.getExitRequested()))
607                                                         {
608                                                                 run = false;
609                                                                 exitstring = app.getExitString();
610                                                         }
611                                                 }
612                                                 app.StopGameEngine();
613                                                 BLO_blendfiledata_free(bfd);
614                                                 
615 #ifdef __APPLE__
616                                                 if (filename) {
617                                                         delete [] filename;
618                                                 }
619 #endif // __APPLE__
620                                         }
621                                 } while (exitcode == KX_EXIT_REQUEST_RESTART_GAME || exitcode == KX_EXIT_REQUEST_START_OTHER_GAME);
622                         }
623                         // Dispose the system
624                         GHOST_ISystem::disposeSystem();
625                 } else {
626                         error = true;
627                         printf("error: couldn't create a system.\n");
628                 }
629         }
630         
631         return error ? -1 : 0;
632 }
633
634