- fixed option menu
[supertux.git] / src / setup.cpp
1 //  $Id$
2 //
3 //  SuperTux -  A Jump'n Run
4 //  Copyright (C) 2000 Bill Kendrick <bill@newbreedsoftware.com>
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.
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.  See the
14 //  GNU General Public License for more details.
15 //
16 //  You should have received a copy of the GNU General Public License
17 //  along with this program; if not, write to the Free Software
18 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19
20 #include <assert.h>
21 #include <stdio.h>
22 #include <iostream>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <unistd.h>
28 #include <SDL.h>
29 #include <SDL_image.h>
30 #ifndef NOOPENGL
31 #include <SDL_opengl.h>
32 #endif
33
34 #include <sys/stat.h>
35 #include <sys/types.h>
36 #include <dirent.h>
37 #ifndef WIN32
38 #include <libgen.h>
39 #endif
40 #include <ctype.h>
41
42 #include "defines.h"
43 #include "globals.h"
44 #include "setup.h"
45 #include "screen.h"
46 #include "texture.h"
47 #include "menu.h"
48 #include "gameloop.h"
49 #include "configfile.h"
50 #include "scene.h"
51 #include "worldmap.h"
52 #include "resources.h"
53
54 #include "player.h"
55
56 #ifdef WIN32
57 #define mkdir(dir, mode)    mkdir(dir)
58 // on win32 we typically don't want LFS paths
59 #undef DATA_PREFIX
60 #define DATA_PREFIX "./data/"
61 #endif
62
63 /* Local function prototypes: */
64
65 void seticon(void);
66 void usage(char * prog, int ret);
67
68 /* Does the given file exist and is it accessible? */
69 int faccessible(const char *filename)
70 {
71   struct stat filestat;
72   if (stat(filename, &filestat) == -1)
73     {
74       return false;
75     }
76   else
77     {
78       if(S_ISREG(filestat.st_mode))
79         return true;
80       else
81         return false;
82     }
83 }
84
85 /* Can we write to this location? */
86 int fwriteable(const char *filename)
87 {
88   FILE* fi;
89   fi = fopen(filename, "wa");
90   if (fi == NULL)
91     {
92       return false;
93     }
94   return true;
95 }
96
97 /* Makes sure a directory is created in either the SuperTux home directory or the SuperTux base directory.*/
98 int fcreatedir(const char* relative_dir)
99 {
100   char path[1024];
101   snprintf(path, 1024, "%s/%s/", st_dir, relative_dir);
102   if(mkdir(path,0755) != 0)
103     {
104       snprintf(path, 1024, "%s/%s/", datadir.c_str(), relative_dir);
105       if(mkdir(path,0755) != 0)
106         {
107           return false;
108         }
109       else
110         {
111           return true;
112         }
113     }
114   else
115     {
116       return true;
117     }
118 }
119
120 FILE * opendata(const char * rel_filename, const char * mode)
121 {
122   char * filename = NULL;
123   FILE * fi;
124
125   filename = (char *) malloc(sizeof(char) * (strlen(st_dir) +
126                                              strlen(rel_filename) + 1));
127
128   strcpy(filename, st_dir);
129   /* Open the high score file: */
130
131   strcat(filename, rel_filename);
132
133   /* Try opening the file: */
134   fi = fopen(filename, mode);
135
136   if (fi == NULL)
137     {
138       fprintf(stderr, "Warning: Unable to open the file \"%s\" ", filename);
139
140       if (strcmp(mode, "r") == 0)
141         fprintf(stderr, "for read!!!\n");
142       else if (strcmp(mode, "w") == 0)
143         fprintf(stderr, "for write!!!\n");
144     }
145   free( filename );
146
147   return(fi);
148 }
149
150 /* Get all names of sub-directories in a certain directory. */
151 /* Returns the number of sub-directories found. */
152 /* Note: The user has to free the allocated space. */
153 string_list_type dsubdirs(const char *rel_path,const  char* expected_file)
154 {
155   DIR *dirStructP;
156   struct dirent *direntp;
157   string_list_type sdirs;
158   char filename[1024];
159   char path[1024];
160
161   string_list_init(&sdirs);
162   sprintf(path,"%s/%s",st_dir,rel_path);
163   if((dirStructP = opendir(path)) != NULL)
164     {
165       while((direntp = readdir(dirStructP)) != NULL)
166         {
167           char absolute_filename[1024];
168           struct stat buf;
169
170           sprintf(absolute_filename, "%s/%s", path, direntp->d_name);
171
172           if (stat(absolute_filename, &buf) == 0 && S_ISDIR(buf.st_mode))
173             {
174               if(expected_file != NULL)
175                 {
176                   sprintf(filename,"%s/%s/%s",path,direntp->d_name,expected_file);
177                   if(!faccessible(filename))
178                     continue;
179                 }
180
181               string_list_add_item(&sdirs,direntp->d_name);
182             }
183         }
184       closedir(dirStructP);
185     }
186
187   sprintf(path,"%s/%s",datadir.c_str(),rel_path);
188   if((dirStructP = opendir(path)) != NULL)
189     {
190       while((direntp = readdir(dirStructP)) != NULL)
191         {
192           char absolute_filename[1024];
193           struct stat buf;
194
195           sprintf(absolute_filename, "%s/%s", path, direntp->d_name);
196
197           if (stat(absolute_filename, &buf) == 0 && S_ISDIR(buf.st_mode))
198             {
199               if(expected_file != NULL)
200                 {
201                   sprintf(filename,"%s/%s/%s",path,direntp->d_name,expected_file);
202                   if(!faccessible(filename))
203                     {
204                       continue;
205                     }
206                   else
207                     {
208                       sprintf(filename,"%s/%s/%s/%s",st_dir,rel_path,direntp->d_name,expected_file);
209                       if(faccessible(filename))
210                         continue;
211                     }
212                 }
213
214               string_list_add_item(&sdirs,direntp->d_name);
215             }
216         }
217       closedir(dirStructP);
218     }
219
220   return sdirs;
221 }
222
223 string_list_type dfiles(const char *rel_path, const  char* glob, const  char* exception_str)
224 {
225   DIR *dirStructP;
226   struct dirent *direntp;
227   string_list_type sdirs;
228   char path[1024];
229
230   string_list_init(&sdirs);
231   sprintf(path,"%s/%s",st_dir,rel_path);
232   if((dirStructP = opendir(path)) != NULL)
233     {
234       while((direntp = readdir(dirStructP)) != NULL)
235         {
236           char absolute_filename[1024];
237           struct stat buf;
238
239           sprintf(absolute_filename, "%s/%s", path, direntp->d_name);
240
241           if (stat(absolute_filename, &buf) == 0 && S_ISREG(buf.st_mode))
242             {
243               if(exception_str != NULL)
244                 {
245                   if(strstr(direntp->d_name,exception_str) != NULL)
246                     continue;
247                 }
248               if(glob != NULL)
249                 if(strstr(direntp->d_name,glob) == NULL)
250                   continue;
251
252               string_list_add_item(&sdirs,direntp->d_name);
253             }
254         }
255       closedir(dirStructP);
256     }
257
258   sprintf(path,"%s/%s",datadir.c_str(),rel_path);
259   if((dirStructP = opendir(path)) != NULL)
260     {
261       while((direntp = readdir(dirStructP)) != NULL)
262         {
263           char absolute_filename[1024];
264           struct stat buf;
265
266           sprintf(absolute_filename, "%s/%s", path, direntp->d_name);
267
268           if (stat(absolute_filename, &buf) == 0 && S_ISREG(buf.st_mode))
269             {
270               if(exception_str != NULL)
271                 {
272                   if(strstr(direntp->d_name,exception_str) != NULL)
273                     continue;
274                 }
275               if(glob != NULL)
276                 if(strstr(direntp->d_name,glob) == NULL)
277                   continue;
278
279               string_list_add_item(&sdirs,direntp->d_name);
280             }
281         }
282       closedir(dirStructP);
283     }
284
285   return sdirs;
286 }
287
288 void free_strings(char **strings, int num)
289 {
290   int i;
291   for(i=0; i < num; ++i)
292     free(strings[i]);
293 }
294
295 /* --- SETUP --- */
296 /* Set SuperTux configuration and save directories */
297 void st_directory_setup(void)
298 {
299   char *home;
300   char str[1024];
301   /* Get home directory (from $HOME variable)... if we can't determine it,
302      use the current directory ("."): */
303   if (getenv("HOME") != NULL)
304     home = getenv("HOME");
305   else
306     home = ".";
307
308   st_dir = (char *) malloc(sizeof(char) * (strlen(home) +
309                                            strlen("/.supertux") + 1));
310   strcpy(st_dir, home);
311   strcat(st_dir, "/.supertux");
312
313   /* Remove .supertux config-file from old SuperTux versions */
314   if(faccessible(st_dir))
315     {
316       remove
317         (st_dir);
318     }
319
320   st_save_dir = (char *) malloc(sizeof(char) * (strlen(st_dir) + strlen("/save") + 1));
321
322   strcpy(st_save_dir,st_dir);
323   strcat(st_save_dir,"/save");
324
325   /* Create them. In the case they exist they won't destroy anything. */
326   mkdir(st_dir, 0755);
327   mkdir(st_save_dir, 0755);
328
329   sprintf(str, "%s/levels", st_dir);
330   mkdir(str, 0755);
331
332   // User has not that a datadir, so we try some magic
333   if (datadir.empty())
334     {
335 #ifndef WIN32
336       // Detect datadir
337       char exe_file[PATH_MAX];
338       if (readlink("/proc/self/exe", exe_file, PATH_MAX) < 0)
339         {
340           puts("Couldn't read /proc/self/exe, using default path: " DATA_PREFIX);
341           datadir = DATA_PREFIX;
342         }
343       else
344         {
345           std::string exedir = std::string(dirname(exe_file)) + "/";
346           
347           datadir = exedir + "../data/"; // SuperTux run from source dir
348           if (access(datadir.c_str(), F_OK) != 0)
349             {
350               datadir = exedir + "../share/supertux/"; // SuperTux run from PATH
351               if (access(datadir.c_str(), F_OK) != 0) 
352                 { // If all fails, fall back to compiled path
353                   datadir = DATA_PREFIX; 
354                 }
355             }
356         }
357 #else
358   datadir = DATA_PREFIX;
359 #endif
360     }
361   printf("Datadir: %s\n", datadir.c_str());
362 }
363
364 /* Create and setup menus. */
365 void st_menu(void)
366 {
367   main_menu      = new Menu();
368   options_menu   = new Menu();
369   options_keys_menu     = new Menu();
370   options_joystick_menu = new Menu();
371   load_game_menu = new Menu();
372   save_game_menu = new Menu();
373   game_menu      = new Menu();
374   highscore_menu = new Menu();
375   contrib_menu   = new Menu();
376   contrib_subset_menu   = new Menu();
377   worldmap_menu  = new Menu();
378
379   main_menu->set_pos(screen->w/2, 335);
380   main_menu->additem(MN_GOTO, "Start Game",0,load_game_menu, MNID_STARTGAME);
381   main_menu->additem(MN_GOTO, "Contrib Levels",0,contrib_menu, MNID_CONTRIB);
382   main_menu->additem(MN_GOTO, "Options",0,options_menu, MNID_OPTIONMENU);
383   main_menu->additem(MN_ACTION,"Level editor",0,0, MNID_LEVELEDITOR);
384   main_menu->additem(MN_ACTION,"Credits",0,0, MNID_CREDITS);
385   main_menu->additem(MN_ACTION,"Quit",0,0, MNID_QUITMAINMENU);
386
387   options_menu->additem(MN_LABEL,"Options",0,0);
388   options_menu->additem(MN_HL,"",0,0);
389 #ifndef NOOPENGL
390   options_menu->additem(MN_TOGGLE,"OpenGL",use_gl,0, MNID_OPENGL);
391 #else
392   options_menu->additem(MN_DEACTIVE,"OpenGL (not supported)",use_gl,MNID_OPENGL);
393 #endif
394   options_menu->additem(MN_TOGGLE,"Fullscreen",use_fullscreen,0, MNID_FULLSCREEN);
395   if(audio_device)
396     {
397       options_menu->additem(MN_TOGGLE,"Sound     ",use_sound,0, MNID_SOUND);
398       options_menu->additem(MN_TOGGLE,"Music     ",use_music,0, MNID_MUSIC);
399     }
400   else
401     {
402       options_menu->additem(MN_DEACTIVE,"Sound     ",use_sound,0, MNID_SOUND);
403       options_menu->additem(MN_DEACTIVE,"Music     ",use_music,0, MNID_MUSIC);
404     }
405   options_menu->additem(MN_TOGGLE,"Show FPS  ",show_fps,0, MNID_SHOWFPS);
406   options_menu->additem(MN_GOTO,"Key Setup",0,options_keys_menu);
407   if(use_joystick)
408     options_menu->additem(MN_GOTO,"Joystick Setup",0,options_joystick_menu);
409   options_menu->additem(MN_HL,"",0,0);
410   options_menu->additem(MN_BACK,"Back",0,0);
411   
412   options_keys_menu->additem(MN_LABEL,"Key Setup",0,0);
413   options_keys_menu->additem(MN_HL,"",0,0);
414   options_keys_menu->additem(MN_CONTROLFIELD,"Left move", 0,0, 0,&keymap.left);
415   options_keys_menu->additem(MN_CONTROLFIELD,"Right move", 0,0, 0,&keymap.right);
416   options_keys_menu->additem(MN_CONTROLFIELD,"Jump", 0,0, 0,&keymap.jump);
417   options_keys_menu->additem(MN_CONTROLFIELD,"Duck", 0,0, 0,&keymap.duck);
418   options_keys_menu->additem(MN_CONTROLFIELD,"Power", 0,0, 0,&keymap.fire);
419   options_keys_menu->additem(MN_HL,"",0,0);
420   options_keys_menu->additem(MN_BACK,"Back",0,0);
421
422   if(use_joystick)
423     {
424     options_joystick_menu->additem(MN_LABEL,"Joystick Setup",0,0);
425     options_joystick_menu->additem(MN_HL,"",0,0);
426     options_joystick_menu->additem(MN_CONTROLFIELD,"X axis", 0,0, 0,&joystick_keymap.x_axis);
427     options_joystick_menu->additem(MN_CONTROLFIELD,"Y axis", 0,0, 0,&joystick_keymap.y_axis);
428     options_joystick_menu->additem(MN_CONTROLFIELD,"A button", 0,0, 0,&joystick_keymap.a_button);
429     options_joystick_menu->additem(MN_CONTROLFIELD,"B button", 0,0, 0,&joystick_keymap.b_button);
430     options_joystick_menu->additem(MN_CONTROLFIELD,"Start", 0,0, 0,&joystick_keymap.start_button);
431     options_joystick_menu->additem(MN_CONTROLFIELD,"DeadZone", 0,0, 0,&joystick_keymap.dead_zone);
432     options_joystick_menu->additem(MN_HL,"",0,0);
433     options_joystick_menu->additem(MN_BACK,"Back",0,0);
434     }
435   
436   load_game_menu->additem(MN_LABEL,"Start Game",0,0);
437   load_game_menu->additem(MN_HL,"",0,0);
438   load_game_menu->additem(MN_DEACTIVE,"Slot 1",0,0);
439   load_game_menu->additem(MN_DEACTIVE,"Slot 2",0,0);
440   load_game_menu->additem(MN_DEACTIVE,"Slot 3",0,0);
441   load_game_menu->additem(MN_DEACTIVE,"Slot 4",0,0);
442   load_game_menu->additem(MN_DEACTIVE,"Slot 5",0,0);
443   load_game_menu->additem(MN_HL,"",0,0);
444   load_game_menu->additem(MN_BACK,"Back",0,0);
445
446   save_game_menu->additem(MN_LABEL,"Save Game",0,0);
447   save_game_menu->additem(MN_HL,"",0,0);
448   save_game_menu->additem(MN_DEACTIVE,"Slot 1",0,0);
449   save_game_menu->additem(MN_DEACTIVE,"Slot 2",0,0);
450   save_game_menu->additem(MN_DEACTIVE,"Slot 3",0,0);
451   save_game_menu->additem(MN_DEACTIVE,"Slot 4",0,0);
452   save_game_menu->additem(MN_DEACTIVE,"Slot 5",0,0);
453   save_game_menu->additem(MN_HL,"",0,0);
454   save_game_menu->additem(MN_BACK,"Back",0,0);
455
456   game_menu->additem(MN_LABEL,"Pause",0,0);
457   game_menu->additem(MN_HL,"",0,0);
458   game_menu->additem(MN_ACTION,"Continue",0,0,MNID_CONTINUE);
459   game_menu->additem(MN_GOTO,"Options",0,options_menu);
460   game_menu->additem(MN_HL,"",0,0);
461   game_menu->additem(MN_ACTION,"Abort Level",0,0,MNID_ABORTLEVEL);
462
463   worldmap_menu->additem(MN_LABEL,"Pause",0,0);
464   worldmap_menu->additem(MN_HL,"",0,0);
465   worldmap_menu->additem(MN_ACTION,"Continue",0,0,MNID_RETURNWORLDMAP);
466   worldmap_menu->additem(MN_ACTION,"Save",0,0,MNID_SAVEGAME);
467   worldmap_menu->additem(MN_GOTO,"Options",0,options_menu);
468   worldmap_menu->additem(MN_HL,"",0,0);
469   worldmap_menu->additem(MN_ACTION,"Quit Game",0,0,MNID_QUITWORLDMAP);
470
471   highscore_menu->additem(MN_TEXTFIELD,"Enter your name:",0,0);
472 }
473
474 void update_load_save_game_menu(Menu* pmenu)
475 {
476   for(int i = 2; i < 7; ++i)
477     {
478       // FIXME: Insert a real savegame struct/class here instead of
479       // doing string vodoo
480       std::string tmp = slotinfo(i - 1);
481       pmenu->item[i].kind = MN_ACTION;
482       pmenu->item[i].change_text(tmp.c_str());
483     }
484 }
485
486 bool process_load_game_menu()
487 {
488   int slot = load_game_menu->check();
489
490   if(slot != -1 && load_game_menu->get_item(slot).kind == MN_ACTION)
491     {
492       WorldMapNS::WorldMap worldmap;
493
494       char slotfile[1024];
495       snprintf(slotfile, 1024, "%s/slot%d.stsg", st_save_dir, slot-1);
496      
497       // Load the game or at least set the savegame_file variable
498       worldmap.loadgame(slotfile);
499
500       worldmap.display();
501       
502       Menu::set_current(main_menu);
503
504       st_pause_ticks_stop();
505       return true;
506     }
507   else
508     {
509       return false;
510     }
511 }
512
513 /* Handle changes made to global settings in the options menu. */
514 void process_options_menu(void)
515 {
516   switch (options_menu->check())
517     {
518     case MNID_OPENGL:
519 #ifndef NOOPENGL
520       if(use_gl != options_menu->item[MNID_OPENGL].toggled)
521         {
522           use_gl = !use_gl;
523           st_video_setup();
524         }
525 #else
526       options_menu->item[MNID_OPENGL].toggled = false;
527 #endif
528       break;
529     case MNID_FULLSCREEN:
530       if(use_fullscreen != options_menu->item[MNID_FULLSCREEN].toggled)
531         {
532           use_fullscreen = !use_fullscreen;
533           st_video_setup();
534         }
535       break;
536     case MNID_SOUND:
537       if(use_sound != options_menu->item[MNID_SOUND].toggled)
538         use_sound = !use_sound;
539       break;
540     case MNID_MUSIC:
541       music_manager->enable_music(options_menu->item[MNID_MUSIC].toggled);
542       break;
543     case MNID_SHOWFPS:
544       if(show_fps != options_menu->item[MNID_SHOWFPS].toggled)
545         show_fps = !show_fps;
546       break;
547     }
548 }
549
550 void st_general_setup(void)
551 {
552   /* Seed random number generator: */
553
554   srand(SDL_GetTicks());
555
556   /* Set icon image: */
557
558   seticon();
559
560   /* Unicode needed for input handling: */
561
562   SDL_EnableUNICODE(1);
563
564   /* Load global images: */
565
566   black_text  = new Text(datadir + "/images/status/letters-black.png", TEXT_TEXT, 16,18);
567   gold_text   = new Text(datadir + "/images/status/letters-gold.png", TEXT_TEXT, 16,18);
568   blue_text   = new Text(datadir + "/images/status/letters-blue.png", TEXT_TEXT, 16,18);
569   red_text    = new Text(datadir + "/images/status/letters-red.png", TEXT_TEXT, 16,18);
570   white_text  = new Text(datadir + "/images/status/letters-white.png", TEXT_TEXT, 16,18);
571   white_small_text = new Text(datadir + "/images/status/letters-white-small.png", TEXT_TEXT, 8,9);
572   white_big_text   = new Text(datadir + "/images/status/letters-white-big.png", TEXT_TEXT, 20,23);
573   yellow_nums = new Text(datadir + "/images/status/numbers.png", TEXT_NUM, 32,32);
574
575   /* Load GUI/menu images: */
576   checkbox = new Surface(datadir + "/images/status/checkbox.png", USE_ALPHA);
577   checkbox_checked = new Surface(datadir + "/images/status/checkbox-checked.png", USE_ALPHA);
578   back = new Surface(datadir + "/images/status/back.png", USE_ALPHA);
579   arrow_left = new Surface(datadir + "/images/icons/left.png", USE_ALPHA);
580   arrow_right = new Surface(datadir + "/images/icons/right.png", USE_ALPHA);
581
582   /* Load the mouse-cursor */
583   mouse_cursor = new MouseCursor( datadir + "/images/status/mousecursor.png",1);
584   
585 }
586
587 void st_general_free(void)
588 {
589
590   /* Free global images: */
591
592   delete black_text;
593   delete gold_text;
594   delete white_text;
595   delete blue_text;
596   delete red_text;
597   delete white_small_text;
598   delete white_big_text;
599
600   /* Free GUI/menu images: */
601   delete checkbox;
602   delete checkbox_checked;
603   delete back;
604   delete arrow_left;
605   delete arrow_right;
606
607   /* Free mouse-cursor */
608   delete mouse_cursor;
609   
610   /* Free menus */
611   delete main_menu;
612   delete game_menu;
613   delete options_menu;
614   delete highscore_menu;
615   delete save_game_menu;
616   delete load_game_menu;
617 }
618
619 void st_video_setup(void)
620 {
621   if(screen != NULL)
622     SDL_FreeSurface(screen);
623
624   /* Init SDL Video: */
625
626   if (SDL_Init(SDL_INIT_VIDEO) < 0)
627     {
628       fprintf(stderr,
629               "\nError: I could not initialize video!\n"
630               "The Simple DirectMedia error that occured was:\n"
631               "%s\n\n", SDL_GetError());
632       exit(1);
633     }
634
635   /* Open display: */
636   if(use_gl)
637     st_video_setup_gl();
638   else
639     st_video_setup_sdl();
640
641   Surface::reload_all();
642
643   /* Set window manager stuff: */
644   SDL_WM_SetCaption("SuperTux " VERSION, "SuperTux");
645 }
646
647 void st_video_setup_sdl(void)
648 {
649   SDL_FreeSurface(screen);
650
651   if (use_fullscreen)
652     {
653       screen = SDL_SetVideoMode(640, 480, 0, SDL_FULLSCREEN ) ; /* | SDL_HWSURFACE); */
654       if (screen == NULL)
655         {
656           fprintf(stderr,
657                   "\nWarning: I could not set up fullscreen video for "
658                   "640x480 mode.\n"
659                   "The Simple DirectMedia error that occured was:\n"
660                   "%s\n\n", SDL_GetError());
661           use_fullscreen = false;
662         }
663     }
664   else
665     {
666       screen = SDL_SetVideoMode(640, 480, 0, SDL_HWSURFACE | SDL_DOUBLEBUF );
667
668       if (screen == NULL)
669         {
670           fprintf(stderr,
671                   "\nError: I could not set up video for 640x480 mode.\n"
672                   "The Simple DirectMedia error that occured was:\n"
673                   "%s\n\n", SDL_GetError());
674           exit(1);
675         }
676     }
677 }
678
679 void st_video_setup_gl(void)
680 {
681 #ifndef NOOPENGL
682
683   SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
684   SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5);
685   SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
686   SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
687   SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
688
689   if (use_fullscreen)
690     {
691       screen = SDL_SetVideoMode(640, 480, 0, SDL_FULLSCREEN | SDL_OPENGL) ; /* | SDL_HWSURFACE); */
692       if (screen == NULL)
693         {
694           fprintf(stderr,
695                   "\nWarning: I could not set up fullscreen video for "
696                   "640x480 mode.\n"
697                   "The Simple DirectMedia error that occured was:\n"
698                   "%s\n\n", SDL_GetError());
699           use_fullscreen = false;
700         }
701     }
702   else
703     {
704       screen = SDL_SetVideoMode(640, 480, 0, SDL_OPENGL);
705
706       if (screen == NULL)
707         {
708           fprintf(stderr,
709                   "\nError: I could not set up video for 640x480 mode.\n"
710                   "The Simple DirectMedia error that occured was:\n"
711                   "%s\n\n", SDL_GetError());
712           exit(1);
713         }
714     }
715
716   /*
717    * Set up OpenGL for 2D rendering.
718    */
719   glDisable(GL_DEPTH_TEST);
720   glDisable(GL_CULL_FACE);
721
722   glViewport(0, 0, screen->w, screen->h);
723   glMatrixMode(GL_PROJECTION);
724   glLoadIdentity();
725   glOrtho(0, screen->w, screen->h, 0, -1.0, 1.0);
726
727   glMatrixMode(GL_MODELVIEW);
728   glLoadIdentity();
729   glTranslatef(0.0f, 0.0f, 0.0f);
730
731 #endif
732
733 }
734
735 void st_joystick_setup(void)
736 {
737
738   /* Init Joystick: */
739
740   use_joystick = true;
741
742   if (SDL_Init(SDL_INIT_JOYSTICK) < 0)
743     {
744       fprintf(stderr, "Warning: I could not initialize joystick!\n"
745               "The Simple DirectMedia error that occured was:\n"
746               "%s\n\n", SDL_GetError());
747
748       use_joystick = false;
749     }
750   else
751     {
752       /* Open joystick: */
753       if (SDL_NumJoysticks() <= 0)
754         {
755           fprintf(stderr, "Warning: No joysticks are available.\n");
756
757           use_joystick = false;
758         }
759       else
760         {
761           js = SDL_JoystickOpen(joystick_num);
762
763           if (js == NULL)
764             {
765               fprintf(stderr, "Warning: Could not open joystick %d.\n"
766                       "The Simple DirectMedia error that occured was:\n"
767                       "%s\n\n", joystick_num, SDL_GetError());
768
769               use_joystick = false;
770             }
771           else
772             {
773               if (SDL_JoystickNumAxes(js) < 2)
774                 {
775                   fprintf(stderr,
776                           "Warning: Joystick does not have enough axes!\n");
777
778                   use_joystick = false;
779                 }
780               else
781                 {
782                   if (SDL_JoystickNumButtons(js) < 2)
783                     {
784                       fprintf(stderr,
785                               "Warning: "
786                               "Joystick does not have enough buttons!\n");
787
788                       use_joystick = false;
789                     }
790                 }
791             }
792         }
793     }
794 }
795
796 void st_audio_setup(void)
797 {
798
799   /* Init SDL Audio silently even if --disable-sound : */
800
801   if (audio_device)
802     {
803       if (SDL_Init(SDL_INIT_AUDIO) < 0)
804         {
805           /* only print out message if sound or music
806              was not disabled at command-line
807            */
808           if (use_sound || use_music)
809             {
810               fprintf(stderr,
811                       "\nWarning: I could not initialize audio!\n"
812                       "The Simple DirectMedia error that occured was:\n"
813                       "%s\n\n", SDL_GetError());
814             }
815           /* keep the programming logic the same :-)
816              because in this case, use_sound & use_music' values are ignored
817              when there's no available audio device
818           */
819           use_sound = false;
820           use_music = false;
821           audio_device = false;
822         }
823     }
824
825
826   /* Open sound silently regarless the value of "use_sound": */
827
828   if (audio_device)
829     {
830       if (open_audio(44100, AUDIO_S16, 2, 2048) < 0)
831         {
832           /* only print out message if sound or music
833              was not disabled at command-line
834            */
835           if (use_sound || use_music)
836             {
837               fprintf(stderr,
838                       "\nWarning: I could not set up audio for 44100 Hz "
839                       "16-bit stereo.\n"
840                       "The Simple DirectMedia error that occured was:\n"
841                       "%s\n\n", SDL_GetError());
842             }
843           use_sound = false;
844           use_music = false;
845           audio_device = false;
846         }
847     }
848
849 }
850
851
852 /* --- SHUTDOWN --- */
853
854 void st_shutdown(void)
855 {
856   close_audio();
857   SDL_Quit();
858   saveconfig();
859 }
860
861 /* --- ABORT! --- */
862
863 void st_abort(const std::string& reason, const std::string& details)
864 {
865   fprintf(stderr, "\nError: %s\n%s\n\n", reason.c_str(), details.c_str());
866   st_shutdown();
867   abort();
868 }
869
870
871 /* Set Icon (private) */
872
873 void seticon(void)
874 {
875 //  int masklen;
876 //  Uint8 * mask;
877   SDL_Surface * icon;
878
879
880   /* Load icon into a surface: */
881
882   icon = IMG_Load((datadir + "/images/icon.xpm").c_str());
883   if (icon == NULL)
884     {
885       fprintf(stderr,
886               "\nError: I could not load the icon image: %s%s\n"
887               "The Simple DirectMedia error that occured was:\n"
888               "%s\n\n", datadir.c_str(), "/images/icon.xpm", SDL_GetError());
889       exit(1);
890     }
891
892
893   /* Create mask: */
894 /*
895   masklen = (((icon -> w) + 7) / 8) * (icon -> h);
896   mask = (Uint8*) malloc(masklen * sizeof(Uint8));
897   memset(mask, 0xFF, masklen);
898 */
899
900   /* Set icon: */
901
902   SDL_WM_SetIcon(icon, NULL);//mask);
903
904
905   /* Free icon surface & mask: */
906
907 //  free(mask);
908   SDL_FreeSurface(icon);
909 }
910
911
912 /* Parse command-line arguments: */
913
914 void parseargs(int argc, char * argv[])
915 {
916   int i;
917
918   loadconfig();
919
920   /* Parse arguments: */
921
922   for (i = 1; i < argc; i++)
923     {
924       if (strcmp(argv[i], "--fullscreen") == 0 ||
925           strcmp(argv[i], "-f") == 0)
926         {
927           /* Use full screen: */
928
929           use_fullscreen = true;
930         }
931       else if (strcmp(argv[i], "--joystick") == 0 || strcmp(argv[i], "-j") == 0)
932         {
933           assert(i+1 < argc);
934           joystick_num = atoi(argv[++i]);
935         }
936       else if (strcmp(argv[i], "--joymap") == 0)
937         {
938           assert(i+1 < argc);
939           if (sscanf(argv[++i],
940                      "%d:%d:%d:%d:%d", 
941                      &joystick_keymap.x_axis, 
942                      &joystick_keymap.y_axis, 
943                      &joystick_keymap.a_button, 
944                      &joystick_keymap.b_button, 
945                      &joystick_keymap.start_button) != 5)
946             {
947               puts("Warning: Invalid or incomplete joymap, should be: 'XAXIS:YAXIS:A:B:START'");
948             }
949           else
950             {
951               std::cout << "Using new joymap:\n"
952                         << "  X-Axis:       " << joystick_keymap.x_axis << "\n"
953                         << "  Y-Axis:       " << joystick_keymap.y_axis << "\n"
954                         << "  A-Button:     " << joystick_keymap.a_button << "\n"
955                         << "  B-Button:     " << joystick_keymap.b_button << "\n"
956                         << "  Start-Button: " << joystick_keymap.start_button << std::endl;
957             }
958         }
959       else if (strcmp(argv[i], "--worldmap") == 0)
960         {
961           launch_worldmap_mode = true;
962         }
963       else if (strcmp(argv[i], "--datadir") == 0 
964                || strcmp(argv[i], "-d") == 0 )
965         {
966           assert(i+1 < argc);
967           datadir = argv[++i];
968         }
969       else if (strcmp(argv[i], "--show-fps") == 0)
970         {
971           /* Use full screen: */
972
973           show_fps = true;
974         }
975       else if (strcmp(argv[i], "--opengl") == 0 ||
976                strcmp(argv[i], "-gl") == 0)
977         {
978 #ifndef NOOPENGL
979           /* Use OpengGL: */
980
981           use_gl = true;
982 #endif
983         }
984       else if (strcmp(argv[i], "--sdl") == 0)
985           {
986             use_gl = false;
987           }
988       else if (strcmp(argv[i], "--usage") == 0)
989         {
990           /* Show usage: */
991
992           usage(argv[0], 0);
993         }
994       else if (strcmp(argv[i], "--version") == 0)
995         {
996           /* Show version: */
997           printf("SuperTux " VERSION "\n");
998           exit(0);
999         }
1000       else if (strcmp(argv[i], "--disable-sound") == 0)
1001         {
1002           /* Disable the compiled in sound feature */
1003           printf("Sounds disabled \n");
1004           use_sound = false;
1005           audio_device = false;
1006         }
1007       else if (strcmp(argv[i], "--disable-music") == 0)
1008         {
1009           /* Disable the compiled in sound feature */
1010           printf("Music disabled \n");
1011           use_music = false;
1012         }
1013       else if (strcmp(argv[i], "--debug-mode") == 0)
1014         {
1015           /* Enable the debug-mode */
1016           debug_mode = true;
1017
1018         }
1019       else if (strcmp(argv[i], "--help") == 0)
1020         {     /* Show help: */
1021           puts("Super Tux " VERSION "\n"
1022                "  Please see the file \"README.txt\" for more details.\n");
1023           printf("Usage: %s [OPTIONS] FILENAME\n\n", argv[0]);
1024           puts("Display Options:\n"
1025                "  --fullscreen        Run in fullscreen mode.\n"
1026                "  --opengl            If opengl support was compiled in, this will enable\n"
1027                "                      the EXPERIMENTAL OpenGL mode.\n"
1028                "  --sdl               Use non-opengl renderer\n"
1029                "\n"
1030                "Sound Options:\n"
1031                "  --disable-sound     If sound support was compiled in,  this will\n"
1032                "                      disable sound for this session of the game.\n"
1033                "  --disable-music     Like above, but this will disable music.\n"
1034                "\n"
1035                "Misc Options:\n"
1036                "  -j, --joystick NUM  Use joystick NUM (default: 0)\n" 
1037                "  --joymap XAXIS:YAXIS:A:B:START\n"
1038                "                      Define how joystick buttons and axis should be mapped\n"
1039                "  --worldmap          Start in worldmap-mode (EXPERIMENTAL)\n"          
1040                "  -d, --datadir DIR   Load Game data from DIR (default: automatic)\n"
1041                "  --debug-mode        Enables the debug-mode, which is useful for developers.\n"
1042                "  --help              Display a help message summarizing command-line\n"
1043                "                      options, license and game controls.\n"
1044                "  --usage             Display a brief message summarizing command-line options.\n"
1045                "  --version           Display the version of SuperTux you're running.\n\n"
1046                );
1047           exit(0);
1048         }
1049       else if (argv[i][0] != '-')
1050         {
1051           level_startup_file = argv[i];
1052         }
1053       else
1054         {
1055           /* Unknown - complain! */
1056
1057           usage(argv[0], 1);
1058         }
1059     }
1060 }
1061
1062
1063 /* Display usage: */
1064
1065 void usage(char * prog, int ret)
1066 {
1067   FILE * fi;
1068
1069
1070   /* Determine which stream to write to: */
1071
1072   if (ret == 0)
1073     fi = stdout;
1074   else
1075     fi = stderr;
1076
1077
1078   /* Display the usage message: */
1079
1080   fprintf(fi, "Usage: %s [--fullscreen] [--opengl] [--disable-sound] [--disable-music] [--debug-mode] | [--usage | --help | --version] [--worldmap] FILENAME\n",
1081           prog);
1082
1083
1084   /* Quit! */
1085
1086   exit(ret);
1087 }
1088