3 // SuperTux - A Jump'n Run
4 // Copyright (C) 2000 Bill Kendrick <bill@newbreedsoftware.com>
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.
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.
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.
30 #include "SDL_image.h"
32 #include "SDL_opengl.h"
36 #include <sys/types.h>
44 #include "app/globals.h"
45 #include "app/defines.h"
46 #include "app/setup.h"
47 #include "video/screen.h"
48 #include "video/surface.h"
50 #include "utils/configfile.h"
51 #include "audio/sound_manager.h"
52 #include "app/gettext.h"
56 #define mkdir(dir, mode) mkdir(dir)
57 // on win32 we typically don't want LFS paths
59 #define DATA_PREFIX "./data/"
62 /* Screen proprities: */
63 /* Don't use this to test for the actual screen sizes. Use screen->w/h instead! */
67 /* Local function prototypes: */
70 void usage(char * prog, int ret);
72 /* Does the given file exist and is it accessible? */
73 int faccessible(const char *filename)
76 if (stat(filename, &filestat) == -1)
82 if(S_ISREG(filestat.st_mode))
89 /* Can we write to this location? */
90 int fwriteable(const char *filename)
93 fi = fopen(filename, "wa");
101 /* Makes sure a directory is created in either the SuperTux home directory or the SuperTux base directory.*/
102 int fcreatedir(const char* relative_dir)
105 snprintf(path, 1024, "%s/%s/", st_dir, relative_dir);
106 if(mkdir(path,0755) != 0)
108 snprintf(path, 1024, "%s/%s/", datadir.c_str(), relative_dir);
109 if(mkdir(path,0755) != 0)
124 FILE * opendata(const char * rel_filename, const char * mode)
126 char * filename = NULL;
129 filename = (char *) malloc(sizeof(char) * (strlen(st_dir) +
130 strlen(rel_filename) + 1));
132 strcpy(filename, st_dir);
133 /* Open the high score file: */
135 strcat(filename, rel_filename);
137 /* Try opening the file: */
138 fi = fopen(filename, mode);
142 fprintf(stderr, "Warning: Unable to open the file \"%s\" ", filename);
144 if (strcmp(mode, "r") == 0)
145 fprintf(stderr, "for read!!!\n");
146 else if (strcmp(mode, "w") == 0)
147 fprintf(stderr, "for write!!!\n");
154 /* Get all names of sub-directories in a certain directory. */
155 /* Returns the number of sub-directories found. */
156 /* Note: The user has to free the allocated space. */
157 string_list_type dsubdirs(const char *rel_path,const char* expected_file)
160 struct dirent *direntp;
161 string_list_type sdirs;
165 string_list_init(&sdirs);
166 sprintf(path,"%s/%s",st_dir,rel_path);
167 if((dirStructP = opendir(path)) != NULL)
169 while((direntp = readdir(dirStructP)) != NULL)
171 char absolute_filename[1024];
174 sprintf(absolute_filename, "%s/%s", path, direntp->d_name);
176 if (stat(absolute_filename, &buf) == 0 && S_ISDIR(buf.st_mode))
178 if(expected_file != NULL)
180 sprintf(filename,"%s/%s/%s",path,direntp->d_name,expected_file);
181 if(!faccessible(filename))
185 string_list_add_item(&sdirs,direntp->d_name);
188 closedir(dirStructP);
191 sprintf(path,"%s/%s",datadir.c_str(),rel_path);
192 if((dirStructP = opendir(path)) != NULL)
194 while((direntp = readdir(dirStructP)) != NULL)
196 char absolute_filename[1024];
199 sprintf(absolute_filename, "%s/%s", path, direntp->d_name);
201 if (stat(absolute_filename, &buf) == 0 && S_ISDIR(buf.st_mode))
203 if(expected_file != NULL)
205 sprintf(filename,"%s/%s/%s",path,direntp->d_name,expected_file);
206 if(!faccessible(filename))
212 sprintf(filename,"%s/%s/%s/%s",st_dir,rel_path,direntp->d_name,expected_file);
213 if(faccessible(filename))
218 string_list_add_item(&sdirs,direntp->d_name);
221 closedir(dirStructP);
227 string_list_type dfiles(const char *rel_path, const char* glob, const char* exception_str)
230 struct dirent *direntp;
231 string_list_type sdirs;
234 string_list_init(&sdirs);
235 sprintf(path,"%s/%s",st_dir,rel_path);
236 if((dirStructP = opendir(path)) != NULL)
238 while((direntp = readdir(dirStructP)) != NULL)
240 char absolute_filename[1024];
243 sprintf(absolute_filename, "%s/%s", path, direntp->d_name);
245 if (stat(absolute_filename, &buf) == 0 && S_ISREG(buf.st_mode))
247 if(exception_str != NULL)
249 if(strstr(direntp->d_name,exception_str) != NULL)
253 if(strstr(direntp->d_name,glob) == NULL)
256 string_list_add_item(&sdirs,direntp->d_name);
259 closedir(dirStructP);
262 sprintf(path,"%s/%s",datadir.c_str(),rel_path);
263 if((dirStructP = opendir(path)) != NULL)
265 while((direntp = readdir(dirStructP)) != NULL)
267 char absolute_filename[1024];
270 sprintf(absolute_filename, "%s/%s", path, direntp->d_name);
272 if (stat(absolute_filename, &buf) == 0 && S_ISREG(buf.st_mode))
274 if(exception_str != NULL)
276 if(strstr(direntp->d_name,exception_str) != NULL)
280 if(strstr(direntp->d_name,glob) == NULL)
283 string_list_add_item(&sdirs,direntp->d_name);
286 closedir(dirStructP);
292 void free_strings(char **strings, int num)
295 for(i=0; i < num; ++i)
300 /* Set SuperTux configuration and save directories */
301 void st_directory_setup(void)
305 /* Get home directory (from $HOME variable)... if we can't determine it,
306 use the current directory ("."): */
307 if (getenv("HOME") != NULL)
308 home = getenv("HOME");
312 st_dir = (char *) malloc(sizeof(char) * (strlen(home) +
313 strlen("/.supertux") + 1));
314 strcpy(st_dir, home);
315 strcat(st_dir, "/.supertux");
317 /* Remove .supertux config-file from old SuperTux versions */
318 if(faccessible(st_dir))
324 st_save_dir = (char *) malloc(sizeof(char) * (strlen(st_dir) + strlen("/save") + 1));
326 strcpy(st_save_dir,st_dir);
327 strcat(st_save_dir,"/save");
329 /* Create them. In the case they exist they won't destroy anything. */
331 mkdir(st_save_dir, 0755);
333 sprintf(str, "%s/levels", st_dir);
336 // User has not that a datadir, so we try some magic
341 char exe_file[PATH_MAX];
342 if (readlink("/proc/self/exe", exe_file, PATH_MAX) < 0)
344 puts("Couldn't read /proc/self/exe, using default path: " DATA_PREFIX);
345 datadir = DATA_PREFIX;
349 std::string exedir = std::string(dirname(exe_file)) + "/";
351 datadir = exedir + "../data"; // SuperTux run from source dir
352 if (access(datadir.c_str(), F_OK) != 0)
354 datadir = exedir + "../share/supertux"; // SuperTux run from PATH
355 if (access(datadir.c_str(), F_OK) != 0)
356 { // If all fails, fall back to compiled path
357 datadir = DATA_PREFIX;
362 datadir = DATA_PREFIX;
365 printf("Datadir: %s\n", datadir.c_str());
368 void st_general_setup(void)
370 /* Seed random number generator: */
372 srand(SDL_GetTicks());
374 /* Set icon image: */
378 /* Unicode needed for input handling: */
380 SDL_EnableUNICODE(1);
382 /* Load global images: */
383 gold_text = new Font(datadir + "/images/fonts/gold.png", Font::TEXT, 16,18);
384 blue_text = new Font(datadir + "/images/fonts/blue.png", Font::TEXT, 16,18,3);
385 white_text = new Font(datadir + "/images/fonts/white.png",
387 gray_text = new Font(datadir + "/images/fonts/gray.png",
389 white_small_text = new Font(datadir + "/images/fonts/white-small.png",
391 white_big_text = new Font(datadir + "/images/fonts/white-big.png",
392 Font::TEXT, 20,22, 3);
393 yellow_nums = new Font(datadir + "/images/fonts/numbers.png",
396 /* Load GUI/menu images: */
397 checkbox = new Surface(datadir + "/images/status/checkbox.png", true);
398 checkbox_checked = new Surface(datadir + "/images/status/checkbox-checked.png", true);
399 back = new Surface(datadir + "/images/status/back.png", true);
400 arrow_left = new Surface(datadir + "/images/icons/left.png", true);
401 arrow_right = new Surface(datadir + "/images/icons/right.png", true);
403 /* Load the mouse-cursor */
404 mouse_cursor = new MouseCursor( datadir + "/images/status/mousecursor.png",1);
405 MouseCursor::set_current(mouse_cursor);
409 void st_general_free(void)
412 /* Free global images: */
417 delete white_small_text;
418 delete white_big_text;
421 /* Free GUI/menu images: */
423 delete checkbox_checked;
428 /* Free mouse-cursor */
435 delete options_keys_menu;
436 delete options_joystick_menu;
437 delete highscore_menu;
439 delete contrib_subset_menu;
440 delete save_game_menu;
441 delete load_game_menu;
444 void st_video_setup(void)
446 /* Init SDL Video: */
447 if (SDL_Init(SDL_INIT_VIDEO) < 0)
450 "\nError: I could not initialize video!\n"
451 "The Simple DirectMedia error that occured was:\n"
452 "%s\n\n", SDL_GetError());
460 st_video_setup_sdl();
462 Surface::reload_all();
464 /* Set window manager stuff: */
465 SDL_WM_SetCaption("SuperTux " VERSION, "SuperTux");
468 void st_video_setup_sdl(void)
472 screen = SDL_SetVideoMode(SCREEN_W, SCREEN_H, 0, SDL_FULLSCREEN ) ; /* | SDL_HWSURFACE); */
476 "\nWarning: I could not set up fullscreen video for "
478 "The Simple DirectMedia error that occured was:\n"
479 "%s\n\n", SDL_GetError());
480 use_fullscreen = false;
485 screen = SDL_SetVideoMode(SCREEN_W, SCREEN_H, 0, SDL_HWSURFACE | SDL_DOUBLEBUF );
490 "\nError: I could not set up video for 800x600 mode.\n"
491 "The Simple DirectMedia error that occured was:\n"
492 "%s\n\n", SDL_GetError());
498 void st_video_setup_gl(void)
502 SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
503 SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5);
504 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
505 SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
506 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
510 screen = SDL_SetVideoMode(SCREEN_W, SCREEN_H, 0, SDL_FULLSCREEN | SDL_OPENGL) ; /* | SDL_HWSURFACE); */
514 "\nWarning: I could not set up fullscreen video for "
516 "The Simple DirectMedia error that occured was:\n"
517 "%s\n\n", SDL_GetError());
518 use_fullscreen = false;
523 screen = SDL_SetVideoMode(SCREEN_W, SCREEN_H, 0, SDL_OPENGL);
528 "\nError: I could not set up video for 640x480 mode.\n"
529 "The Simple DirectMedia error that occured was:\n"
530 "%s\n\n", SDL_GetError());
536 * Set up OpenGL for 2D rendering.
538 glDisable(GL_DEPTH_TEST);
539 glDisable(GL_CULL_FACE);
541 glViewport(0, 0, screen->w, screen->h);
542 glMatrixMode(GL_PROJECTION);
544 glOrtho(0, screen->w, screen->h, 0, -1.0, 1.0);
546 glMatrixMode(GL_MODELVIEW);
548 glTranslatef(0.0f, 0.0f, 0.0f);
554 void st_joystick_setup(void)
561 if (SDL_Init(SDL_INIT_JOYSTICK) < 0)
563 fprintf(stderr, "Warning: I could not initialize joystick!\n"
564 "The Simple DirectMedia error that occured was:\n"
565 "%s\n\n", SDL_GetError());
567 use_joystick = false;
572 if (SDL_NumJoysticks() <= 0)
574 fprintf(stderr, "Info: No joysticks were found.\n");
576 use_joystick = false;
580 js = SDL_JoystickOpen(joystick_num);
584 fprintf(stderr, "Warning: Could not open joystick %d.\n"
585 "The Simple DirectMedia error that occured was:\n"
586 "%s\n\n", joystick_num, SDL_GetError());
588 use_joystick = false;
592 if (SDL_JoystickNumAxes(js) < 2)
595 "Warning: Joystick does not have enough axes!\n");
597 use_joystick = false;
601 if (SDL_JoystickNumButtons(js) < 2)
605 "Joystick does not have enough buttons!\n");
607 use_joystick = false;
615 void st_audio_setup(void)
618 /* Init SDL Audio silently even if --disable-sound : */
622 if (SDL_Init(SDL_INIT_AUDIO) < 0)
624 /* only print out message if sound or music
625 was not disabled at command-line
627 if (use_sound || use_music)
630 "\nWarning: I could not initialize audio!\n"
631 "The Simple DirectMedia error that occured was:\n"
632 "%s\n\n", SDL_GetError());
634 /* keep the programming logic the same :-)
635 because in this case, use_sound & use_music' values are ignored
636 when there's no available audio device
640 audio_device = false;
645 /* Open sound silently regarless the value of "use_sound": */
649 if (open_audio(44100, AUDIO_S16, 2, 2048) < 0)
651 /* only print out message if sound or music
652 was not disabled at command-line
654 if (use_sound || use_music)
657 "\nWarning: I could not set up audio for 44100 Hz "
659 "The Simple DirectMedia error that occured was:\n"
660 "%s\n\n", SDL_GetError());
664 audio_device = false;
671 /* --- SHUTDOWN --- */
673 void st_shutdown(void)
682 void st_abort(const std::string& reason, const std::string& details)
684 fprintf(stderr, "\nError: %s\n%s\n\n", reason.c_str(), details.c_str());
689 /* Set Icon (private) */
698 /* Load icon into a surface: */
700 icon = IMG_Load((datadir + "/images/supertux.xpm").c_str());
704 "\nError: I could not load the icon image: %s%s\n"
705 "The Simple DirectMedia error that occured was:\n"
706 "%s\n\n", datadir.c_str(), "/images/supertux.xpm", SDL_GetError());
713 masklen = (((icon -> w) + 7) / 8) * (icon -> h);
714 mask = (Uint8*) malloc(masklen * sizeof(Uint8));
715 memset(mask, 0xFF, masklen);
720 SDL_WM_SetIcon(icon, NULL);//mask);
723 /* Free icon surface & mask: */
726 SDL_FreeSurface(icon);
730 /* Parse command-line arguments: */
732 void parseargs(int argc, char * argv[])
738 /* Parse arguments: */
740 for (i = 1; i < argc; i++)
742 if (strcmp(argv[i], "--fullscreen") == 0 ||
743 strcmp(argv[i], "-f") == 0)
745 use_fullscreen = true;
747 else if (strcmp(argv[i], "--window") == 0 ||
748 strcmp(argv[i], "-w") == 0)
750 use_fullscreen = false;
752 else if (strcmp(argv[i], "--joystick") == 0 || strcmp(argv[i], "-j") == 0)
755 joystick_num = atoi(argv[++i]);
757 else if (strcmp(argv[i], "--joymap") == 0)
760 if (sscanf(argv[++i],
762 &joystick_keymap.x_axis,
763 &joystick_keymap.y_axis,
764 &joystick_keymap.a_button,
765 &joystick_keymap.b_button,
766 &joystick_keymap.start_button) != 5)
768 puts("Warning: Invalid or incomplete joymap, should be: 'XAXIS:YAXIS:A:B:START'");
772 std::cout << "Using new joymap:\n"
773 << " X-Axis: " << joystick_keymap.x_axis << "\n"
774 << " Y-Axis: " << joystick_keymap.y_axis << "\n"
775 << " A-Button: " << joystick_keymap.a_button << "\n"
776 << " B-Button: " << joystick_keymap.b_button << "\n"
777 << " Start-Button: " << joystick_keymap.start_button << std::endl;
780 else if (strcmp(argv[i], "--leveleditor") == 0)
782 launch_leveleditor_mode = true;
784 else if (strcmp(argv[i], "--worldmap") == 0)
786 launch_worldmap_mode = true;
788 else if (strcmp(argv[i], "--datadir") == 0
789 || strcmp(argv[i], "-d") == 0 )
794 else if (strcmp(argv[i], "--show-fps") == 0)
796 /* Use full screen: */
800 else if (strcmp(argv[i], "--opengl") == 0 ||
801 strcmp(argv[i], "-gl") == 0)
809 else if (strcmp(argv[i], "--sdl") == 0)
813 else if (strcmp(argv[i], "--usage") == 0)
819 else if (strcmp(argv[i], "--version") == 0)
822 printf("SuperTux " VERSION "\n");
825 else if (strcmp(argv[i], "--disable-sound") == 0)
827 /* Disable the compiled in sound feature */
828 printf("Sounds disabled \n");
830 audio_device = false;
832 else if (strcmp(argv[i], "--disable-music") == 0)
834 /* Disable the compiled in sound feature */
835 printf("Music disabled \n");
838 else if (strcmp(argv[i], "--debug") == 0)
840 /* Enable the debug-mode */
844 else if (strcmp(argv[i], "--help") == 0)
846 puts(_(" SuperTux " VERSION "\n"
847 " Please see the file \"README.txt\" for more details.\n"));
848 printf(_("Usage: %s [OPTIONS] FILENAME\n\n"), argv[0]);
849 puts(_("Display Options:\n"
850 " -f, --fullscreen Run in fullscreen mode.\n"
851 " -w, --window Run in window mode.\n"
852 " --opengl If OpenGL support was compiled in, this will tell\n"
853 " SuperTux to make use of it.\n"
854 " --sdl Use the SDL software graphical renderer\n"
857 " --disable-sound If sound support was compiled in, this will\n"
858 " disable sound for this session of the game.\n"
859 " --disable-music Like above, but this will disable music.\n"
862 " -j, --joystick NUM Use joystick NUM (default: 0)\n"
863 " --joymap XAXIS:YAXIS:A:B:START\n"
864 " Define how joystick buttons and axis should be mapped\n"
865 " --leveleditor Opens the leveleditor in a file.\n"
866 " --worldmap Opens the specified worldmap file.\n"
867 " -d, --datadir DIR Load Game data from DIR (default: automatic)\n"
868 " --debug Enables the debug mode, which is useful for developers.\n"
869 " --help Display a help message summarizing command-line\n"
870 " options, license and game controls.\n"
871 " --usage Display a brief message summarizing command-line options.\n"
872 " --version Display the version of SuperTux you're running.\n\n"
876 else if (argv[i][0] != '-')
878 level_startup_file = argv[i];
882 /* Unknown - complain! */
892 void usage(char * prog, int ret)
897 /* Determine which stream to write to: */
905 /* Display the usage message: */
907 fprintf(fi, _("Usage: %s [--fullscreen] [--opengl] [--disable-sound] [--disable-music] [--debug] | [--usage | --help | --version] [--leveleditor] [--worldmap] FILENAME\n"),
916 std::vector<std::string> read_directory(const std::string& pathname)
918 std::vector<std::string> dirnames;
920 DIR* dir = opendir(pathname.c_str());
923 struct dirent *direntp;
925 while((direntp = readdir(dir)))
927 dirnames.push_back(direntp->d_name);