#include <stdexcept>
#include <sstream>
-#include <time.h>
-#include <stdlib.h>
+#include <ctime>
+#include <cstdlib>
#include <sys/stat.h>
#include <sys/types.h>
-#include <dirent.h>
#include <unistd.h>
-#include <assert.h>
#include <physfs.h>
#include <SDL.h>
#include <SDL_image.h>
-#include <GL/gl.h>
+
+#ifdef MACOSX
+namespace supertux_apple {
+#include <CoreFoundation/CoreFoundation.h>
+}
+#endif
#include "gameconfig.hpp"
#include "resources.hpp"
#include "audio/sound_manager.hpp"
#include "video/surface.hpp"
#include "video/texture_manager.hpp"
+#include "video/drawing_context.hpp"
+#include "video/glutil.hpp"
#include "control/joystickkeyboardcontroller.hpp"
#include "options_menu.hpp"
#include "mainloop.hpp"
#include "title.hpp"
#include "game_session.hpp"
-#include "script_manager.hpp"
-#include "scripting/sound.hpp"
#include "scripting/level.hpp"
-#include "scripting/wrapper_util.hpp"
+#include "scripting/squirrel_util.hpp"
#include "file_system.hpp"
#include "physfs/physfs_sdl.hpp"
+#include "random_generator.hpp"
+#include "worldmap/worldmap.hpp"
+#include "addon/addon_manager.hpp"
+#include "binreloc/binreloc.h"
-SDL_Surface* screen = 0;
+namespace { DrawingContext *context_pointer; }
+SDL_Surface *screen;
JoystickKeyboardController* main_controller = 0;
TinyGetText::DictionaryManager dictionary_manager;
+int SCREEN_WIDTH;
+int SCREEN_HEIGHT;
+
static void init_config()
{
config = new Config();
{
dictionary_manager.add_directory("locale");
dictionary_manager.set_charset("UTF-8");
+
+ // Config setting "locale" overrides language detection
+ if (config->locale != "") {
+ dictionary_manager.set_language( config->locale );
+ }
}
static void init_physfs(const char* argv0)
throw std::runtime_error(msg.str());
}
+ // allow symbolic links
+ PHYSFS_permitSymbolicLinks(1);
+
// Initialize physfs (this is a slightly modified version of
// PHYSFS_setSaneConfig
- const char* application = PACKAGE_NAME;
+ const char* application = "supertux2"; //instead of PACKAGE_NAME so we can coexist with MS1
const char* userdir = PHYSFS_getUserDir();
- const char* dirsep = PHYSFS_getDirSeparator();
char* writedir = new char[strlen(userdir) + strlen(application) + 2];
// Set configuration directory
sprintf(mkdir, ".%s", application);
if(!PHYSFS_setWriteDir(userdir) || !PHYSFS_mkdir(mkdir)) {
std::ostringstream msg;
- msg << "Failed creating configuration directory '"
+ msg << "Failed creating configuration directory '"
<< writedir << "': " << PHYSFS_getLastError();
delete[] writedir;
delete[] mkdir;
throw std::runtime_error(msg.str());
}
delete[] mkdir;
-
+
if(!PHYSFS_setWriteDir(writedir)) {
std::ostringstream msg;
- msg << "Failed to use configuration directory '"
+ msg << "Failed to use configuration directory '"
<< writedir << "': " << PHYSFS_getLastError();
delete[] writedir;
throw std::runtime_error(msg.str());
PHYSFS_addToSearchPath(writedir, 0);
delete[] writedir;
- // Search for archives and add them to the search path
- const char* archiveExt = "zip";
- char** rc = PHYSFS_enumerateFiles("/");
- size_t extlen = strlen(archiveExt);
-
- for(char** i = rc; *i != 0; ++i) {
- size_t l = strlen(*i);
- if((l > extlen) && ((*i)[l - extlen - 1] == '.')) {
- const char* ext = (*i) + (l - extlen);
- if(strcasecmp(ext, archiveExt) == 0) {
- const char* d = PHYSFS_getRealDir(*i);
- char* str = new char[strlen(d) + strlen(dirsep) + l + 1];
- sprintf(str, "%s%s%s", d, dirsep, *i);
- PHYSFS_addToSearchPath(str, 1);
- delete[] str;
- }
- }
- }
-
- PHYSFS_freeList(rc);
-
// when started from source dir...
std::string dir = PHYSFS_getBaseDir();
dir += "/data";
}
#ifdef MACOSX
+{
+ using namespace supertux_apple;
+
// when started from Application file on Mac OS X...
- dir = PHYSFS_getBaseDir();
- dir += "SuperTux.app/Contents/Resources/data";
+ char path[PATH_MAX];
+ CFBundleRef mainBundle = CFBundleGetMainBundle();
+ assert(mainBundle != 0);
+ CFURLRef mainBundleURL = CFBundleCopyBundleURL(mainBundle);
+ assert(mainBundleURL != 0);
+ CFStringRef pathStr = CFURLCopyFileSystemPath(mainBundleURL, kCFURLPOSIXPathStyle);
+ assert(pathStr != 0);
+ CFStringGetCString(pathStr, path, PATH_MAX, kCFStringEncodingUTF8);
+ CFRelease(mainBundleURL);
+ CFRelease(pathStr);
+
+ dir = std::string(path) + "/Contents/Resources/data";
testfname = dir + "/credits.txt";
sourcedir = false;
f = fopen(testfname.c_str(), "r");
if(f) {
fclose(f);
if(!PHYSFS_addToSearchPath(dir.c_str(), 1)) {
- msg_warning << "Couldn't add '" << dir << "' to physfs searchpath: " << PHYSFS_getLastError() << std::endl;
+ log_warning << "Couldn't add '" << dir << "' to physfs searchpath: " << PHYSFS_getLastError() << std::endl;
} else {
sourcedir = true;
}
}
+}
+#endif
+
+#ifdef _WIN32
+ PHYSFS_addToSearchPath(".\\data", 1);
#endif
if(!sourcedir) {
#if defined(APPDATADIR) || defined(ENABLE_BINRELOC)
std::string datadir;
#ifdef ENABLE_BINRELOC
- char* brdatadir = br_strcat(DATADIR, "/" PACKAGE_NAME);
- datadir = brdatadir;
- free(brdatadir);
+
+ char* dir;
+ br_init (NULL);
+ dir = br_find_data_dir(APPDATADIR);
+ datadir = dir;
+ free(dir);
+
#else
datadir = APPDATADIR;
#endif
#endif
}
- // allow symbolic links
- PHYSFS_permitSymbolicLinks(1);
-
//show search Path
- for(char** i = PHYSFS_getSearchPath(); *i != NULL; i++)
+ char** searchpath = PHYSFS_getSearchPath();
+ for(char** i = searchpath; *i != NULL; i++)
log_info << "[" << *i << "] is in the search path" << std::endl;
+ PHYSFS_freeList(searchpath);
}
static void print_usage(const char* argv0)
" -f, --fullscreen Run in fullscreen mode\n"
" -w, --window Run in window mode\n"
" -g, --geometry WIDTHxHEIGHT Run SuperTux in given resolution\n"
+ " -a, --aspect WIDTH:HEIGHT Run SuperTux with given aspect ratio\n"
+ " -d, --default Reset video settings to default values\n"
" --disable-sfx Disable sound effects\n"
" --disable-music Disable music\n"
- " --help Show this help message\n"
- " --version Display SuperTux version and quit\n"
+ " -h, --help Show this help message\n"
+ " -v, --version Show SuperTux version and quit\n"
+ " --console Enable ingame scripting console\n"
+ " --noconsole Disable ingame scripting console\n"
" --show-fps Display framerate in levels\n"
+ " --no-show-fps Do not display framerate in levels\n"
" --record-demo FILE LEVEL Record a demo to FILE\n"
" --play-demo FILE LEVEL Play a recorded demo\n"
+ " -s, --debug-scripts Enable script debugger.\n"
"\n"));
}
+/**
+ * Options that should be evaluated prior to any initializations at all go here
+ */
+static bool pre_parse_commandline(int argc, char** argv)
+{
+ for(int i = 1; i < argc; ++i) {
+ std::string arg = argv[i];
+
+ if(arg == "--version" || arg == "-v") {
+ std::cout << PACKAGE_NAME << " " << PACKAGE_VERSION << std::endl;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * Options that should be evaluated after config is read go here
+ */
static bool parse_commandline(int argc, char** argv)
{
for(int i = 1; i < argc; ++i) {
std::string arg = argv[i];
- if(arg == "--fullscreen" || arg == "-f") {
+ if(arg == "--help" || arg == "-h") {
+ print_usage(argv[0]);
+ return true;
+ } else if(arg == "--fullscreen" || arg == "-f") {
config->use_fullscreen = true;
+ } else if(arg == "--default" || arg == "-d") {
+ config->use_fullscreen = false;
+
+ config->window_width = 800;
+ config->window_height = 600;
+
+ config->fullscreen_width = 800;
+ config->fullscreen_height = 600;
+
+ config->aspect_width = 0; // auto detect
+ config->aspect_height = 0;
+
} else if(arg == "--window" || arg == "-w") {
config->use_fullscreen = false;
} else if(arg == "--geometry" || arg == "-g") {
- if(i+1 >= argc) {
- print_usage(argv[0]);
- throw std::runtime_error("Need to specify a parameter for geometry switch");
- }
- if(sscanf(argv[++i], "%dx%d", &config->screenwidth, &config->screenheight)
- != 2) {
- print_usage(argv[0]);
- throw std::runtime_error("Invalid geometry spec, should be WIDTHxHEIGHT");
- }
+ i += 1;
+ if(i >= argc)
+ {
+ print_usage(argv[0]);
+ throw std::runtime_error("Need to specify a size (WIDTHxHEIGHT) for geometry argument");
+ }
+ else
+ {
+ int width, height;
+ if (sscanf(argv[i], "%dx%d", &width, &height) != 2)
+ {
+ print_usage(argv[0]);
+ throw std::runtime_error("Invalid geometry spec, should be WIDTHxHEIGHT");
+ }
+ else
+ {
+ config->window_width = width;
+ config->window_height = height;
+
+ config->fullscreen_width = width;
+ config->fullscreen_height = height;
+ }
+ }
+ } else if(arg == "--aspect" || arg == "-a") {
+ i += 1;
+ if(i >= argc)
+ {
+ print_usage(argv[0]);
+ throw std::runtime_error("Need to specify a ratio (WIDTH:HEIGHT) for aspect ratio");
+ }
+ else
+ {
+ int aspect_width = 0;
+ int aspect_height = 0;
+ if (strcmp(argv[i], "auto") == 0)
+ {
+ aspect_width = 0;
+ aspect_height = 0;
+ }
+ else if (sscanf(argv[i], "%d:%d", &aspect_width, &aspect_height) != 2)
+ {
+ print_usage(argv[0]);
+ throw std::runtime_error("Invalid aspect spec, should be WIDTH:HEIGHT or auto");
+ }
+ else
+ {
+ float aspect_ratio = static_cast<double>(config->aspect_width) /
+ static_cast<double>(config->aspect_height);
+
+ // use aspect ratio to calculate logical resolution
+ if (aspect_ratio > 1) {
+ config->aspect_width = static_cast<int> (600 * aspect_ratio + 0.5);
+ config->aspect_height = 600;
+ } else {
+ config->aspect_width = 600;
+ config->aspect_height = static_cast<int> (600 * 1/aspect_ratio + 0.5);
+ }
+ }
+ }
} else if(arg == "--show-fps") {
config->show_fps = true;
+ } else if(arg == "--no-show-fps") {
+ config->show_fps = false;
+ } else if(arg == "--console") {
+ config->console_enabled = true;
+ } else if(arg == "--noconsole") {
+ config->console_enabled = false;
} else if(arg == "--disable-sfx") {
config->sound_enabled = false;
} else if(arg == "--disable-music") {
throw std::runtime_error("Need to specify a demo filename");
}
config->record_demo = argv[++i];
- } else if(arg == "--help") {
- print_usage(argv[0]);
- return true;
- } else if(arg == "--version") {
- log_info << PACKAGE_NAME << " " << PACKAGE_VERSION << std::endl;
- return true;
+ } else if(arg == "--debug-scripts" || arg == "-s") {
+ config->enable_script_debugger = true;
} else if(arg[0] != '-') {
config->start_level = arg;
} else {
log_warning << "Unknown option '" << arg << "'. Use --help to see a list of options" << std::endl;
+ return true;
}
}
;
}
-static void check_gl_error()
+static void init_rand()
{
- GLenum glerror = glGetError();
- std::string errormsg;
-
- if(glerror != GL_NO_ERROR) {
- switch(glerror) {
- case GL_INVALID_ENUM:
- errormsg = "Invalid enumeration value";
- break;
- case GL_INVALID_VALUE:
- errormsg = "Numeric argzment out of range";
- break;
- case GL_INVALID_OPERATION:
- errormsg = "Invalid operation";
- break;
- case GL_STACK_OVERFLOW:
- errormsg = "stack overflow";
- break;
- case GL_STACK_UNDERFLOW:
- errormsg = "stack underflow";
- break;
- case GL_OUT_OF_MEMORY:
- errormsg = "out of memory";
- break;
-#ifdef GL_TABLE_TOO_LARGE
- case GL_TABLE_TOO_LARGE:
- errormsg = "table too large";
- break;
-#endif
- default:
- errormsg = "unknown error number";
- break;
- }
- std::stringstream msg;
- msg << "OpenGL Error: " << errormsg;
- throw std::runtime_error(msg.str());
- }
+ config->random_seed = systemRandom.srand(config->random_seed);
+
+ //const char *how = config->random_seed? ", user fixed.": ", from time().";
+ //log_info << "Using random seed " << config->random_seed << how << std::endl;
}
void init_video()
{
- if(texture_manager != NULL)
- texture_manager->save_textures();
-
- SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
- SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
- SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5);
- SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
-
- int flags = SDL_OPENGL;
- if(config->use_fullscreen)
- flags |= SDL_FULLSCREEN;
- int width = config->screenwidth;
- int height = config->screenheight;
- int bpp = 0;
-
- screen = SDL_SetVideoMode(width, height, bpp, flags);
- if(screen == 0) {
- std::stringstream msg;
- msg << "Couldn't set video mode (" << width << "x" << height
- << "-" << bpp << "bpp): " << SDL_GetError();
- throw std::runtime_error(msg.str());
- }
+ // FIXME: Add something here
+ SCREEN_WIDTH = 800;
+ SCREEN_HEIGHT = 600;
+
+ context_pointer->init_renderer();
+ screen = SDL_GetVideoSurface();
SDL_WM_SetCaption(PACKAGE_NAME " " PACKAGE_VERSION, 0);
// set icon
- SDL_Surface* icon = IMG_Load_RW(
- get_physfs_SDLRWops("images/engine/icons/supertux.xpm"), true);
+#ifdef MACOSX
+ const char* icon_fname = "images/engine/icons/supertux-256x256.png";
+#else
+ const char* icon_fname = "images/engine/icons/supertux.xpm";
+#endif
+ SDL_Surface* icon;
+ try {
+ icon = IMG_Load_RW(get_physfs_SDLRWops(icon_fname), true);
+ } catch (const std::runtime_error& err) {
+ icon = 0;
+ log_warning << "Couldn't load icon '" << icon_fname << "': " << err.what() << std::endl;
+ }
if(icon != 0) {
SDL_WM_SetIcon(icon, 0);
SDL_FreeSurface(icon);
}
#ifdef DEBUG
else {
- log_warning << "Couldn't find icon 'images/engine/icons/supertux.xpm'" << std::endl;
+ log_warning << "Couldn't load icon '" << icon_fname << "'" << std::endl;
}
#endif
- // setup opengl state and transform
- glDisable(GL_DEPTH_TEST);
- glDisable(GL_CULL_FACE);
- glEnable(GL_TEXTURE_2D);
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-
- glViewport(0, 0, screen->w, screen->h);
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
- // logical resolution here not real monitor resolution
- glOrtho(0, 800, 600, 0, -1.0, 1.0);
- glMatrixMode(GL_MODELVIEW);
- glLoadIdentity();
- glTranslatef(0, 0, 0);
-
- check_gl_error();
-
- if(texture_manager != NULL)
- texture_manager->reload_textures();
- else
- texture_manager = new TextureManager();
+ SDL_ShowCursor(0);
+
+ log_info << (config->use_fullscreen?"fullscreen ":"window ")
+ << " Window: " << config->window_width << "x" << config->window_height
+ << " Fullscreen: " << config->fullscreen_width << "x" << config->fullscreen_height
+ << " Area: " << config->aspect_width << "x" << config->aspect_height << std::endl;
}
static void init_audio()
{
sound_manager = new SoundManager();
-
+
sound_manager->enable_sound(config->sound_enabled);
sound_manager->enable_music(config->music_enabled);
}
-static void init_scripting()
-{
- ScriptManager::instance = new ScriptManager();
-
- HSQUIRRELVM vm = ScriptManager::instance->get_vm();
- sq_pushroottable(vm);
- expose_object(vm, -1, new Scripting::Sound(), "Sound", true);
- expose_object(vm, -1, new Scripting::Level(), "Level", true);
- sq_pop(vm, 1);
-}
-
static void quit_audio()
{
if(sound_manager != NULL) {
void wait_for_event(float min_delay, float max_delay)
{
assert(min_delay <= max_delay);
-
+
Uint32 min = (Uint32) (min_delay * 1000);
Uint32 max = (Uint32) (max_delay * 1000);
static inline void timelog(const char* component)
{
Uint32 current_ticks = SDL_GetTicks();
-
+
if(last_timelog_component != 0) {
log_info << "Component '" << last_timelog_component << "' finished after " << (current_ticks - last_timelog_ticks) / 1000.0 << " seconds" << std::endl;
}
}
#endif
-int main(int argc, char** argv)
+int main(int argc, char** argv)
{
int result = 0;
-
+
+#ifndef DEBUG
try {
- srand(time(0));
+#endif
+
+ if(pre_parse_commandline(argc, argv))
+ return 0;
+
+ Console::instance = new Console();
init_physfs(argv[0]);
init_sdl();
-
- log_fatal << "Test" << std::endl;
-
+
timelog("controller");
- main_controller = new JoystickKeyboardController();
+ main_controller = new JoystickKeyboardController();
+
timelog("config");
init_config();
+
+ timelog("addons");
+ AddonManager::get_instance().load_addons();
+
timelog("tinygettext");
init_tinygettext();
+
timelog("commandline");
if(parse_commandline(argc, argv))
return 0;
+
timelog("audio");
init_audio();
+
timelog("video");
+ DrawingContext context;
+ context_pointer = &context;
init_video();
- Console::instance = new Console();
+
+ Console::instance->init_graphics();
+
timelog("scripting");
- init_scripting();
+ Scripting::init_squirrel(config->enable_script_debugger);
timelog("resources");
- load_shared();
+ load_shared();
+
timelog(0);
- main_loop = new MainLoop();
+ main_loop = new MainLoop();
if(config->start_level != "") {
// we have a normal path specified at commandline not physfs paths.
// So we simply mount that path here...
std::string dir = FileSystem::dirname(config->start_level);
PHYSFS_addToSearchPath(dir.c_str(), true);
- std::auto_ptr<GameSession> session
- (new GameSession(FileSystem::basename(config->start_level)));
- if(config->start_demo != "")
- session->play_demo(config->start_demo);
- if(config->record_demo != "")
- session->record_demo(config->record_demo);
- main_loop->push_screen(session.release());
+
+ if(config->start_level.size() > 4 &&
+ config->start_level.compare(config->start_level.size() - 5, 5, ".stwm") == 0) {
+ init_rand();
+ main_loop->push_screen(new WorldMapNS::WorldMap(
+ FileSystem::basename(config->start_level)));
+ } else {
+ init_rand();//If level uses random eg. for
+ // rain particles before we do this:
+ std::auto_ptr<GameSession> session (
+ new GameSession(FileSystem::basename(config->start_level)));
+
+ config->random_seed =session->get_demo_random_seed(config->start_demo);
+ init_rand();//initialise generator with seed from session
+
+ if(config->start_demo != "")
+ session->play_demo(config->start_demo);
+
+ if(config->record_demo != "")
+ session->record_demo(config->record_demo);
+ main_loop->push_screen(session.release());
+ }
} else {
+ init_rand();
main_loop->push_screen(new TitleScreen());
}
- main_loop->run();
+ //init_rand(); PAK: this call might subsume the above 3, but I'm chicken!
+ main_loop->run(context);
+#ifndef DEBUG
} catch(std::exception& e) {
log_fatal << "Unexpected exception: " << e.what() << std::endl;
result = 1;
log_fatal << "Unexpected exception" << std::endl;
result = 1;
}
+#endif
delete main_loop;
main_loop = NULL;
- free_options_menu();
unload_shared();
quit_audio();
main_controller = NULL;
delete Console::instance;
Console::instance = NULL;
- delete ScriptManager::instance;
- ScriptManager::instance = NULL;
+ Scripting::exit_squirrel();
delete texture_manager;
texture_manager = NULL;
SDL_Quit();
PHYSFS_deinit();
-
+
return result;
}