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"
49 #include "../gui/menu.h"
50 #include "../utils/configfile.h"
51 #include "../audio/sound_manager.h"
52 #include "../app/gettext.h"
54 using namespace SuperTux;
57 #define mkdir(dir, mode) mkdir(dir)
58 // on win32 we typically don't want LFS paths
60 #define DATA_PREFIX "./data/"
63 /* Screen proprities: */
64 /* Don't use this to test for the actual screen sizes. Use screen->w/h instead! */
68 /* Local function prototypes: */
71 void usage(char * prog, int ret);
73 /* Does the given file exist and is it accessible? */
74 int SuperTux::faccessible(const char *filename)
77 if (stat(filename, &filestat) == -1)
83 if(S_ISREG(filestat.st_mode))
90 /* Can we write to this location? */
91 int SuperTux::fwriteable(const char *filename)
94 fi = fopen(filename, "wa");
102 /* Makes sure a directory is created in either the SuperTux home directory or the SuperTux base directory.*/
103 int SuperTux::fcreatedir(const char* relative_dir)
106 snprintf(path, 1024, "%s/%s/", st_dir, relative_dir);
107 if(mkdir(path,0755) != 0)
109 snprintf(path, 1024, "%s/%s/", datadir.c_str(), relative_dir);
110 if(mkdir(path,0755) != 0)
125 FILE * SuperTux::opendata(const char * rel_filename, const char * mode)
127 char * filename = NULL;
130 filename = (char *) malloc(sizeof(char) * (strlen(st_dir) +
131 strlen(rel_filename) + 1));
133 strcpy(filename, st_dir);
134 /* Open the high score file: */
136 strcat(filename, rel_filename);
138 /* Try opening the file: */
139 fi = fopen(filename, mode);
143 fprintf(stderr, "Warning: Unable to open the file \"%s\" ", filename);
145 if (strcmp(mode, "r") == 0)
146 fprintf(stderr, "for read!!!\n");
147 else if (strcmp(mode, "w") == 0)
148 fprintf(stderr, "for write!!!\n");
155 /* Get all names of sub-directories in a certain directory. */
156 /* Returns the number of sub-directories found. */
157 /* Note: The user has to free the allocated space. */
158 string_list_type SuperTux::dsubdirs(const char *rel_path,const char* expected_file)
161 struct dirent *direntp;
162 string_list_type sdirs;
166 string_list_init(&sdirs);
167 sprintf(path,"%s/%s",st_dir,rel_path);
168 if((dirStructP = opendir(path)) != NULL)
170 while((direntp = readdir(dirStructP)) != NULL)
172 char absolute_filename[1024];
175 sprintf(absolute_filename, "%s/%s", path, direntp->d_name);
177 if (stat(absolute_filename, &buf) == 0 && S_ISDIR(buf.st_mode))
179 if(expected_file != NULL)
181 sprintf(filename,"%s/%s/%s",path,direntp->d_name,expected_file);
182 if(!faccessible(filename))
186 string_list_add_item(&sdirs,direntp->d_name);
189 closedir(dirStructP);
192 sprintf(path,"%s/%s",datadir.c_str(),rel_path);
193 if((dirStructP = opendir(path)) != NULL)
195 while((direntp = readdir(dirStructP)) != NULL)
197 char absolute_filename[1024];
200 sprintf(absolute_filename, "%s/%s", path, direntp->d_name);
202 if (stat(absolute_filename, &buf) == 0 && S_ISDIR(buf.st_mode))
204 if(expected_file != NULL)
206 sprintf(filename,"%s/%s/%s",path,direntp->d_name,expected_file);
207 if(!faccessible(filename))
213 sprintf(filename,"%s/%s/%s/%s",st_dir,rel_path,direntp->d_name,expected_file);
214 if(faccessible(filename))
219 string_list_add_item(&sdirs,direntp->d_name);
222 closedir(dirStructP);
228 string_list_type SuperTux::dfiles(const char *rel_path, const char* glob, const char* exception_str)
231 struct dirent *direntp;
232 string_list_type sdirs;
235 string_list_init(&sdirs);
236 sprintf(path,"%s/%s",st_dir,rel_path);
237 if((dirStructP = opendir(path)) != NULL)
239 while((direntp = readdir(dirStructP)) != NULL)
241 char absolute_filename[1024];
244 sprintf(absolute_filename, "%s/%s", path, direntp->d_name);
246 if (stat(absolute_filename, &buf) == 0 && S_ISREG(buf.st_mode))
248 if(exception_str != NULL)
250 if(strstr(direntp->d_name,exception_str) != NULL)
254 if(strstr(direntp->d_name,glob) == NULL)
257 string_list_add_item(&sdirs,direntp->d_name);
260 closedir(dirStructP);
263 sprintf(path,"%s/%s",datadir.c_str(),rel_path);
264 if((dirStructP = opendir(path)) != NULL)
266 while((direntp = readdir(dirStructP)) != NULL)
268 char absolute_filename[1024];
271 sprintf(absolute_filename, "%s/%s", path, direntp->d_name);
273 if (stat(absolute_filename, &buf) == 0 && S_ISREG(buf.st_mode))
275 if(exception_str != NULL)
277 if(strstr(direntp->d_name,exception_str) != NULL)
281 if(strstr(direntp->d_name,glob) == NULL)
284 string_list_add_item(&sdirs,direntp->d_name);
287 closedir(dirStructP);
293 void SuperTux::free_strings(char **strings, int num)
296 for(i=0; i < num; ++i)
301 /* Set SuperTux configuration and save directories */
302 void SuperTux::st_directory_setup(void)
306 /* Get home directory (from $HOME variable)... if we can't determine it,
307 use the current directory ("."): */
308 if (getenv("HOME") != NULL)
309 home = getenv("HOME");
313 st_dir = (char *) malloc(sizeof(char) * (strlen(home) +
314 strlen("/.supertux") + 1));
315 strcpy(st_dir, home);
316 strcat(st_dir, "/.supertux");
318 /* Remove .supertux config-file from old SuperTux versions */
319 if(faccessible(st_dir))
325 st_save_dir = (char *) malloc(sizeof(char) * (strlen(st_dir) + strlen("/save") + 1));
327 strcpy(st_save_dir,st_dir);
328 strcat(st_save_dir,"/save");
330 /* Create them. In the case they exist they won't destroy anything. */
332 mkdir(st_save_dir, 0755);
334 sprintf(str, "%s/levels", st_dir);
337 // User has not that a datadir, so we try some magic
342 char exe_file[PATH_MAX];
343 if (readlink("/proc/self/exe", exe_file, PATH_MAX) < 0)
345 puts("Couldn't read /proc/self/exe, using default path: " DATA_PREFIX);
346 datadir = DATA_PREFIX;
350 std::string exedir = std::string(dirname(exe_file)) + "/";
352 datadir = exedir + "../data"; // SuperTux run from source dir
353 if (access(datadir.c_str(), F_OK) != 0)
355 datadir = exedir + "../share/supertux"; // SuperTux run from PATH
356 if (access(datadir.c_str(), F_OK) != 0)
357 { // If all fails, fall back to compiled path
358 datadir = DATA_PREFIX;
363 datadir = DATA_PREFIX;
366 printf("Datadir: %s\n", datadir.c_str());
369 void SuperTux::st_general_setup(void)
371 /* Seed random number generator: */
373 srand(SDL_GetTicks());
375 /* Set icon image: */
379 /* Unicode needed for input handling: */
381 SDL_EnableUNICODE(1);
383 /* Load global images: */
384 gold_text = new Font(datadir + "/images/fonts/gold.png", Font::TEXT, 16,18);
385 blue_text = new Font(datadir + "/images/fonts/blue.png", Font::TEXT, 16,18,3);
386 white_text = new Font(datadir + "/images/fonts/white.png",
388 gray_text = new Font(datadir + "/images/fonts/gray.png",
390 white_small_text = new Font(datadir + "/images/fonts/white-small.png",
392 white_big_text = new Font(datadir + "/images/fonts/white-big.png",
393 Font::TEXT, 20,22, 3);
394 yellow_nums = new Font(datadir + "/images/fonts/numbers.png",
397 /* Load GUI/menu images: */
398 checkbox = new Surface(datadir + "/images/status/checkbox.png", true);
399 checkbox_checked = new Surface(datadir + "/images/status/checkbox-checked.png", true);
400 back = new Surface(datadir + "/images/status/back.png", true);
401 arrow_left = new Surface(datadir + "/images/icons/left.png", true);
402 arrow_right = new Surface(datadir + "/images/icons/right.png", true);
404 /* Load the mouse-cursor */
405 mouse_cursor = new MouseCursor( datadir + "/images/status/mousecursor.png",1);
406 MouseCursor::set_current(mouse_cursor);
410 void SuperTux::st_general_free(void)
413 /* Free global images: */
418 delete white_small_text;
419 delete white_big_text;
422 /* Free GUI/menu images: */
424 delete checkbox_checked;
429 /* Free mouse-cursor */
434 void SuperTux::st_video_setup(void)
436 /* Init SDL Video: */
437 if (SDL_Init(SDL_INIT_VIDEO) < 0)
440 "\nError: I could not initialize video!\n"
441 "The Simple DirectMedia error that occured was:\n"
442 "%s\n\n", SDL_GetError());
450 st_video_setup_sdl();
452 Surface::reload_all();
454 /* Set window manager stuff: */
455 SDL_WM_SetCaption("SuperTux " VERSION, "SuperTux");
458 void SuperTux::st_video_setup_sdl(void)
462 screen = SDL_SetVideoMode(SCREEN_W, SCREEN_H, 0, SDL_FULLSCREEN ) ; /* | SDL_HWSURFACE); */
466 "\nWarning: I could not set up fullscreen video for "
468 "The Simple DirectMedia error that occured was:\n"
469 "%s\n\n", SDL_GetError());
470 use_fullscreen = false;
475 screen = SDL_SetVideoMode(SCREEN_W, SCREEN_H, 0, SDL_HWSURFACE | SDL_DOUBLEBUF );
480 "\nError: I could not set up video for 800x600 mode.\n"
481 "The Simple DirectMedia error that occured was:\n"
482 "%s\n\n", SDL_GetError());
488 void SuperTux::st_video_setup_gl(void)
492 SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
493 SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5);
494 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
495 SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
496 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
500 screen = SDL_SetVideoMode(SCREEN_W, SCREEN_H, 0, SDL_FULLSCREEN | SDL_OPENGL) ; /* | SDL_HWSURFACE); */
504 "\nWarning: I could not set up fullscreen video for "
506 "The Simple DirectMedia error that occured was:\n"
507 "%s\n\n", SDL_GetError());
508 use_fullscreen = false;
513 screen = SDL_SetVideoMode(SCREEN_W, SCREEN_H, 0, SDL_OPENGL);
518 "\nError: I could not set up video for 640x480 mode.\n"
519 "The Simple DirectMedia error that occured was:\n"
520 "%s\n\n", SDL_GetError());
526 * Set up OpenGL for 2D rendering.
528 glDisable(GL_DEPTH_TEST);
529 glDisable(GL_CULL_FACE);
531 glViewport(0, 0, screen->w, screen->h);
532 glMatrixMode(GL_PROJECTION);
534 glOrtho(0, screen->w, screen->h, 0, -1.0, 1.0);
536 glMatrixMode(GL_MODELVIEW);
538 glTranslatef(0.0f, 0.0f, 0.0f);
544 void SuperTux::st_joystick_setup(void)
551 if (SDL_Init(SDL_INIT_JOYSTICK) < 0)
553 fprintf(stderr, "Warning: I could not initialize joystick!\n"
554 "The Simple DirectMedia error that occured was:\n"
555 "%s\n\n", SDL_GetError());
557 use_joystick = false;
562 if (SDL_NumJoysticks() <= 0)
564 fprintf(stderr, "Info: No joysticks were found.\n");
566 use_joystick = false;
570 js = SDL_JoystickOpen(joystick_num);
574 fprintf(stderr, "Warning: Could not open joystick %d.\n"
575 "The Simple DirectMedia error that occured was:\n"
576 "%s\n\n", joystick_num, SDL_GetError());
578 use_joystick = false;
582 if (SDL_JoystickNumAxes(js) < 2)
585 "Warning: Joystick does not have enough axes!\n");
587 use_joystick = false;
591 if (SDL_JoystickNumButtons(js) < 2)
595 "Joystick does not have enough buttons!\n");
597 use_joystick = false;
605 void SuperTux::st_audio_setup(void)
608 /* Init SDL Audio silently even if --disable-sound : */
612 if (SDL_Init(SDL_INIT_AUDIO) < 0)
614 /* only print out message if sound or music
615 was not disabled at command-line
617 if (use_sound || use_music)
620 "\nWarning: I could not initialize audio!\n"
621 "The Simple DirectMedia error that occured was:\n"
622 "%s\n\n", SDL_GetError());
624 /* keep the programming logic the same :-)
625 because in this case, use_sound & use_music' values are ignored
626 when there's no available audio device
630 audio_device = false;
635 /* Open sound silently regarless the value of "use_sound": */
639 if (open_audio(44100, AUDIO_S16, 2, 2048) < 0)
641 /* only print out message if sound or music
642 was not disabled at command-line
644 if (use_sound || use_music)
647 "\nWarning: I could not set up audio for 44100 Hz "
649 "The Simple DirectMedia error that occured was:\n"
650 "%s\n\n", SDL_GetError());
654 audio_device = false;
661 /* --- SHUTDOWN --- */
663 void SuperTux::st_shutdown(void)
672 void SuperTux::st_abort(const std::string& reason, const std::string& details)
674 fprintf(stderr, "\nError: %s\n%s\n\n", reason.c_str(), details.c_str());
679 /* Set Icon (private) */
688 /* Load icon into a surface: */
690 icon = IMG_Load((datadir + "/images/supertux.xpm").c_str());
694 "\nError: I could not load the icon image: %s%s\n"
695 "The Simple DirectMedia error that occured was:\n"
696 "%s\n\n", datadir.c_str(), "/images/supertux.xpm", SDL_GetError());
703 masklen = (((icon -> w) + 7) / 8) * (icon -> h);
704 mask = (Uint8*) malloc(masklen * sizeof(Uint8));
705 memset(mask, 0xFF, masklen);
710 SDL_WM_SetIcon(icon, NULL);//mask);
713 /* Free icon surface & mask: */
716 SDL_FreeSurface(icon);
720 /* Parse command-line arguments: */
722 void SuperTux::parseargs(int argc, char * argv[])
728 /* Parse arguments: */
730 for (i = 1; i < argc; i++)
732 if (strcmp(argv[i], "--fullscreen") == 0 ||
733 strcmp(argv[i], "-f") == 0)
735 use_fullscreen = true;
737 else if (strcmp(argv[i], "--window") == 0 ||
738 strcmp(argv[i], "-w") == 0)
740 use_fullscreen = false;
742 else if (strcmp(argv[i], "--joystick") == 0 || strcmp(argv[i], "-j") == 0)
745 joystick_num = atoi(argv[++i]);
747 else if (strcmp(argv[i], "--joymap") == 0)
750 if (sscanf(argv[++i],
752 &joystick_keymap.x_axis,
753 &joystick_keymap.y_axis,
754 &joystick_keymap.a_button,
755 &joystick_keymap.b_button,
756 &joystick_keymap.start_button) != 5)
758 puts("Warning: Invalid or incomplete joymap, should be: 'XAXIS:YAXIS:A:B:START'");
762 std::cout << "Using new joymap:\n"
763 << " X-Axis: " << joystick_keymap.x_axis << "\n"
764 << " Y-Axis: " << joystick_keymap.y_axis << "\n"
765 << " A-Button: " << joystick_keymap.a_button << "\n"
766 << " B-Button: " << joystick_keymap.b_button << "\n"
767 << " Start-Button: " << joystick_keymap.start_button << std::endl;
770 else if (strcmp(argv[i], "--leveleditor") == 0)
772 launch_leveleditor_mode = true;
774 else if (strcmp(argv[i], "--worldmap") == 0)
776 launch_worldmap_mode = true;
778 else if (strcmp(argv[i], "--datadir") == 0
779 || strcmp(argv[i], "-d") == 0 )
784 else if (strcmp(argv[i], "--show-fps") == 0)
786 /* Use full screen: */
790 else if (strcmp(argv[i], "--opengl") == 0 ||
791 strcmp(argv[i], "-gl") == 0)
799 else if (strcmp(argv[i], "--sdl") == 0)
803 else if (strcmp(argv[i], "--usage") == 0)
809 else if (strcmp(argv[i], "--version") == 0)
812 printf("SuperTux " VERSION "\n");
815 else if (strcmp(argv[i], "--disable-sound") == 0)
817 /* Disable the compiled in sound feature */
818 printf("Sounds disabled \n");
820 audio_device = false;
822 else if (strcmp(argv[i], "--disable-music") == 0)
824 /* Disable the compiled in sound feature */
825 printf("Music disabled \n");
828 else if (strcmp(argv[i], "--debug") == 0)
830 /* Enable the debug-mode */
834 else if (strcmp(argv[i], "--help") == 0)
836 puts(_(" SuperTux " VERSION "\n"
837 " Please see the file \"README.txt\" for more details.\n"));
838 printf(_("Usage: %s [OPTIONS] FILENAME\n\n"), argv[0]);
839 puts(_("Display Options:\n"
840 " -f, --fullscreen Run in fullscreen mode.\n"
841 " -w, --window Run in window mode.\n"
842 " --opengl If OpenGL support was compiled in, this will tell\n"
843 " SuperTux to make use of it.\n"
844 " --sdl Use the SDL software graphical renderer\n"
847 " --disable-sound If sound support was compiled in, this will\n"
848 " disable sound for this session of the game.\n"
849 " --disable-music Like above, but this will disable music.\n"
852 " -j, --joystick NUM Use joystick NUM (default: 0)\n"
853 " --joymap XAXIS:YAXIS:A:B:START\n"
854 " Define how joystick buttons and axis should be mapped\n"
855 " --leveleditor Opens the leveleditor in a file.\n"
856 " --worldmap Opens the specified worldmap file.\n"
857 " -d, --datadir DIR Load Game data from DIR (default: automatic)\n"
858 " --debug Enables the debug mode, which is useful for developers.\n"
859 " --help Display a help message summarizing command-line\n"
860 " options, license and game controls.\n"
861 " --usage Display a brief message summarizing command-line options.\n"
862 " --version Display the version of SuperTux you're running.\n\n"
866 else if (argv[i][0] != '-')
868 level_startup_file = argv[i];
872 /* Unknown - complain! */
882 void usage(char * prog, int ret)
887 /* Determine which stream to write to: */
895 /* Display the usage message: */
897 fprintf(fi, _("Usage: %s [--fullscreen] [--opengl] [--disable-sound] [--disable-music] [--debug] | [--usage | --help | --version] [--leveleditor] [--worldmap] FILENAME\n"),
906 std::vector<std::string> SuperTux::read_directory(const std::string& pathname)
908 std::vector<std::string> dirnames;
910 DIR* dir = opendir(pathname.c_str());
913 struct dirent *direntp;
915 while((direntp = readdir(dir)))
917 dirnames.push_back(direntp->d_name);