CoreApp.cpp

Go to the documentation of this file.
00001 /*
00002 Copyright (C) 2004-2006 The Pentagram team
00003 
00004 This program is free software; you can redistribute it and/or
00005 modify it under the terms of the GNU General Public License
00006 as published by the Free Software Foundation; either version 2
00007 of the License, or (at your option) any later version.
00008 
00009 This program is distributed in the hope that it will be useful,
00010 but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 GNU General Public License for more details.
00013 
00014 You should have received a copy of the GNU General Public License
00015 along with this program; if not, write to the Free Software
00016 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00017 */
00018 
00019 #include "pent_include.h"
00020 
00021 #include "CoreApp.h"
00022 
00023 #include "FileSystem.h"
00024 #include "ConfigFileManager.h"
00025 #include "SettingManager.h"
00026 
00027 #include "IDataSource.h"
00028 #include "Args.h"
00029 #include "GameInfo.h"
00030 #include "GameDetector.h"
00031 
00032 #if defined(WIN32) && defined(WIN32_USE_MY_DOCUMENTS)
00033 #include <shlobj.h>
00034 #endif
00035 
00036 using std::string;
00037 
00038 static void ToLower(std::string& str);
00039 
00040 
00041 // p_dynamic_cast stuff
00042 DEFINE_RUNTIME_CLASSTYPE_CODE_BASE_CLASS(CoreApp);
00043 
00044 CoreApp* CoreApp::application = 0;
00045 
00046 CoreApp::CoreApp(int argc_, const char* const* argv_)
00047         : isRunning(false), gameinfo(0), filesystem(0),
00048           configfileman(0), settingman(0), argc(argc_),
00049           argv(argv_), oHelp(false), oQuiet(false), oVQuiet(false)
00050 {
00051         assert(application == 0);
00052         application = this;
00053 }
00054 
00055 CoreApp::~CoreApp()
00056 {
00057         std::map<Pentagram::istring, GameInfo*>::iterator i;
00058         for (i = games.begin(); i != games.end(); ++i)
00059                 delete i->second;
00060 
00061         games.clear();
00062 
00063         FORGET_OBJECT(filesystem);
00064         FORGET_OBJECT(settingman);
00065         FORGET_OBJECT(configfileman);
00066         FORGET_OBJECT(gameinfo);
00067 
00068         application = 0;
00069 }
00070 
00071 void CoreApp::startup()
00072 {
00073         DeclareArgs(); // Note: this is virtual
00074 
00075         ParseArgs(argc, argv);
00076 
00077         // if we're spitting out help, we probably want to avoid having the
00078         // other cruft dumped too...
00079         if(oHelp) { oQuiet=oVQuiet=true; }
00080         if(oQuiet)
00081                 con.setMsgMask(static_cast<MsgMask>(MM_ALL & ~MM_INFO &
00082                                                                                         ~MM_MINOR_WARN));
00083         if(oVQuiet)
00084                 con.setMsgMask(static_cast<MsgMask>(MM_ALL & ~MM_INFO & ~MM_MINOR_WARN
00085                                                                                         & ~MM_MAJOR_WARN & ~MM_MINOR_ERR));
00086 
00087         if (oHelp) {
00088                 helpMe(); // Note: this is virtual
00089                 exit(0);
00090         }
00091 
00092 
00093         sysInit();
00094 
00095         setupVirtualPaths(); // setup @home, @data
00096         loadConfig(); // load config files
00097 }
00098 
00099 void CoreApp::DeclareArgs()
00100 {
00101         parameters.declare("--game",    &oGamename,     "");
00102         parameters.declare("-h",                &oHelp,         true);
00103         parameters.declare("--help",    &oHelp,         true);
00104         parameters.declare("-q",                &oQuiet,        true);
00105         parameters.declare("-qq",               &oVQuiet,       true);  
00106 }
00107 
00108 void CoreApp::sysInit()
00109 {
00110         gameinfo = 0;
00111 
00112         filesystem = new FileSystem;
00113 
00114         configfileman = new ConfigFileManager();
00115         settingman = new SettingManager();
00116         settingman->setDomainName(SettingManager::DOM_GLOBAL, "pentagram");
00117         settingman->setCurrentDomain(SettingManager::DOM_GLOBAL);
00118 
00119 }
00120 
00121 void CoreApp::setupVirtualPaths()
00122 {
00123         // setup the 'base' virtual paths:
00124         // @home - $HOME/.pentagram/ - for config files, saves,... (OS dependant)
00125         // @data - /usr/share/pentagram/ - for config files, data,.. (OS dependant)
00126         //       NB: @data can be overwritten by config files
00127         //       this should be a default set by configure (or other build systems)
00128 
00129         bool ok;
00130         std::string home;
00131 #ifdef HAVE_HOME
00132         home = getenv("HOME");
00133         home += "/.pentagram";
00134 #elif defined(WIN32) && defined(WIN32_USE_MY_DOCUMENTS)
00135         TCHAR MyDocumentsPath[MAX_PATH];
00136         SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, MyDocumentsPath);
00137         home = MyDocumentsPath;
00138         home += "\\Pentagram";
00139 //#elif defined(UNDER_CE)
00140 //      home = "\\\\Pierce\\Moo\\UC";
00141 #elif defined(MACOSX)
00142         home = getenv("HOME");
00143         home += "/Library/Application Support/Pentagram";
00144 #else
00145         // TODO: what to do on systems without $HOME?
00146         home = ".";
00147 #endif
00148         ok = filesystem->AddVirtualPath("@home", home, true);
00149         if (!ok) {
00150                 pout << "Error opening default home directory: " << home << std::endl;
00151         } else {
00152                 pout << "Default home path: " << home << std::endl;
00153         }
00154 
00155         std::string data;
00156 #ifdef DATA_PATH
00157         data = DATA_PATH;
00158 #elif defined(MACOSX)
00159     data = macosxResourcePath();
00160 #else
00161         data = "data";
00162 #endif
00163         ok = filesystem->AddVirtualPath("@data", data);
00164         if (!ok) {
00165 #ifndef BUILTIN_DATA
00166                 pout << "Error opening default data directory: " << data << std::endl;
00167                 pout << "Trying custom data path specified in configuration file."
00168                          << std::endl;
00169 #endif
00170         } else {
00171                 pout << "Default data path: " << data << std::endl;
00172         }
00173 }
00174 
00175 // load configuration files
00176 void CoreApp::loadConfig()
00177 {
00178         pout << "Loading configuration files:" << std::endl;
00179 
00180         bool dataconf, homeconf;
00181 
00182         // system-wide config, read-only
00183         dataconf = settingman->readConfigFile("@data/pentagram.ini", true);
00184 
00185         // user config
00186         homeconf = settingman->readConfigFile("@home/pentagram.ini");
00187 
00188         if (!homeconf && !dataconf) {
00189                 pout << "No configuration files found." << std::endl;
00190         } else {
00191 
00192                 if (dataconf)
00193                         pout << "@data/pentagram.ini" << std::endl;
00194                 if (homeconf)
00195                         pout << "@home/pentagram.ini" << std::endl;
00196         }
00197 
00198         //  load pentagram specific data path
00199         std::string data;
00200         if (settingman->get("data", data, SettingManager::DOM_GLOBAL)) {
00201                 pout << "Setting custom data path: " << data << std::endl;
00202                 bool ok = filesystem->AddVirtualPath("@data", data);
00203                 if (!ok) {
00204                         perr << "Error opening data directory." << std::endl;
00205                 }
00206         }
00207 }
00208 
00209 void CoreApp::setupGameList()
00210 {
00211         std::vector<Pentagram::istring> gamelist;
00212         gamelist = settingman->listGames();
00213         con.Print(MM_INFO, "Scanning config file for games:\n");
00214         std::vector<Pentagram::istring>::iterator iter;
00215         Pentagram::istring gamename;
00216 
00217         for (iter = gamelist.begin(); iter != gamelist.end(); ++iter) {
00218                 Pentagram::istring game = *iter;
00219                 GameInfo* info = new GameInfo;
00220                 bool detected = getGameInfo(game, info);
00221 
00222                 // output detected game info
00223                 con.Printf(MM_INFO, "%s: ", game.c_str());
00224                 if (detected) {
00225                         // add game to games map
00226                         games[game] = info;
00227 
00228                         std::string details = info->getPrintDetails();
00229                         con.Print(MM_INFO, details.c_str());
00230                 } else {
00231                         con.Print(MM_INFO, "unknown, skipping");
00232                 }
00233                 con.Print(MM_INFO, "\n");
00234         }
00235 }
00236 
00237 GameInfo* CoreApp::getDefaultGame()
00238 {
00239         Pentagram::istring gamename;
00240 
00241         std::string defaultgame;
00242         bool defaultset = settingman->get("defaultgame", defaultgame,
00243                                                                           SettingManager::DOM_GLOBAL);
00244 
00245         if (oGamename != "") {
00246                 // game specified on commandline
00247                 gamename = oGamename;
00248         } else if (defaultset) {
00249                 // default game specified in config file
00250                 gamename = defaultgame;
00251         } else if (games.size() == 2) {// TODO - Do this in a better method
00252                 // only one game in config file, so pick that
00253                 std::map<Pentagram::istring, GameInfo*>::iterator i;
00254                 for (i = games.begin(); i != games.end(); ++i) {
00255                         if (i->second->name != "pentagram")
00256                                 gamename = i->second->name;
00257                 }
00258         } else if (games.size() == 1) {
00259                 perr << "----------------------------------------" << std::endl
00260                          << "No games set up in configuration. " << std::endl
00261                          << "Please read the README for instructions." << std::endl
00262                          << "----------------------------------------" << std::endl;
00263                 // FIXME - report more useful error message
00264                 return 0;
00265         } else {                
00266                 perr << "Multiple games found in configuration, but no default "
00267                          << "game is selected." << std::endl
00268                          << "Either start Pentagram with the \"--game <gamename>\","
00269                          << std::endl
00270                          << "or set pentagram/defaultgame in pentagram.ini"
00271                          << std::endl;  // FIXME - report more useful error message
00272                 return 0;
00273         }
00274 
00275         pout << "Default game: " << gamename << std::endl;
00276 
00277         GameInfo* info = getGameInfo(gamename);
00278 
00279         if (!info) {
00280                 perr << "Game \"" << gamename << "\" not found." << std::endl;
00281         }
00282 
00283         // We've got a default game name, doesn't mean it will work though
00284         return info;
00285 }
00286 
00287 bool CoreApp::setupGame(GameInfo* info)
00288 {
00289         if (!info) return false;
00290         assert(info->name != "");
00291 
00292         gameinfo = info;
00293 
00294         if (info->name == "pentagram") return false;
00295 
00296         pout << "Selected game: " << info->name << std::endl;
00297         pout << info->getPrintDetails() << std::endl;
00298 
00299         setupGamePaths();
00300 
00301         return true;
00302 }
00303 
00304 void CoreApp::killGame()
00305 {
00306         filesystem->RemoveVirtualPath("@game");
00307         filesystem->RemoveVirtualPath("@work");
00308         filesystem->RemoveVirtualPath("@save");
00309 
00310         configfileman->clearRoot("bindings");
00311         configfileman->clearRoot("language");
00312         configfileman->clearRoot("weapons");
00313         configfileman->clearRoot("armour");
00314         configfileman->clearRoot("monsters");
00315         configfileman->clearRoot("game");
00316         settingman->setCurrentDomain(SettingManager::DOM_GLOBAL);
00317 
00318         gameinfo = 0;
00319 }
00320 
00321 
00322 bool CoreApp::getGameInfo(Pentagram::istring& game, GameInfo* gameinfo)
00323 {
00324         // first try getting the information from the config file
00325         // if that fails, try to autodetect it
00326 
00327         gameinfo->name = game;
00328         gameinfo->type = GameInfo::GAME_UNKNOWN;
00329         gameinfo->version = 0;
00330         gameinfo->language = GameInfo::GAMELANG_UNKNOWN;
00331 
00332         Pentagram::istring gamekey = "settings/";
00333         gamekey += game;
00334 
00335         if (game == "pentagram") {
00336                 gameinfo->type = GameInfo::GAME_PENTAGRAM_MENU;
00337                 gameinfo->language = GameInfo::GAMELANG_ENGLISH;
00338         }
00339         else {
00340                 std::string gametype;
00341                 if (!configfileman->get(gamekey+"/type", gametype))
00342                         gametype = "unknown";
00343                 ToLower(gametype);
00344 
00345                 if (gametype == "u8") {
00346                         gameinfo->type = GameInfo::GAME_U8;
00347                 } else if (gametype == "remorse") {
00348                         gameinfo->type = GameInfo::GAME_REMORSE;
00349                 }
00350         }
00351 
00352         std::string version;
00353         if (!configfileman->get(gamekey+"/version", version))
00354                 version = "unknown";
00355 
00356         std::string language;
00357         if (!configfileman->get(gamekey+"/language", language))
00358                 language = "unknown";
00359         ToLower(language);
00360 
00361 
00363 
00364         if (language == "english") {
00365                 gameinfo->language = GameInfo::GAMELANG_ENGLISH;
00366         } else if (language == "french") {
00367                 gameinfo->language = GameInfo::GAMELANG_FRENCH;
00368         } else if (language == "german") {
00369                 gameinfo->language = GameInfo::GAMELANG_GERMAN;
00370         } else if (language == "spanish") {
00371                 gameinfo->language = GameInfo::GAMELANG_SPANISH;
00372         } else if (language == "japanese") {
00373                 gameinfo->language = GameInfo::GAMELANG_JAPANESE;
00374         }
00375 
00376         if (gameinfo->type == GameInfo::GAME_UNKNOWN ||
00377                 /* gameinfo->version == 0 || */
00378                 gameinfo->language == GameInfo::GAMELANG_UNKNOWN)
00379         {
00380                 std::string path;
00381                 if (!configfileman->get(gamekey+"/path", path)) return false;
00382 
00383                 return GameDetector::detect(path, gameinfo);
00384         }
00385 
00386         if (gameinfo->type == GameInfo::GAME_UNKNOWN) {
00387                 return false;
00388         }
00389                 
00390         return true;
00391 }
00392 
00393 void CoreApp::setupGamePaths()
00394 {
00395         assert(gameinfo);
00396         assert(gameinfo->name != "pentagram");
00397         Pentagram::istring game = gameinfo->name;
00398 
00399         settingman->setDomainName(SettingManager::DOM_GAME, game);
00400         settingman->setCurrentDomain(SettingManager::DOM_GAME);
00401 
00402         // load main game data path
00403         std::string gpath;
00404         settingman->get("path", gpath, SettingManager::DOM_GAME);
00405         filesystem->AddVirtualPath("@game", gpath);
00406         con.Printf(MM_INFO, "Game Path: %s\n", gpath.c_str());
00407 
00408 
00409         // load work path. Default is @home/game-work
00410         // where 'game' in the above is the specified 'game' loaded
00411         std::string work;
00412         if (!settingman->get("work", work, SettingManager::DOM_GAME))
00413                 work = "@home/"+game+"-work";
00414 
00415 #if 0
00416         // force creation if it doesn't exist
00417 
00418         // TODO: I don't like these being created here.
00419         //       I'd prefer them to be created when needed. (-wjp)
00420 
00421         filesystem->AddVirtualPath("@work", work, true);
00422         con.Printf(MM_INFO, "U8 Workdir: %s\n", work.c_str()); 
00423 
00424         // make sure we've got a minimal sane filesystem under there...
00425         filesystem->MkDir("@work/usecode");
00426         filesystem->MkDir("@work/usecode/obj");
00427         filesystem->MkDir("@work/usecode/src");
00428         filesystem->MkDir("@work/usecode/asm");
00429 #endif
00430 
00431         // load savegame path. Default is @home/game-save
00432         std::string save;
00433         if (!settingman->get("save", save, SettingManager::DOM_GAME))
00434                 save = "@home/"+game+"-save";
00435 
00436         // force creation if it doesn't exist
00437         filesystem->AddVirtualPath("@save", save, true);
00438         con.Printf(MM_INFO, "Savegame directory: %s\n", save.c_str());
00439 }
00440 
00441 void CoreApp::ParseArgs(const int argc, const char * const * const argv)
00442 {
00443         parameters.process(argc, argv);
00444 }
00445 
00446 void CoreApp::helpMe()
00447 {
00448         con.Print("\t-h\t\t- quick help menu (this)\n");
00449         con.Print("\t-q\t\t- silence general logging messages\n");
00450         con.Print("\t-qq\t\t- silence general logging messages and\n\t\t\t  non-critical warnings/errors\n");
00451         con.Print("\t--game {name}\t- select a game\n");
00452 }
00453 
00454 GameInfo* CoreApp::getGameInfo(Pentagram::istring game) const
00455 {
00456         std::map<Pentagram::istring, GameInfo*>::const_iterator i;
00457         i = games.find(game);
00458 
00459         if (i != games.end())
00460                 return i->second;
00461         else
00462                 return 0;
00463 }
00464 
00465 
00466 static void ToLower(std::string& str)
00467 {
00468         for (unsigned int i = 0; i < str.size(); ++i)
00469         {
00470 #if (defined(BEOS) || defined(OPENBSD) || defined(CYGWIN) || defined(__MORPHOS__))
00471                 if ((str[i] >= 'A') && (str[i] <= 'Z')) str[i] += 32;
00472 #else
00473                 str[i] = static_cast<char>(std::tolower(str[i]));
00474 #endif
00475         }
00476 
00477 }

Generated on Fri Jul 27 22:27:12 2007 for pentagram by  doxygen 1.4.7