]> icculus.org git repositories - btb/d2x.git/blob - arch/carbon/SDL_main.c
use PhysicsFS for saving levels
[btb/d2x.git] / arch / carbon / SDL_main.c
1 /*
2     SDL - Simple DirectMedia Layer
3     Copyright (C) 1997-2004 Sam Lantinga
4
5     This library is free software; you can redistribute it and/or
6     modify it under the terms of the GNU Library General Public
7     License as published by the Free Software Foundation; either
8     version 2 of the License, or (at your option) any later version.
9
10     This library is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13     Library General Public License for more details.
14
15     You should have received a copy of the GNU Library General Public
16     License along with this library; if not, write to the Free
17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
19     Sam Lantinga
20     slouken@libsdl.org
21 */
22
23 #ifdef SAVE_RCSID
24 static char rcsid =
25  "@(#) $Id: SDL_main.c,v 1.1 2004-08-01 13:08:34 schaffner Exp $";
26 #endif
27
28 /* This file takes care of command line argument parsing, and stdio redirection
29    in the MacOS environment.
30  */
31
32 #ifdef HAVE_CONFIG_H
33 #include "conf.h"
34 #endif
35
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <ctype.h>
40 #if defined(__APPLE__) && defined(__MACH__)
41 #include <Carbon/Carbon.h>
42 #elif TARGET_API_MAC_CARBON
43 #include <Carbon.h>
44 #else
45 #include <Dialogs.h>
46 #include <Fonts.h>
47 #include <Events.h>
48 #include <Resources.h>
49 #include <Folders.h>
50 #endif
51
52 /* Include the SDL main definition header */
53 #include "SDL.h"
54 #include "SDL_main.h"
55 #ifdef main
56 #undef main
57 #endif
58
59 /* The standard output files */
60 #define STDOUT_FILE     "stdout.txt"
61 #define STDERR_FILE     "stderr.txt"
62
63 #if !defined(__MWERKS__) && !TARGET_API_MAC_CARBON
64         /* In MPW, the qd global has been removed from the libraries */
65         QDGlobals qd;
66 #endif
67
68 /* Structure for keeping prefs in 1 variable */
69 typedef struct {
70     Str255  command_line;
71     Str255  video_driver_name;
72     Boolean output_to_file;
73 }  PrefsRecord;
74
75 /* See if the command key is held down at startup */
76 static Boolean CommandKeyIsDown(void)
77 {
78         KeyMap  theKeyMap;
79
80         GetKeys(theKeyMap);
81
82         if (((unsigned char *) theKeyMap)[6] & 0x80) {
83                 return(true);
84         }
85         return(false);
86 }
87
88 /* Parse a command line buffer into arguments */
89 static int ParseCommandLine(char *cmdline, char **argv)
90 {
91         char *bufp;
92         int argc;
93
94         argc = 0;
95         for ( bufp = cmdline; *bufp; ) {
96                 /* Skip leading whitespace */
97                 while ( isspace(*bufp) ) {
98                         ++bufp;
99                 }
100                 /* Skip over argument */
101                 if ( *bufp == '"' ) {
102                         ++bufp;
103                         if ( *bufp ) {
104                                 if ( argv ) {
105                                         argv[argc] = bufp;
106                                 }
107                                 ++argc;
108                         }
109                         /* Skip over word */
110                         while ( *bufp && (*bufp != '"') ) {
111                                 ++bufp;
112                         }
113                 } else {
114                         if ( *bufp ) {
115                                 if ( argv ) {
116                                         argv[argc] = bufp;
117                                 }
118                                 ++argc;
119                         }
120                         /* Skip over word */
121                         while ( *bufp && ! isspace(*bufp) ) {
122                                 ++bufp;
123                         }
124                 }
125                 if ( *bufp ) {
126                         if ( argv ) {
127                                 *bufp = '\0';
128                         }
129                         ++bufp;
130                 }
131         }
132         if ( argv ) {
133                 argv[argc] = NULL;
134         }
135         return(argc);
136 }
137
138 /* Remove the output files if there was no output written */
139 static void cleanup_output(void)
140 {
141         FILE *file;
142         int empty;
143
144         /* Flush the output in case anything is queued */
145         fclose(stdout);
146         fclose(stderr);
147
148         /* See if the files have any output in them */
149         file = fopen(STDOUT_FILE, "rb");
150         if ( file ) {
151                 empty = (fgetc(file) == EOF) ? 1 : 0;
152                 fclose(file);
153                 if ( empty ) {
154                         remove(STDOUT_FILE);
155                 }
156         }
157         file = fopen(STDERR_FILE, "rb");
158         if ( file ) {
159                 empty = (fgetc(file) == EOF) ? 1 : 0;
160                 fclose(file);
161                 if ( empty ) {
162                         remove(STDERR_FILE);
163                 }
164         }
165 }
166
167 static int getCurrentAppName (StrFileName name) {
168         
169     ProcessSerialNumber process;
170     ProcessInfoRec      process_info;
171     FSSpec              process_fsp;
172     
173     process.highLongOfPSN = 0;
174     process.lowLongOfPSN  = kCurrentProcess;
175     process_info.processInfoLength = sizeof (process_info);
176     process_info.processName    = NULL;
177     process_info.processAppSpec = &process_fsp;
178     
179     if ( noErr != GetProcessInformation (&process, &process_info) )
180        return 0;
181     
182     memcpy (name, process_fsp.name, process_fsp.name[0] + 1);
183     return 1;
184 }
185
186 static int getPrefsFile (FSSpec *prefs_fsp, int create) {
187
188     /* The prefs file name is the application name, possibly truncated, */
189     /* plus " Preferences */
190     
191     #define  SUFFIX   " Preferences"
192     #define  MAX_NAME 19             /* 31 - strlen (SUFFIX) */
193     
194     short  volume_ref_number;
195     long   directory_id;
196     StrFileName  prefs_name;
197     StrFileName  app_name;
198     
199     /* Get Preferences folder - works with Multiple Users */
200     if ( noErr != FindFolder ( kOnSystemDisk, kPreferencesFolderType, kDontCreateFolder,
201                                &volume_ref_number, &directory_id) )
202         exit (-1);
203     
204     if ( ! getCurrentAppName (app_name) )
205         exit (-1);
206     
207     /* Truncate if name is too long */
208     if (app_name[0] > MAX_NAME )
209         app_name[0] = MAX_NAME;
210         
211     memcpy (prefs_name + 1, app_name + 1, app_name[0]);    
212     memcpy (prefs_name + app_name[0] + 1, SUFFIX, strlen (SUFFIX));
213     prefs_name[0] = app_name[0] + strlen (SUFFIX);
214    
215     /* Make the file spec for prefs file */
216     if ( noErr != FSMakeFSSpec (volume_ref_number, directory_id, prefs_name, prefs_fsp) )
217         if ( !create )
218             return 0;
219         else {
220             /* Create the prefs file */
221             memcpy (prefs_fsp->name, prefs_name, prefs_name[0] + 1);
222             prefs_fsp->parID   = directory_id;
223             prefs_fsp->vRefNum = volume_ref_number;
224                 
225             FSpCreateResFile (prefs_fsp, '????', 'pref', 0);
226             
227             if ( noErr != ResError () )
228                 return 0;
229         }
230       
231     return 1;
232 }
233
234 static int readPrefsResource (PrefsRecord *prefs) {
235     
236     Handle prefs_handle;
237     
238     prefs_handle = Get1Resource( 'CLne', 128 );
239
240         if (prefs_handle != NULL) {
241                 int offset = 0;
242                 
243                 HLock(prefs_handle);
244                 
245                 /* Get command line string */   
246                 memcpy (prefs->command_line, *prefs_handle, (*prefs_handle)[0]+1);
247
248                 /* Get video driver name */
249                 offset += (*prefs_handle)[0] + 1;       
250                 memcpy (prefs->video_driver_name, *prefs_handle + offset, (*prefs_handle)[offset] + 1);         
251                 
252                 /* Get save-to-file option (1 or 0) */
253                 offset += (*prefs_handle)[offset] + 1;
254                 prefs->output_to_file = (*prefs_handle)[offset];
255                 
256                 ReleaseResource( prefs_handle );
257     
258         return ResError() == noErr;
259     }
260
261     return 0;
262 }
263
264 static int writePrefsResource (PrefsRecord *prefs, short resource_file) {
265
266     Handle prefs_handle;
267     
268     UseResFile (resource_file);
269     
270     prefs_handle = Get1Resource ( 'CLne', 128 );
271     if (prefs_handle != NULL)
272         RemoveResource (prefs_handle);
273     
274     prefs_handle = NewHandle ( prefs->command_line[0] + prefs->video_driver_name[0] + 4 );
275     if (prefs_handle != NULL) {
276     
277         int offset;
278         
279         HLock (prefs_handle);
280         
281         /* Command line text */
282         offset = 0;
283         memcpy (*prefs_handle, prefs->command_line, prefs->command_line[0] + 1);
284         
285         /* Video driver name */
286         offset += prefs->command_line[0] + 1;
287         memcpy (*prefs_handle + offset, prefs->video_driver_name, prefs->video_driver_name[0] + 1);
288         
289         /* Output-to-file option */
290         offset += prefs->video_driver_name[0] + 1;
291         *( *((char**)prefs_handle) + offset)     = (char)prefs->output_to_file;
292         *( *((char**)prefs_handle) + offset + 1) = 0;
293               
294         AddResource   (prefs_handle, 'CLne', 128, "\pCommand Line");
295         WriteResource (prefs_handle);
296         UpdateResFile (resource_file);
297         DisposeHandle (prefs_handle);
298         
299         return ResError() == noErr;
300     }
301     
302     return 0;
303 }
304
305 static int readPreferences (PrefsRecord *prefs) {
306
307     int    no_error = 1;
308     FSSpec prefs_fsp;
309
310     /* Check for prefs file first */
311     if ( getPrefsFile (&prefs_fsp, 0) ) {
312     
313         short  prefs_resource;
314         
315         prefs_resource = FSpOpenResFile (&prefs_fsp, fsRdPerm);
316         if ( prefs_resource == -1 ) /* this shouldn't happen, but... */
317             return 0;
318     
319         UseResFile   (prefs_resource);
320         no_error = readPrefsResource (prefs);     
321         CloseResFile (prefs_resource);
322     }
323     
324     /* Fall back to application's resource fork (reading only, so this is safe) */
325     else {
326     
327           no_error = readPrefsResource (prefs);
328      }
329
330     return no_error;
331 }
332
333 static int writePreferences (PrefsRecord *prefs) {
334     
335     int    no_error = 1;
336     FSSpec prefs_fsp;
337     
338     /* Get prefs file, create if it doesn't exist */
339     if ( getPrefsFile (&prefs_fsp, 1) ) {
340     
341         short  prefs_resource;
342         
343         prefs_resource = FSpOpenResFile (&prefs_fsp, fsRdWrPerm);
344         if (prefs_resource == -1)
345             return 0;
346         no_error = writePrefsResource (prefs, prefs_resource);
347         CloseResFile (prefs_resource);
348     }
349     
350     return no_error;
351 }
352
353 /* This is where execution begins */
354 int main(int argc, char *argv[])
355 {
356
357 #pragma unused(argc, argv)
358         
359 #define DEFAULT_ARGS "\p"                /* pascal string for default args */
360 #define DEFAULT_VIDEO_DRIVER "\ptoolbox" /* pascal string for default video driver name */      
361 #define DEFAULT_OUTPUT_TO_FILE 1         /* 1 == output to file, 0 == no output */
362
363 #define VIDEO_ID_DRAWSPROCKET 1          /* these correspond to popup menu choices */
364 #define VIDEO_ID_TOOLBOX      2
365
366     PrefsRecord prefs = { DEFAULT_ARGS, DEFAULT_VIDEO_DRIVER, DEFAULT_OUTPUT_TO_FILE }; 
367         
368         int     nargs;
369         char   **args;
370         char   *commandLine;
371         
372         StrFileName  appNameText;
373         int     videodriver     = VIDEO_ID_TOOLBOX;
374     int     settingsChanged = 0;
375     
376     long        i;
377
378         /* Kyle's SDL command-line dialog code ... */
379 #if !TARGET_API_MAC_CARBON
380         InitGraf    (&qd.thePort);
381         InitFonts   ();
382         InitWindows ();
383         InitMenus   ();
384         InitDialogs (nil);
385 #endif
386         InitCursor ();
387         FlushEvents(everyEvent,0);
388 #if !TARGET_API_MAC_CARBON
389         MaxApplZone ();
390 #endif
391         MoreMasters ();
392         MoreMasters ();
393 #if 0
394         /* Intialize SDL, and put up a dialog if we fail */
395         if ( SDL_Init (0) < 0 ) {
396
397 #define kErr_OK         1
398 #define kErr_Text       2
399
400         DialogPtr errorDialog;
401         short     dummyType;
402         Rect      dummyRect;
403             Handle    dummyHandle;
404             short     itemHit;
405         
406                 errorDialog = GetNewDialog (1001, nil, (WindowPtr)-1);
407                 DrawDialog (errorDialog);
408                 
409                 GetDialogItem (errorDialog, kErr_Text, &dummyType, &dummyHandle, &dummyRect);
410                 SetDialogItemText (dummyHandle, "\pError Initializing SDL");
411                 
412                 SetPort (errorDialog);
413                 do {
414                         ModalDialog (nil, &itemHit);
415                 } while (itemHit != kErr_OK);
416                 
417                 DisposeDialog (errorDialog);
418                 exit (-1);
419         }
420         atexit(cleanup_output);
421         atexit(SDL_Quit);
422 #endif
423
424 /* Set up SDL's QuickDraw environment  */
425 #if !TARGET_API_MAC_CARBON
426         SDL_InitQuickDraw(&qd);
427 #endif
428
429          if ( readPreferences (&prefs) ) {
430                 
431         if (memcmp (prefs.video_driver_name+1, "DSp", 3) == 0)
432             videodriver = 1;
433         else if (memcmp (prefs.video_driver_name+1, "toolbox", 7) == 0)
434             videodriver = 2;
435          }
436                 
437         if ( CommandKeyIsDown() ) {
438
439 #define kCL_OK          1
440 #define kCL_Cancel      2
441 #define kCL_Text        3
442 #define kCL_File        4
443 #define kCL_Video   6
444        
445         DialogPtr commandDialog;
446         short     dummyType;
447         Rect      dummyRect;
448         Handle    dummyHandle;
449         short     itemHit;
450
451         /* Assume that they will change settings, rather than do exhaustive check */
452         settingsChanged = 1;
453         
454         /* Create dialog and display it */
455         commandDialog = GetNewDialog (1000, nil, (WindowPtr)-1);
456         SetPortDialogPort (commandDialog);
457             
458         /* Setup controls */
459         GetDialogItem   (commandDialog, kCL_File, &dummyType, &dummyHandle, &dummyRect); /* MJS */
460         SetControlValue ((ControlHandle)dummyHandle, prefs.output_to_file );
461
462         GetDialogItem     (commandDialog, kCL_Text, &dummyType, &dummyHandle, &dummyRect);
463         SetDialogItemText (dummyHandle, prefs.command_line);
464
465         GetDialogItem   (commandDialog, kCL_Video, &dummyType, &dummyHandle, &dummyRect);
466         SetControlValue ((ControlRef)dummyHandle, videodriver);
467
468         SetDialogDefaultItem (commandDialog, kCL_OK);
469         SetDialogCancelItem  (commandDialog, kCL_Cancel);
470
471         do {
472                         
473                 ModalDialog(nil, &itemHit); /* wait for user response */
474             
475             /* Toggle command-line output checkbox */   
476                 if ( itemHit == kCL_File ) {
477                         GetDialogItem(commandDialog, kCL_File, &dummyType, &dummyHandle, &dummyRect); /* MJS */
478                         SetControlValue((ControlHandle)dummyHandle, !GetControlValue((ControlHandle)dummyHandle) );
479                 }
480
481         } while (itemHit != kCL_OK && itemHit != kCL_Cancel);
482
483         /* Get control values, even if they did not change */
484         GetDialogItem     (commandDialog, kCL_Text, &dummyType, &dummyHandle, &dummyRect); /* MJS */
485         GetDialogItemText (dummyHandle, prefs.command_line);
486
487         GetDialogItem (commandDialog, kCL_File, &dummyType, &dummyHandle, &dummyRect); /* MJS */
488         prefs.output_to_file = GetControlValue ((ControlHandle)dummyHandle);
489
490         GetDialogItem (commandDialog, kCL_Video, &dummyType, &dummyHandle, &dummyRect);
491         videodriver = GetControlValue ((ControlRef)dummyHandle);
492
493         DisposeDialog (commandDialog);
494
495         if (itemHit == kCL_Cancel ) {
496                 exit (0);
497         }
498         }
499     
500     /* Set pseudo-environment variables for video driver, update prefs */
501         switch ( videodriver ) {
502            case VIDEO_ID_DRAWSPROCKET: 
503               putenv ("SDL_VIDEODRIVER=DSp");
504               memcpy (prefs.video_driver_name, "\pDSp", 4);
505               break;
506            case VIDEO_ID_TOOLBOX:
507               putenv ("SDL_VIDEODRIVER=toolbox");
508               memcpy (prefs.video_driver_name, "\ptoolbox", 8);
509               break;
510         }
511     /* Redirect standard I/O to files */
512         if ( prefs.output_to_file ) {
513                 freopen (STDOUT_FILE, "w", stdout);
514                 freopen (STDERR_FILE, "w", stderr);
515         } else {
516                 fclose (stdout);
517                 fclose (stderr);
518         }
519    
520     if (settingsChanged) {
521         /* Save the prefs, even if they might not have changed (but probably did) */
522         if ( ! writePreferences (&prefs) )
523             fprintf (stderr, "WARNING: Could not save preferences!\n");
524     }
525    
526     getCurrentAppName (appNameText); /* check for error here ? */
527
528     commandLine = (char*) malloc (appNameText[0] + prefs.command_line[0] + 2);
529     if ( commandLine == NULL ) {
530        exit(-1);
531     }
532
533     /* Rather than rewrite ParseCommandLine method, let's replace  */
534     /* any spaces in application name with underscores,            */
535     /* so that the app name is only 1 argument                     */   
536     for (i = 1; i < 1+appNameText[0]; i++)
537         if ( appNameText[i] == ' ' ) appNameText[i] = '_';
538
539     /* Copy app name & full command text to command-line C-string */      
540     memcpy (commandLine, appNameText + 1, appNameText[0]);
541     commandLine[appNameText[0]] = ' ';
542     memcpy (commandLine + appNameText[0] + 1, prefs.command_line + 1, prefs.command_line[0]);
543     commandLine[ appNameText[0] + 1 + prefs.command_line[0] ] = '\0';
544
545     /* Parse C-string into argv and argc */
546     nargs = ParseCommandLine (commandLine, NULL);
547     args = (char **)malloc((nargs+1)*(sizeof *args));
548     if ( args == NULL ) {
549                 exit(-1);
550         }
551         ParseCommandLine (commandLine, args);
552         
553         /* Run the main application code */
554         SDL_main(nargs, args);
555         free (args);
556         free (commandLine);
557    
558         /* Remove useless stdout.txt and stderr.txt */
559         cleanup_output ();
560         
561         /* Exit cleanly, calling atexit() functions */
562         exit (0);    
563
564         /* Never reached, but keeps the compiler quiet */
565         return (0);
566 }