huge CVS merge, see ChangeLog for details.
[supertux.git] / src / setup.c
1 /*
2   setup.c
3   
4   Super Tux - Setup
5   
6   by Bill Kendrick
7   bill@newbreedsoftware.com
8   http://www.newbreedsoftware.com/supertux/
9   
10   April 11, 2000 - February 1st, 2004
11 */
12
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <errno.h>
17 #include <unistd.h>
18 #include <SDL.h>
19 #include <SDL_image.h>
20 #ifndef NOOPENGL
21 #include <SDL_opengl.h>
22 #endif
23
24 #ifdef LINUX
25 #include <pwd.h>
26 #include <sys/stat.h>
27 #include <sys/types.h>
28 #include <dirent.h>
29 #include <ctype.h>
30 #endif
31
32 #include "defines.h"
33 #include "globals.h"
34 #include "setup.h"
35 #include "screen.h"
36 #include "texture.h"
37 #include "menu.h"
38
39 /* Local function prototypes: */
40
41 void seticon(void);
42 void usage(char * prog, int ret);
43
44 /* Does the given file exist and is it accessible? */
45 int faccessible(char *filename)
46 {
47   struct stat filestat;
48   if (stat(filename, &filestat) == -1)
49     return NO;
50   else
51     return YES;
52 }
53
54 /* Can we write to this location? */
55 int fwriteable(char *filename)
56 {
57   FILE* fi;
58   fi = fopen(filename, "wa");
59   if (fi == NULL)
60     {
61       return NO;
62     }
63   return YES;
64 }
65
66 /* Makes sure a directory is created in either the SuperTux base directory or the SuperTux base directory.*/
67 int fcreatedir(char* relative_dir)
68 {
69   char path[1024];
70   snprintf(path, 1024, "%s/%s/", st_dir, relative_dir);
71   if(mkdir(path,0755) != 0)
72     {
73       snprintf(path, 1024, "%s/%s/", DATA_PREFIX, relative_dir);
74       if(mkdir(path,0755) != 0)
75         {
76           return NO;
77         }
78       else
79         {
80           return YES;
81         }
82     }
83   else
84     {
85       return YES;
86     }
87 }
88
89 /* Get all names of sub-directories in a certain directory. */
90 /* Returns the number of sub-directories found. */
91 /* Note: The user has to free the allocated space. */
92 char ** dsubdirs(char *rel_path, char* expected_file, int* num)
93 {
94   DIR *dirStructP;
95   struct dirent *direntp;
96   int i = 0;
97   char ** sdirs= NULL;
98   char filename[100];
99   char path[1024];
100
101   sprintf(path,"%s/%s",st_dir,rel_path);
102   if((dirStructP = opendir(path)) != NULL)
103     {
104       while((direntp = readdir(dirStructP)) != NULL)
105         {
106           if ( direntp->d_type == DT_DIR )
107             {
108               if(expected_file != NULL)
109                 {
110                   sprintf(filename,"%s/%s/%s",path,direntp->d_name,expected_file);
111                   if(!faccessible(filename))
112                     continue;
113                 }
114
115               sdirs = (char**) realloc(sdirs, sizeof(char*) * (i+1));
116               sdirs[i] = (char*) malloc(sizeof(char) * strlen(direntp->d_name) + 1 );
117               strcpy(sdirs[i],direntp->d_name);
118               ++i;
119             }
120         }
121       closedir(dirStructP);
122     }
123
124   sprintf(path,"%s/%s",DATA_PREFIX,rel_path);
125   if((dirStructP = opendir(path)) != NULL)
126     {
127       while((direntp = readdir(dirStructP)) != NULL)
128         {
129           if ( direntp->d_type == DT_DIR )
130             {
131               if(expected_file != NULL)
132                 {
133                   sprintf(filename,"%s/%s/%s",path,direntp->d_name,expected_file);
134                   if(!faccessible(filename))
135                     {
136                       continue;
137                     }
138                   else
139                     {
140                       sprintf(filename,"%s/%s/%s/%s",st_dir,rel_path,direntp->d_name,expected_file);
141                       if(faccessible(filename))
142                         continue;
143                     }
144                 }
145
146               sdirs = (char**) realloc(sdirs, sizeof(char*) * (i+1));
147               sdirs[i] = (char*) malloc(sizeof(char) * strlen(direntp->d_name) + 1 );
148               strcpy(sdirs[i],direntp->d_name);
149               ++i;
150             }
151         }
152       closedir(dirStructP);
153     }
154
155   *num = i;
156   return sdirs;
157 }
158
159 void free_strings(char **strings, int num)
160 {
161 int i;
162 for(i=0; i < num; ++i)
163 free(strings[i]);
164 }
165
166 /* --- SETUP --- */
167 /* Set SuperTux configuration and save directories */
168 void st_directory_setup(void)
169 {
170   char *home;
171   char str[1024];
172   /* Get home directory (from $HOME variable)... if we can't determine it,
173      use the current directory ("."): */
174   if (getenv("HOME") != NULL)
175     home = getenv("HOME");
176   else
177     home = ".";
178
179   st_dir = (char *) malloc(sizeof(char) * (strlen(home) +
180                            strlen("/.supertux") + 1));
181   strcpy(st_dir, home);
182   strcat(st_dir, "/.supertux");
183
184   st_save_dir = (char *) malloc(sizeof(char) * (strlen(st_dir) + strlen("/save") + 1));
185
186   strcpy(st_save_dir,st_dir);
187   strcat(st_save_dir,"/save");
188
189   /* Create them. In the case they exist they won't destroy anything. */
190 #ifdef LINUX
191
192   mkdir(st_dir, 0755);
193   mkdir(st_save_dir, 0755);
194
195   sprintf(str, "%s/levels", st_dir);
196   mkdir(str, 0755);
197 #else
198   #ifdef WIN32
199
200   mkdir(st_dir);
201   mkdir(st_save_dir);
202   sprintf(str, "%s/levels", st_dir);
203   mkdir(str);
204 #endif
205 #endif
206
207 }
208
209 /* Create and setup menus. */
210 void st_menu(void)
211 {
212
213   menu_init(&main_menu);
214   menu_additem(&main_menu,menu_item_create(MN_ACTION,"Start Game",0,0));
215   menu_additem(&main_menu,menu_item_create(MN_ACTION,"Load Game",0,0));
216   menu_additem(&main_menu,menu_item_create(MN_GOTO,"Options",0,&options_menu));
217   menu_additem(&main_menu,menu_item_create(MN_ACTION,"Level editor",0,0));
218   menu_additem(&main_menu,menu_item_create(MN_ACTION,"Quit",0,0));
219
220   menu_init(&options_menu);
221   menu_additem(&options_menu,menu_item_create(MN_TOGGLE,"Fullscreen",use_fullscreen,0));
222   if(audio_device == YES)
223     {
224       menu_additem(&options_menu,menu_item_create(MN_TOGGLE,"Sound",use_sound,0));
225       menu_additem(&options_menu,menu_item_create(MN_TOGGLE,"Music",use_music,0));
226     }
227   else
228     {
229       menu_additem(&options_menu,menu_item_create(MN_DEACTIVE,"Sound",use_sound,0));
230       menu_additem(&options_menu,menu_item_create(MN_DEACTIVE,"Music",use_music,0));
231     }
232   menu_additem(&options_menu,menu_item_create(MN_TOGGLE,"Show FPS",show_fps,0));
233   menu_additem(&options_menu,menu_item_create(MN_BACK,"Back",0,0));
234
235   menu_init(&leveleditor_menu);
236   menu_additem(&leveleditor_menu,menu_item_create(MN_ACTION,"Return To Level Editor",0,0));
237   menu_additem(&leveleditor_menu,menu_item_create(MN_ACTION,"New Level",0,0));
238   menu_additem(&leveleditor_menu,menu_item_create(MN_ACTION,"Load Level",0,0));
239   menu_additem(&leveleditor_menu,menu_item_create(MN_ACTION,"Save Level",0,0));
240   menu_additem(&leveleditor_menu,menu_item_create(MN_ACTION,"Quit Level Editor",0,0));
241
242   menu_init(&game_menu);
243   menu_additem(&game_menu,menu_item_create(MN_ACTION,"Return To Game",0,0));
244   menu_additem(&game_menu,menu_item_create(MN_ACTION,"Save Game",0,0));
245   menu_additem(&game_menu,menu_item_create(MN_ACTION,"Load Game",0,0));
246   menu_additem(&game_menu,menu_item_create(MN_GOTO,"Options",0,&options_menu));
247   menu_additem(&game_menu,menu_item_create(MN_ACTION,"Quit Game",0,0));
248
249   menu_init(&highscore_menu);
250   menu_additem(&highscore_menu,menu_item_create(MN_TEXTFIELD,"Enter your name:",0,0));
251
252 }
253
254 /* Handle changes made to global settings in the options menu. */
255 void process_options_menu(void)
256 {
257   switch (menu_check(&options_menu))
258     {
259     case 0:
260       if(use_fullscreen != options_menu.item[0].toggled)
261         {
262           use_fullscreen = !use_fullscreen;
263           st_video_setup();
264         }
265       break;
266     case 1:
267       if(use_sound != options_menu.item[1].toggled)
268         use_sound = !use_sound;
269       break;
270     case 2:
271       if(use_music != options_menu.item[2].toggled)
272         {
273           if(use_music == YES)
274             {
275               if(playing_music())
276                 {
277                   halt_music();
278                 }
279               use_music = NO;
280             }
281           else
282             {
283               use_music = YES;
284               if (!playing_music())
285                 {
286                   play_current_music();
287                 }
288             }
289         }
290       break;
291     case 3:
292       if(show_fps != options_menu.item[3].toggled)
293         show_fps = !show_fps;
294       break;
295     }
296 }
297
298 void st_general_setup(void)
299 {
300   /* Seed random number generator: */
301
302   srand(SDL_GetTicks());
303
304   /* Load global images: */
305
306   text_load(&black_text,DATA_PREFIX "/images/status/letters-black.png", TEXT_TEXT, 16,18);
307   text_load(&gold_text,DATA_PREFIX "/images/status/letters-gold.png", TEXT_TEXT, 16,18);
308   text_load(&blue_text,DATA_PREFIX "/images/status/letters-blue.png", TEXT_TEXT, 16,18);
309   text_load(&red_text,DATA_PREFIX "/images/status/letters-red.png", TEXT_TEXT, 16,18);
310   text_load(&white_text,DATA_PREFIX "/images/status/letters-white.png", TEXT_TEXT, 16,18);
311   text_load(&white_small_text,DATA_PREFIX "/images/status/letters-white-small.png", TEXT_TEXT, 8,9);
312   text_load(&yellow_nums,DATA_PREFIX "/images/status/numbers.png", TEXT_NUM, 32,32);
313
314   /* Load GUI/menu images: */
315   texture_load(&checkbox, DATA_PREFIX "/images/status/checkbox.png", USE_ALPHA);
316   texture_load(&checkbox_checked, DATA_PREFIX "/images/status/checkbox-checked.png", USE_ALPHA);
317
318   /* Set icon image: */
319
320   seticon();
321   SDL_EnableUNICODE(1);
322
323 }
324
325 void st_general_free(void)
326 {
327
328   /* Free global images: */
329
330   text_free(&black_text);
331   text_free(&gold_text);
332   text_free(&white_text);
333   text_free(&blue_text);
334   text_free(&red_text);
335   text_free(&white_small_text); 
336   
337   /* Free GUI/menu images: */
338   texture_free(&checkbox);
339   texture_free(&checkbox_checked);
340
341   /* Free menus */
342
343   menu_free(&main_menu);
344   menu_free(&game_menu);
345   menu_free(&options_menu);
346   menu_free(&leveleditor_menu);
347   menu_free(&highscore_menu);
348
349 }
350
351 void st_video_setup(void)
352 {
353
354   if(screen != NULL)
355     SDL_FreeSurface(screen);
356
357   /* Init SDL Video: */
358
359   if (SDL_Init(SDL_INIT_VIDEO) < 0)
360     {
361       fprintf(stderr,
362               "\nError: I could not initialize video!\n"
363               "The Simple DirectMedia error that occured was:\n"
364               "%s\n\n", SDL_GetError());
365       exit(1);
366     }
367
368   /* Open display: */
369
370   if(use_gl)
371     st_video_setup_gl();
372   else
373     st_video_setup_sdl();
374
375   texture_setup();
376
377   /* Set window manager stuff: */
378
379   SDL_WM_SetCaption("Super Tux", "Super Tux");
380
381 }
382
383 void st_video_setup_sdl(void)
384 {
385   SDL_FreeSurface(screen);
386
387   if (use_fullscreen == YES)
388     {
389       screen = SDL_SetVideoMode(640, 480, 16, SDL_FULLSCREEN ) ; /* | SDL_HWSURFACE); */
390       if (screen == NULL)
391         {
392           fprintf(stderr,
393                   "\nWarning: I could not set up fullscreen video for "
394                   "640x480 mode.\n"
395                   "The Simple DirectMedia error that occured was:\n"
396                   "%s\n\n", SDL_GetError());
397           use_fullscreen = NO;
398         }
399     }
400   else
401     {
402       screen = SDL_SetVideoMode(640, 480, 16, SDL_HWSURFACE | SDL_DOUBLEBUF );
403
404       if (screen == NULL)
405         {
406           fprintf(stderr,
407                   "\nError: I could not set up video for 640x480 mode.\n"
408                   "The Simple DirectMedia error that occured was:\n"
409                   "%s\n\n", SDL_GetError());
410           exit(1);
411         }
412     }
413 }
414
415 void st_video_setup_gl(void)
416 {
417 #ifndef NOOPENGL
418
419   SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
420   SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5);
421   SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
422   SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
423   SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
424
425   if (use_fullscreen == YES)
426     {
427       screen = SDL_SetVideoMode(640, 480, 32, SDL_FULLSCREEN | SDL_OPENGL | SDL_OPENGLBLIT ) ; /* | SDL_HWSURFACE); */
428       if (screen == NULL)
429         {
430           fprintf(stderr,
431                   "\nWarning: I could not set up fullscreen video for "
432                   "640x480 mode.\n"
433                   "The Simple DirectMedia error that occured was:\n"
434                   "%s\n\n", SDL_GetError());
435           use_fullscreen = NO;
436         }
437     }
438   else
439     {
440       screen = SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE | SDL_OPENGL | SDL_OPENGLBLIT  );
441
442       if (screen == NULL)
443         {
444           fprintf(stderr,
445                   "\nError: I could not set up video for 640x480 mode.\n"
446                   "The Simple DirectMedia error that occured was:\n"
447                   "%s\n\n", SDL_GetError());
448           exit(1);
449         }
450     }
451
452   /*
453    * Set up OpenGL for 2D rendering.
454    */
455   glDisable(GL_DEPTH_TEST);
456   glDisable(GL_CULL_FACE);
457
458   glViewport(0, 0, screen->w, screen->h);
459   glMatrixMode(GL_PROJECTION);
460   glLoadIdentity();
461   glOrtho(0, screen->w, screen->h, 0, -1.0, 1.0);
462
463   glMatrixMode(GL_MODELVIEW);
464   glLoadIdentity();
465   glTranslatef(0.0f, 0.0f, 0.0f);
466
467 #endif
468
469 }
470
471 void st_joystick_setup(void)
472 {
473
474   /* Init Joystick: */
475
476 #ifdef JOY_YES
477   use_joystick = YES;
478
479   if (SDL_Init(SDL_INIT_JOYSTICK) < 0)
480     {
481       fprintf(stderr, "Warning: I could not initialize joystick!\n"
482               "The Simple DirectMedia error that occured was:\n"
483               "%s\n\n", SDL_GetError());
484
485       use_joystick = NO;
486     }
487   else
488     {
489       /* Open joystick: */
490
491       if (SDL_NumJoysticks() <= 0)
492         {
493           fprintf(stderr, "Warning: No joysticks are available.\n");
494
495           use_joystick = NO;
496         }
497       else
498         {
499           js = SDL_JoystickOpen(0);
500
501           if (js == NULL)
502             {
503               fprintf(stderr, "Warning: Could not open joystick 1.\n"
504                       "The Simple DirectMedia error that occured was:\n"
505                       "%s\n\n", SDL_GetError());
506
507               use_joystick = NO;
508             }
509           else
510             {
511               /* Check for proper joystick configuration: */
512
513               if (SDL_JoystickNumAxes(js) < 2)
514                 {
515                   fprintf(stderr,
516                           "Warning: Joystick does not have enough axes!\n");
517
518                   use_joystick = NO;
519                 }
520               else
521                 {
522                   if (SDL_JoystickNumButtons(js) < 2)
523                     {
524                       fprintf(stderr,
525                               "Warning: "
526                               "Joystick does not have enough buttons!\n");
527
528                       use_joystick = NO;
529                     }
530                 }
531             }
532         }
533     }
534 #endif
535
536 }
537
538 void st_audio_setup(void)
539 {
540
541   /* Init SDL Audio silently even if --disable-sound : */
542
543   if (audio_device == YES)
544     {
545       if (SDL_Init(SDL_INIT_AUDIO) < 0)
546         {
547           /* only print out message if sound or music
548              was not disabled at command-line
549            */
550           if (use_sound == YES || use_music == YES)
551             {
552               fprintf(stderr,
553                       "\nWarning: I could not initialize audio!\n"
554                       "The Simple DirectMedia error that occured was:\n"
555                       "%s\n\n", SDL_GetError());
556             }
557           /* keep the programming logic the same :-)
558              because in this case, use_sound & use_music' values are ignored
559              when there's no available audio device
560           */
561           use_sound = NO;
562           use_music = NO;
563           audio_device = NO;
564         }
565     }
566
567
568   /* Open sound silently regarless the value of "use_sound": */
569
570   if (audio_device == YES)
571     {
572       if (open_audio(44100, AUDIO_S16, 2, 2048) < 0)
573         {
574           /* only print out message if sound or music
575              was not disabled at command-line
576            */
577           if ((use_sound == YES) || (use_music == YES))
578             {
579               fprintf(stderr,
580                       "\nWarning: I could not set up audio for 44100 Hz "
581                       "16-bit stereo.\n"
582                       "The Simple DirectMedia error that occured was:\n"
583                       "%s\n\n", SDL_GetError());
584             }
585           use_sound = NO;
586           use_music = NO;
587           audio_device = NO;
588         }
589     }
590
591 }
592
593
594 /* --- SHUTDOWN --- */
595
596 void st_shutdown(void)
597 {
598   close_audio();
599   SDL_Quit();
600 }
601
602
603 /* --- ABORT! --- */
604
605 void st_abort(char * reason, char * details)
606 {
607   fprintf(stderr, "\nError: %s\n%s\n\n", reason, details);
608   st_shutdown();
609   exit(1);
610 }
611
612
613 /* Set Icon (private) */
614
615 void seticon(void)
616 {
617   int masklen;
618   Uint8 * mask;
619   SDL_Surface * icon;
620
621
622   /* Load icon into a surface: */
623
624   icon = IMG_Load(DATA_PREFIX "/images/icon.png");
625   if (icon == NULL)
626     {
627       fprintf(stderr,
628               "\nError: I could not load the icon image: %s\n"
629               "The Simple DirectMedia error that occured was:\n"
630               "%s\n\n", DATA_PREFIX "images/icon.png", SDL_GetError());
631       exit(1);
632     }
633
634
635   /* Create mask: */
636
637   masklen = (((icon -> w) + 7) / 8) * (icon -> h);
638   mask = (Uint8*) malloc(masklen * sizeof(Uint8));
639   memset(mask, 0xFF, masklen);
640
641
642   /* Set icon: */
643
644   SDL_WM_SetIcon(icon, mask);
645
646
647   /* Free icon surface & mask: */
648
649   free(mask);
650   SDL_FreeSurface(icon);
651 }
652
653
654 /* Parse command-line arguments: */
655
656 void parseargs(int argc, char * argv[])
657 {
658   int i;
659
660   /* Set defaults: */
661
662
663   debug_mode = NO;
664   use_fullscreen = NO;
665   show_fps = NO;
666   use_gl = NO;
667
668 #ifndef NOSOUND
669
670   use_sound = YES;
671   use_music = YES;
672   audio_device = YES;
673 #else
674
675   use_sound = NO;
676   use_music = NO;
677   audio_device = NO;
678 #endif
679
680   /* Parse arguments: */
681
682   for (i = 1; i < argc; i++)
683     {
684       if (strcmp(argv[i], "--fullscreen") == 0 ||
685           strcmp(argv[i], "-f") == 0)
686         {
687           /* Use full screen: */
688
689           use_fullscreen = YES;
690         }
691       else if (strcmp(argv[i], "--show-fps") == 0)
692         {
693           /* Use full screen: */
694
695           show_fps = YES;
696         }
697       else if (strcmp(argv[i], "--opengl") == 0 ||
698                strcmp(argv[i], "-gl") == 0)
699         {
700 #ifndef NOOPENGL
701           /* Use OpengGL: */
702
703           use_gl = YES;
704 #endif
705
706         }
707       else if (strcmp(argv[i], "--usage") == 0)
708         {
709           /* Show usage: */
710
711           usage(argv[0], 0);
712         }
713       else if (strcmp(argv[i], "--version") == 0)
714         {
715           /* Show version: */
716
717           printf("Super Tux - version " VERSION "\n");
718           exit(0);
719         }
720       else if (strcmp(argv[i], "--disable-sound") == 0)
721         {
722           /* Disable the compiled in sound feature */
723 #ifndef NOSOUND
724           printf("Sounds disabled \n");
725           use_sound = NO;
726 #else
727
728           printf("Warning: Sounds feature is not compiled in \n");
729 #endif
730
731         }
732       else if (strcmp(argv[i], "--disable-music") == 0)
733         {
734           /* Disable the compiled in sound feature */
735 #ifndef NOSOUND
736           printf("Music disabled \n");
737           use_music = NO;
738 #else
739
740           printf("Warning: Music feature is not compiled in \n");
741 #endif
742
743         }
744       else if (strcmp(argv[i], "--debug-mode") == 0)
745         {
746           /* Enable the debug-mode */
747           debug_mode = YES;
748
749         }
750       else if (strcmp(argv[i], "--help") == 0)
751         {         /* Show help: */
752
753           printf("Super Tux " VERSION "\n\n");
754
755           printf("----------  Command-line options  ----------\n\n");
756
757           printf("  --opengl            - If opengl support was compiled in, this will enable the EXPERIMENTAL OpenGL mode.\n\n");
758
759           printf("  --disable-sound     - If sound support was compiled in,  this will\n                        disable sound for this session of the game.\n\n");
760
761           printf("  --disable-music     - Like above, but this will disable music.\n\n");
762
763           printf("  --fullscreen        - Run in fullscreen mode.\n\n");
764
765           printf("  --debug-mode        - Enables the debug-mode, which is useful for developers.\n\n");
766
767           printf("  --help              - Display a help message summarizing command-line\n                        options, license and game controls.\n\n");
768
769           printf("  --usage             - Display a brief message summarizing command-line options.\n\n");
770
771           printf("  --version           - Display the version of SuperTux you're running.\n\n\n");
772
773
774           printf("----------          License       ----------\n\n");
775           printf("  This program comes with ABSOLUTELY NO WARRANTY.\n");
776           printf("  This is free software, and you are welcome to redistribute\n");
777           printf("  or modify it under certain conditions. See the file \n");
778           printf("  \"COPYING.txt\" for more details.\n\n\n");
779
780           printf("----------      Game controls     ----------\n\n");
781           printf("  Please see the file \"README.txt\"\n\n");
782
783           exit(0);
784         }
785       else
786         {
787           /* Unknown - complain! */
788
789           usage(argv[0], 1);
790         }
791     }
792 }
793
794
795 /* Display usage: */
796
797 void usage(char * prog, int ret)
798 {
799   FILE * fi;
800
801
802   /* Determine which stream to write to: */
803
804   if (ret == 0)
805     fi = stdout;
806   else
807     fi = stderr;
808
809
810   /* Display the usage message: */
811
812   fprintf(fi, "Usage: %s [--fullscreen] [--opengl] [--disable-sound] [--disable-music] [--debug-mode] | [--usage | --help | --version]\n",
813           prog);
814
815
816   /* Quit! */
817
818   exit(ret);
819 }
820