/* * Copyright (C) 2002,2003,2004,2005 Daniel Heck * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "main.hh" #include "display.hh" #include "lua.hh" #include "gui/MainMenu.hh" #include "gui/ErrorMenu.hh" #include "gui/LevelPreviewCache.hh" #include "options.hh" #include "oxyd.hh" #include "sound.hh" #include "video.hh" #include "ecl_argp.hh" #include "ecl_system.hh" #include "errors.hh" #include "world.hh" #include "nls.hh" #include "LocalToXML.hh" #include "PreferenceManager.hh" #include "Utf8ToXML.hh" #include "XMLtoUtf8.hh" #include "XMLtoLocal.hh" #include "lev/RatingManager.hh" #include "lev/VolatileIndex.hh" #include "lev/PersistentIndex.hh" #include "lev/ScoreManager.hh" #include "enet/enet.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef MACOSX // for search paths #include "NSSystemDirectories.h" #endif using namespace std; using namespace ecl; using namespace enigma; XERCES_CPP_NAMESPACE_USE #ifdef WIN32 // LoadImage is a Syscall on Windows, which gets defined to LoadImageA // or LoadImageW in winuser.h so we simply undefine it to use this // name for one of the methods #undef LoadImage #endif namespace { class Nulbuf : public std::streambuf {}; Nulbuf* nullbuffer = new Nulbuf; } /* -------------------- Variables -------------------- */ namespace enigma { Application app; bool noAssert = true; // block expensive assert evaluations by default bool WizardMode = false; //! If true, do not ``grab'' the mouse and keyboard bool Nograb = false; } /*! The stream object that is used for logging messages. As defined here, it is not connected to any file or buffer. (Note: I think writing to a stream without a streambuffer *should* be a no-op, but it leads to crashes with g++ 2.95. to circumvent this, Log is initialized with a dummy streambuf in init(). ) */ std::ostream enigma::Log(0); /*! This is the stream object that is used when logging to a file. In this case, enigma::Log uses this object's streambuffer. */ static std::fstream logfile; /* -------------------- Functions -------------------- */ static void usage() { printf("Usage: %s [options] [level files]\n\n" "Available options :\n\n" " --nosound Disable music and sound\n" " --nomusic Disable music\n" " --window -w Run in a window; do not enter fullscreen mode\n" " --help -h Show this help\n" " --version Print the executable's version number\n" " --nograb Do not use exclusive mouse/keyboard access\n" " --data -d path Load data from additional directory\n" " --lang -l lang Set game language\n" " --pref -p file Use filename or dirname for preferences\n" "\n", app.progCallPath.c_str() ); } namespace { struct AP : public ecl::ArgParser { public: // Constructor AP(); // Variables. bool nosound, nomusic, show_help, show_version, do_log, do_assert, force_window; bool dumpinfo, makepreview; string gamename; string datapath; string preffilename; std::vector levelnames; private: enum { OPT_WINDOW, OPT_GAME, OPT_DATA, OPT_LANG, OPT_PREF }; // ArgParser interface. void on_error (ErrorType t, const string &option) { cout << errormsg(t, option) << endl; show_help = true; } void on_option (int id, const string ¶m); void on_argument (const string &arg); }; } AP::AP() : ArgParser (app.args.begin(), app.args.end()) { nosound = nomusic = show_help = show_version = do_log = do_assert = force_window = false; dumpinfo = makepreview = false; gamename = ""; datapath = ""; preffilename = PREFFILENAME; def (&nosound, "nosound"); def (&nomusic, "nomusic"); def (&show_version, "version"); def (&show_help, "help", 'h'); def (&WizardMode, "wizard"); def (&Nograb, "nograb"); def (&do_log, "log"); def (&do_assert, "assert"); def (&dumpinfo, "dumpinfo"); def (&makepreview, "makepreview"); def (&force_window, "window", 'w'); def (OPT_GAME, "game", true); def (OPT_DATA, "data", 'd', true); def (OPT_LANG, "lang", 'l', true); def (OPT_PREF, "pref", 'p', true); } void AP::on_option (int id, const string ¶m) { switch (id) { case OPT_GAME: gamename = param; break; case OPT_DATA: // we should be able to add several paths -- file.cc has does not yet support this // if (datapath.empty()) datapath = param; // else // datapath = param + ":" + datapath; break; case OPT_LANG: app.argumentLanguage = param; break; case OPT_PREF: preffilename = param; break; } } void AP::on_argument (const string &arg) { levelnames.push_back (arg); } /*! Initialize enough of the game to be able to show error messages in the window, not on the console. */ /* -------------------- Application -------------------- */ Application::Application() : wizard_mode (false), nograb (false), language (""), defaultLanguage (""), argumentLanguage (""), errorInit (false), isMakePreviews (false) { } void Application::init(int argc, char **argv) { progCallPath = argv[0]; copy(argv+1, argv+argc, back_inserter(args)); // parse commandline arguments -- needs args AP ap; ap.parse(); // ----- Evaluate command line arguments. // start with simple actions that do not need further initialization if (ap.show_help || ap.show_version) { printf("Enigma %s\n", getVersionInfo().c_str()); if (ap.show_help) usage(); exit(0); } // if (ap.makepreview) { ap.force_window = true; ap.nosound = true; ap.nomusic = true; isMakePreviews = true; } // initialize logfile -- needs ap if (ap.do_log) enigma::Log.rdbuf(cout.rdbuf()); else enigma::Log.rdbuf(::nullbuffer); // initialize assertion stop flag if (ap.do_assert) enigma::noAssert = false; // initialize system datapaths -- needs ap, log systemCmdDataPath = ap.datapath; initSysDatapaths(ap.preffilename); // initialize XML -- needs log, datapaths initXerces(); // initialize LUA - Run initialization scripts lua_State *L = lua::GlobalState(); lua::CheckedDoFile(L, app.systemFS, "startup.lua"); // initialize preferences -- needs LUA, XML if (!options::Load()) { fprintf(stderr, _("Error in configuration file.\n")); fprintf(stderr, lua::LastError (lua::GlobalState()).c_str()); } prefs = PreferenceManager::instance(); if (ap.force_window) { options::SetOption("FullScreen", false); } if (isMakePreviews) { options::SetOption("VideoMode", 0); } // initialize user data paths -- needs preferences, system datapaths initUserDatapaths(); // set message language init_i18n(); // ----- Initialize object repositories world::Init(); if (ap.dumpinfo) { world::DumpObjectInfo(); exit(0); } // ----- Initialize SDL library #ifdef WIN32 // SDL_putenv("SDL_VIDEODRIVER=directx"); //needed for SDL 1.2.12 that favors GDI which crashes on SetGamma #endif int sdl_flags = SDL_INIT_VIDEO; #ifdef ANDROID sdl_flags |= SDL_INIT_JOYSTICK; #endif if (enigma::WizardMode) sdl_flags |= SDL_INIT_NOPARACHUTE; if (SDL_Init(sdl_flags) < 0) { fprintf(stderr, "Couldn't initialize SDL: %s\n", SDL_GetError()); exit(1); } atexit(SDL_Quit); SDL_EnableUNICODE(1); const SDL_version* vi = SDL_Linked_Version(); Log << ecl::strf("SDL Version: %u.%u.%u\n", vi->major, vi->minor, vi->patch); vi = TTF_Linked_Version(); Log << ecl::strf("SDL_ttf Version: %u.%u.%u\n", vi->major, vi->minor, vi->patch); if(TTF_Init() == -1) { fprintf(stderr, "Couldn't initialize SDL_ttf: %s\n", TTF_GetError()); exit(1); } vi = IMG_Linked_Version(); Log << ecl::strf("SDL_image Version: %u.%u.%u\n", vi->major, vi->minor, vi->patch); #ifdef SDL_IMG_INIT int img_flags = IMG_INIT_PNG | IMG_INIT_JPG; if (IMG_Init(img_flags) & img_flags != img_flags) { fprintf(stderr, "Couldn't initialize SDL_image: %s\n", IMG_GetError()); exit(1); } #endif // ----- Initialize video subsystem video::Init(); video::SetCaption ("Enigma v" PACKAGE_VERSION); video::SetMouseCursor(enigma::LoadImage("cur-magic"), 4, 4); video::ShowMouse(); SDL_ShowCursor(0); errorInit = true; // ----- Initialize sound subsystem sound::Init(!ap.nomusic, !ap.nosound); lua::CheckedDoFile (L, app.systemFS, "sound-defaults.lua"); lua::DoSubfolderfile (L, "soundsets", "soundset.lua"); // ----- Initialize network layer if (enet_initialize () != 0) { fprintf (stderr, "An error occurred while initializing ENet.\n"); exit (1); } // ----- Load models display::Init(); // initialize application state state = StateManager::instance(); // ----- Load level packs -- needs state lev::Index::initGroups(); oxyd::Init(!isMakePreviews); // Load oxyd data files - must be first to create correct proxies lev::PersistentIndex::registerPersistentIndices(ap.makepreview); if (!isMakePreviews) { lua::Dofile(L, "levels/index.lua"); lua::DoSubfolderfile(L, "levels", "index.lua"); lua::Dofile(L, "levels/index_user.lua"); if (!ap.levelnames.empty()) { lev::Index::registerIndex(new lev::VolatileIndex(INDEX_STARTUP_PACK_NAME, INDEX_EVERY_GROUP, ap.levelnames, INDEX_STARTUP_PACK_LOCATION)); lev::Index::setCurrentIndex(INDEX_STARTUP_PACK_NAME); } #ifndef ANDROID std::vector emptyList; lev::Index::registerIndex(new lev::VolatileIndex(INDEX_SEARCH_PACK_NAME, INDEX_DEFAULT_GROUP, emptyList, INDEX_SEARCH_PACK_LOCATION)); #endif } lev::Proxy::countLevels(); // ----- Initialize sound tables -- needs sound, oxyd, video (error messages!) sound::InitSoundSets(); #if MACOSX updateMac1_00(); #endif // initialize random enigma::Randomize(); // if (isMakePreviews) { std::set proxies = lev::Proxy::getProxies(); int size = proxies.size(); std::set::iterator it; std::string message = ecl::strf("Make %d previews on system path '%s'", size, systemAppDataPath.c_str()); Log << message; Screen *scr = video::GetScreen(); GC gc (scr->get_surface()); Font *f = enigma::GetFont("menufont"); f->render (gc, 80, 240, message.c_str()); set_color(gc, 200,200,200); hline(gc, 170, 280, 300); hline(gc, 170, 300, 300); vline(gc, 170, 280, 20); vline(gc, 470, 280, 20); scr->update_all (); scr->flush_updates(); int i = 0; for (it = proxies.begin(); it != proxies.end(); it++, i++) { gui::LevelPreviewCache::makeSystemPreview(*it, systemAppDataPath); vline(gc, 170 + i*300 / size, 280, 20); scr->update_all (); scr->flush_updates(); } return; } // initialize score -- needs random init lev::ScoreManager::instance(); } std::string Application::getVersionInfo() { std::string versionInfo; double enigmaVersion; sscanf(PACKAGE_VERSION,"%4lf",&enigmaVersion); if (enigmaVersion >= ENIGMACOMPATIBITLITY) versionInfo = "v" PACKAGE_VERSION; else { versionInfo = "v" PACKAGE_VERSION " (development version - v" + ecl::strf("%.2f",ENIGMACOMPATIBITLITY) + " compatibility branch)"; } return versionInfo; } void Application::initSysDatapaths(const std::string &prefFilename) { std::string progDir; // directory path part of args[0] std::string progName; // filename part of args[0] bool progDirExists = split_path(progCallPath, &progDir, &progName); std::string systemPath = SYSTEM_DATA_DIR; // substituted by configure.ac bool haveHome = (getenv("HOME") != 0); #ifdef __MINGW32__ // windows standard user specific application data directory path std::string winAppDataPath = ecl::ApplicationDataPath() + "/Enigma"; #endif // systemFS systemFS = new GameFS(); #ifdef __MINGW32__ if (!progDirExists) { // filename only -- working dir should be on enigma as enigma // should never be located on exec path on windows systemAppDataPath = "./data"; } else { // a call from elsewhere -- absolute or relative path does not matter. // this enables calls from a commandline. systemAppDataPath = progDir + "/data"; } #elif MACOSX // Mac OS X applications are self-contained bundles, // i.e., directories like "Enigma.app". Resources are // placed in those bundles under "Enigma.app/Contents/Resources", // the main executable would be "Enigma.app/Contents/MacOS/enigma". // Here, we get the executable name, clip off the last bit, chdir into it, // then chdir to ../Resources. The original SDL implementation chdirs to // "../../..", i.e. the directory the bundle is placed in. This breaks // the self-containedness. systemAppDataPath = progDir + "/../Resources/data"; #else // Unix -- we get our data path from the installation systemAppDataPath = systemPath; #endif systemFS->append_dir(systemAppDataPath); if (!systemCmdDataPath.empty()) systemFS->prepend_dir(systemCmdDataPath); Log << "systemFS = \"" << systemFS->getDataPath() << "\"\n"; // l10nPath l10nPath = LOCALEDIR; // defined in src/Makefile.am #ifdef __MINGW32__ if (progDirExists) { l10nPath = progDir + "/" + l10nPath; } #elif MACOSX l10nPath = progDir + "/../Resources/locale"; #endif Log << "l10nPath = \"" << l10nPath << "\"\n"; // prefPath if (prefFilename.find_first_of(ecl::PathSeparators) != std::string::npos) { // pref is a path - absolute or home relative prefPath = ecl::ExpandPath(prefFilename); if (!ecl::FolderExists(prefPath)) if(!ecl::FolderCreate(prefPath)) { fprintf(stderr, ("Error cannot create pref directory.\n")); exit(1); } userStdPath = prefPath; // default if pref is a path prefPath = prefPath + ecl::PathSeparator + "." + PREFFILENAME; // include pref in user data path } else if (haveHome) { prefPath = ecl::ExpandPath("~"); if (!ecl::FolderExists(prefPath)) // may happen on Windows if(!ecl::FolderCreate(prefPath)) { fprintf(stderr, _("Error Home directory does not exist.\n")); exit(1); } #ifdef MACOSX userStdPathMac1_00 = prefPath + "/.enigma"; userStdPath = prefPath + "/Library/Application Support/Enigma"; #else userStdPath = prefPath + "/.enigma"; #endif prefPath = prefPath + ecl::PathSeparator + "." + prefFilename; #ifdef __MINGW32__ } else if (!winAppDataPath.empty()) { if (!ecl::FolderExists(winAppDataPath)) // may happen on Windows if(!ecl::FolderCreate(winAppDataPath)) { fprintf(stderr, _("Error Application Data directory does not exist.\n")); exit(1); } Log << "winAppDataPath " << winAppDataPath << "\n"; userStdPath = winAppDataPath; prefPath = winAppDataPath + ecl::PathSeparator + "." + prefFilename; #endif } else { fprintf(stderr, _("Error Home directory does not exist.\n")); exit(1); } Log << "prefPath = \"" << prefPath << "\"\n"; } void Application::initXerces() { // init XML try { // Initialize to en_US - we don't know the user prefs yet // If more than the error messages should be influenced we would // have to terminate and reinit after reading the user prefs. XMLPlatformUtils::Initialize(); // Initialize transcoding service for XML <-> utf8 XMLTransService::Codes initResult; xmlUtf8Transcoder = XMLPlatformUtils::fgTransService-> makeNewTranscoderFor(XMLRecognizer::UTF_8, initResult, 4096); // the block size is irrelevant for utf-8 if (initResult != XMLTransService::Ok) { fprintf(stderr, _("Error in XML initialization.\n")); exit(1); } static const XMLCh ls[] = { chLatin_L, chLatin_S, chNull }; static const XMLCh core[] = { chLatin_C, chLatin_O, chLatin_R, chLatin_E, chNull }; domImplementationLS = (DOMImplementationLS*) (DOMImplementationRegistry::getDOMImplementation(ls)); domImplementationCore = (DOMImplementation*) (DOMImplementationRegistry::getDOMImplementation(core)); domParserErrorHandler = new DOMErrorReporter(&Log); domParserSchemaResolver = new DOMSchemaResolver(); domSerErrorHandler = new DOMErrorReporter(&Log); #if _XERCES_VERSION >= 30000 domParser = domImplementationLS->createLSParser(DOMImplementationLS::MODE_SYNCHRONOUS, 0); DOMConfiguration *config = domParser->getDomConfig(); config->setParameter(XMLUni::fgDOMNamespaces, true); config->setParameter(XMLUni::fgXercesSchema, true); config->setParameter(XMLUni::fgXercesSchemaFullChecking, true); config->setParameter(XMLUni::fgDOMValidate, true); config->setParameter(XMLUni::fgDOMDatatypeNormalization, true); config->setParameter(XMLUni::fgDOMErrorHandler, domParserErrorHandler); config->setParameter(XMLUni::fgDOMResourceResolver, domParserSchemaResolver); domSer = domImplementationLS->createLSSerializer(); config = domSer->getDomConfig(); config->setParameter(XMLUni::fgDOMWRTFormatPrettyPrint, true); config->setParameter(XMLUni::fgDOMErrorHandler, domSerErrorHandler); #else domParser = domImplementationLS->createDOMBuilder(DOMImplementationLS::MODE_SYNCHRONOUS, 0); domParser->setFeature(XMLUni::fgDOMNamespaces, true); domParser->setFeature(XMLUni::fgXercesSchema, true); domParser->setFeature(XMLUni::fgXercesSchemaFullChecking, true); domParser->setFeature(XMLUni::fgDOMValidation, true); domParser->setFeature(XMLUni::fgDOMDatatypeNormalization, true); domParser->setErrorHandler(domParserErrorHandler); domParser->setEntityResolver(domParserSchemaResolver); domSer = domImplementationLS->createDOMWriter(); domSer->setFeature(XMLUni::fgDOMWRTFormatPrettyPrint, true); domSer->setErrorHandler(domSerErrorHandler); #endif } catch (...) { fprintf(stderr, _("Error in XML initialization.\n")); exit(1); } } void Application::initUserDatapaths() { // userPath userPath = prefs->getString("UserPath"); if (userPath.empty()) { #ifdef MACOSX if (prefs->getInt("_MacUpdate1.00") != 1) userPath = userStdPathMac1_00; // empty prefs user path is 1.00 std user path else { // first installation of Enigma on a Mac userPath = userStdPath; // use the new path prefs->setProperty("UserPath", std::string(XMLtoUtf8(LocalToXML(&userPath).x_str()).c_str())); prefs->setProperty("UserImagePath", std::string(XMLtoUtf8(LocalToXML(&userPath).x_str()).c_str())); prefs->setProperty("_MacUpdate1.00", 2); } #else userPath = userStdPath; #endif } else { userPath = XMLtoLocal(Utf8ToXML(userPath.c_str()).x_str()).c_str(); } Log << "userPath = \"" << userPath << "\"\n"; // userImagePath userImagePath = prefs->getString("UserImagePath"); if (userImagePath.empty()) { #ifdef MACOSX userImagePath = userStdPathMac1_00; // empty prefs user path is 1.00 std user path #else userImagePath = userStdPath; #endif } else { userImagePath = XMLtoLocal(Utf8ToXML(userImagePath.c_str()).x_str()).c_str(); } Log << "userImagePath = \"" << userImagePath << "\"\n"; // resourceFS resourceFS = new GameFS(); resourceFS->append_dir(systemAppDataPath); #ifdef MACOSX // set user-visible data paths -- use it for resource paths NSSearchPathEnumerationState cur=NSStartSearchPathEnumeration(NSLibraryDirectory, NSAllDomainsMask); char path[PATH_MAX]; while(cur) { cur=NSGetNextSearchPathEnumeration(cur, path); resourceFS->prepend_dir(std::string(path)+"/Application Support/Enigma"); } #endif if (!systemCmdDataPath.empty()) resourceFS->prepend_dir(systemCmdDataPath); resourceFS->prepend_dir(userPath); if (userImagePath != userPath) resourceFS->prepend_dir(userImagePath); Log << "resourceFS = \"" << resourceFS->getDataPath() << "\"\n"; // create levels/auto, levels/cross, levels/legacy_dat on userPath if (!ecl::FolderExists(userPath + "/levels/auto")) ecl::FolderCreate (userPath + "/levels/auto"); if (!ecl::FolderExists(userPath + "/levels/cross")) ecl::FolderCreate (userPath + "/levels/cross"); if (!ecl::FolderExists(userPath + "/levels/legacy_dat")) ecl::FolderCreate (userPath + "/levels/legacy_dat"); if (!ecl::FolderExists(userPath + "/backup")) ecl::FolderCreate (userPath + "/backup"); } #ifdef MACOSX void Application::updateMac1_00() { if (prefs->getInt("_MacUpdate1.00") == 0 && prefs->getString("UserPath").empty() && prefs->getString("UserImagePath").empty()) { gui::ErrorMenu m(ecl::strf(N_("Mac OS X upgrade from Enigma 1.00\n\nThe default user data path has changed from\n %s \n\nto the visible data path\n %s \n\nIf ok Enigma will move your data to this new location.\nNote that you have to restart Enigma once for completion of this update."), userStdPathMac1_00.c_str(), userStdPath.c_str()), N_("OK"), N_("Never"), N_("Remind")); m.manage(); if (m.isRejectQuit()) { prefs->setProperty("_MacUpdate1.00", 2); } else if (m.isLaterQuit()) { prefs->setProperty("_MacUpdate1.00", 0); } else { // OK move now Log << "Mac update\n"; // move std::system(ecl::strf("mkdir '%s' && cd ~/.enigma && tar -cf - * | (cd '%s' && tar -xf -) && cd ~ && rm -rf ~/.enigma", userStdPath.c_str(), userStdPath.c_str()).c_str()); setUserPath(""); setUserImagePath(""); prefs->setProperty("_MacUpdate1.00", 2); prefs->shutdown(); exit(0); } } } #endif void Application::init_i18n() { // Initialize the internationalization subsystem // priorities: // language: command-line --- user option --- system (environment) // defaultLanguage: command-line --- system (environment) app.language = app.argumentLanguage; app.defaultLanguage = app.argumentLanguage; if (app.language == "") { options::GetOption("Language", app.language); } if (app.defaultLanguage == "") { app.defaultLanguage = ecl::SysMessageLocaleName(); if (app.language == "") { app.language = app.defaultLanguage; } } #if defined(ENABLE_NLS) nls::SetMessageLocale (app.language); bindtextdomain (PACKAGE_NAME, app.l10nPath.c_str()); // SDL_ttf does not handle arbitrary encodings, so use UTF-8 bind_textdomain_codeset (PACKAGE_NAME, "utf-8"); textdomain (PACKAGE_NAME); #endif } void Application::setLanguage(std::string newLanguage) { if (newLanguage == "") { language = defaultLanguage; } else { language = newLanguage; } nls::SetMessageLocale(language); } void Application::setUserPath(std::string newPath) { std::string prefUserPath = (newPath == userStdPath) ? "" : newPath; if ((prefUserPath.empty() && userPath != userStdPath) || (!prefUserPath.empty() && prefUserPath != userPath)) { // set the new userPath - used for saves if (prefUserPath.empty()) userPath = userStdPath; else userPath = prefUserPath; // load all resources primarily from the new path but keep the old user path // because the user could not yet copy his user data to the new location resourceFS->prepend_dir(userPath); // set the new path as the users preference - the standard path is saved as "" #ifdef MACOSX // 1.00 uses "" as "~/.enigma" - we have to store the complete path if (prefUserPath.empty()) prefUserPath = userStdPath; #endif prefs->setProperty("UserPath", std::string(XMLtoUtf8(LocalToXML(&prefUserPath).x_str()).c_str())); } } void Application::setUserImagePath(std::string newPath) { std::string prefUserImagePath = (newPath == userStdPath) ? "" : newPath; if ((prefUserImagePath.empty() && userImagePath != userStdPath) || (!prefUserImagePath.empty() && prefUserImagePath != userImagePath)) { // set the new userImagePath - used for saves if (prefUserImagePath.empty()) userImagePath = userStdPath; else userImagePath = prefUserImagePath; // load all resources primarily from the new path but keep the old user path // because the user could not yet copy his user data to the new location if (userImagePath != userPath) resourceFS->prepend_dir(userImagePath); // set the new path as the users preference - the standard path is saved as "" #ifdef MACOSX // 1.00 uses "" as "~/.enigma" - we have to store the complete path if (prefUserImagePath.empty()) prefUserImagePath = userStdPath; #endif prefs->setProperty("UserImagePath", std::string(XMLtoUtf8(LocalToXML(&prefUserImagePath).x_str()).c_str())); } } /* -------------------- Functions -------------------- */ void Application::shutdown() { oxyd::Shutdown(); world::Shutdown(); display::Shutdown(); if (!isMakePreviews) { // avoid saves on preview generation lev::RatingManager::instance()->save(); if (lev::PersistentIndex::historyIndex != NULL) lev::PersistentIndex::historyIndex->save(); lev::ScoreManager::instance()->shutdown(); app.state->shutdown(); app.prefs->shutdown(); } // now we shutdown SDL - no error reports will be possible! app.errorInit = false; video::Shutdown(); sound::Shutdown(); enet_deinitialize(); lua::ShutdownGlobal(); XMLPlatformUtils::Terminate(); #ifdef SDL_IMG_INIT IMG_Quit(); #endif ClearFontCache(); TTF_Quit(); ClearImageCache(); delete ::nullbuffer; } int main(int argc, char** argv) { try { app.init(argc,argv); if (!app.isMakePreviews) gui::ShowMainMenu(); app.shutdown(); return 0; } catch (XFrontend &e) { cerr << "Error: " << e.what() << endl; std::string message = _("Fatal Error that causes the application to quit:\n\n"); if (app.errorInit) { gui::ErrorMenu m(message + e.what(), N_("Quit")); m.manage(); } } catch (ecl::XGeneric &e) { cerr << "Error: " << e.what() << endl; } catch (std::exception &e) { cerr << "Error: " << e.what() << endl; } catch (...) { cerr << "Uncaught exception...\n"; } return 1; }