diff --git a/src/game.cpp b/src/game.cpp index 34e030c9..6e81fba0 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -26,6 +26,7 @@ #include "helpers.h" #include "error.h" #include "metadata.h" +#include "parsers.h" #include @@ -38,74 +39,24 @@ namespace boss { std::vector GetGames(const YAML::Node& settings) { vector games; - games.push_back(Game(GAME_TES4)); - games.push_back(Game(GAME_TES5)); - games.push_back(Game(GAME_FO3)); - games.push_back(Game(GAME_FONV)); + if (settings["Games"]) + games = settings["Games"].as< vector >(); + + if (find(games.begin(), games.end(), Game(GAME_TES4)) == games.end()) + games.push_back(Game(GAME_TES4)); + + if (find(games.begin(), games.end(), Game(GAME_TES5)) == games.end()) + games.push_back(Game(GAME_TES5)); + + if (find(games.begin(), games.end(), Game(GAME_FO3)) == games.end()) + games.push_back(Game(GAME_FO3)); + + if (find(games.begin(), games.end(), Game(GAME_FONV)) == games.end()) + games.push_back(Game(GAME_FONV)); - if (settings["Games"]) { - for(YAML::const_iterator it=settings["Games"].begin(), endit=settings["Games"].end(); it != endit; ++it) { - /* - Each game has the following variables: name, type, master, folder, url, path and registry. - - The four archetypes have hardcoded values for each of these. Each game instance must have a unique name and a unique folder. Two or more games can be the same type, have the same master, use the same masterlist, be installed to the same path (eg. multiple install swapping) and have the same registry entry (again, swapping). - - In the YAML file, at least a name must be supplied. If the name matches one of the four archetypes, that is all that is required, but if not, then a type and folder are also required. - */ - - Game game(it->first.as()); - - vector::iterator jt = find(games.begin(), games.end(), game); - - if (jt == games.end() && (!it->second["type"] || !it->second["name"])) - continue; - - string name, master, url, path, registry; - if (it->second["name"]) - name = it->second["name"].as(); - if (it->second["master"]) - master = it->second["master"].as(); - if (it->second["url"]) - url = it->second["url"].as(); - if (it->second["path"]) - path = it->second["path"].as(); - if (it->second["registry"]) - registry = it->second["registry"].as(); - - if (jt != games.end()) - jt->SetDetails(name, master, url, path, registry); - else { - - if (boost::iequals(it->second["type"].as(), Game(GAME_TES4).FolderName())) - game = Game(GAME_TES4, game.FolderName()); - else if (boost::iequals(it->second["type"].as(), Game(GAME_TES5).FolderName())) - game = Game(GAME_TES5, game.FolderName()); - else if (boost::iequals(it->second["type"].as(), Game(GAME_FO3).FolderName())) - game = Game(GAME_FO3, game.FolderName()); - else if (boost::iequals(it->second["type"].as(), Game(GAME_FONV).FolderName())) - game = Game(GAME_FONV, game.FolderName()); - else - continue; - - games.push_back(game.SetDetails(name, master, url, path, registry)); - } - } - } return games; } - void DetectGames(std::vector& detected, std::vector& undetected) { - //detected is also the input vector. - vector::iterator it = detected.begin(); - while (it != detected.end()) { - if (!it->IsInstalled()) { - undetected.push_back(*it); - it = detected.erase(it); - } else - ++it; - } - } - Game::Game() : id(0) {} Game::Game(const std::string& folder) : bossFolderName(folder) {} @@ -219,7 +170,7 @@ namespace boss { } bool Game::operator == (const Game& rhs) const { - return (_name == rhs.Name() || bossFolderName == rhs.FolderName()); + return (boost::iequals(_name, rhs.Name()) || boost::iequals(bossFolderName, rhs.FolderName())); } unsigned int Game::Id() const { @@ -246,7 +197,6 @@ namespace boss { return _masterlistURL; } - fs::path Game::GamePath() const { return gamePath; } diff --git a/src/game.h b/src/game.h index 0f542ddf..669ffed4 100644 --- a/src/game.h +++ b/src/game.h @@ -97,8 +97,6 @@ namespace boss { }; std::vector GetGames(const YAML::Node& settings); - - void DetectGames(std::vector& detected, std::vector& undetected); } #endif diff --git a/src/generators.h b/src/generators.h index 34d4c1ef..b93943ad 100644 --- a/src/generators.h +++ b/src/generators.h @@ -488,8 +488,7 @@ namespace YAML { inline Emitter& operator << (Emitter& out, const boss::Game& rhs) { out << BeginMap - << Key << rhs.FolderName() << Value - << BeginMap + << Key << "folder" << Value << rhs.FolderName() << Key << "name" << Value << rhs.Name() << Key << "master" << Value << rhs.Master() << Key << "url" << Value << rhs.URL() @@ -505,7 +504,7 @@ namespace YAML { else if (rhs.Id() == boss::GAME_FONV) out << Key << Value << boss::Game(boss::GAME_FONV).FolderName(); - out << EndMap << EndMap; + out << EndMap; } inline Emitter& operator << (Emitter& out, const boss::Message& rhs) { diff --git a/src/gui/main.cpp b/src/gui/main.cpp index bdac97d9..a7eead49 100644 --- a/src/gui/main.cpp +++ b/src/gui/main.cpp @@ -140,8 +140,7 @@ bool BossGUI::OnInit() { //Detect installed games. try { - _detectedGames = GetGames(_settings); - DetectGames(_detectedGames, _undetectedGames); + _games = GetGames(_settings); } catch (boss::error& e) { wxMessageBox( FromUTF8(format(loc::translate("Error: Game-specific settings could not be initialised. %1%")) % e.what()), @@ -152,15 +151,7 @@ bool BossGUI::OnInit() { } catch (YAML::Exception& e) { //LOG_ERROR("Error: %s", e.getString().c_str()); wxMessageBox( - FromUTF8(format(loc::translate("Error: Settings parsing failed. %1%")) % e.what()), - translate("BOSS: Error"), - wxOK | wxICON_ERROR, - NULL); - return false; - } - if (_detectedGames.empty()) { - wxMessageBox( - translate("Error: None of the supported games were detected."), + FromUTF8(format(loc::translate("Error: Games' settings parsing failed. %1%")) % e.what()), translate("BOSS: Error"), wxOK | wxICON_ERROR, NULL); @@ -174,18 +165,33 @@ bool BossGUI::OnInit() { target = _settings["Last Game"].as(); if (!target.empty()) { - for (size_t i=0, max=_detectedGames.size(); i < max; ++i) { - if (target == _detectedGames[i].FolderName()) - _game = _detectedGames[i]; + for (size_t i=0, max=_games.size(); i < max; ++i) { + if (target == _games[i].FolderName() && _games[i].IsInstalled()) + _game = _games[i]; } - if (_game == Game()) - _game = _detectedGames[0]; - } else - _game = _detectedGames[0]; + } + if (_game == Game()) { + //Set _game to the first installed game. + for (size_t i=0, max=_games.size(); i < max; ++i) { + if (_games[i].IsInstalled()) { + _game = _games[i]; + break; + } + } + if (_game == Game()) { + wxMessageBox( + translate("Error: None of the supported games were detected."), + translate("BOSS: Error"), + wxOK | wxICON_ERROR, + NULL); + return false; + } + } //Now that game is selected, initialise it. try { _game.Init(); + *find(_games.begin(), _games.end(), _game) = _game; //Sync changes. } catch (boss::error& e) { wxMessageBox( FromUTF8(format(loc::translate("Error: Game-specific settings could not be initialised. %1%")) % e.what()), @@ -196,7 +202,7 @@ bool BossGUI::OnInit() { } //Create launcher window. - Launcher * launcher = new Launcher(wxT("BOSS"), _settings, _game, _detectedGames, _undetectedGames); + Launcher * launcher = new Launcher(wxT("BOSS"), _settings, _game, _games); launcher->SetIcon(wxIconLocation("BOSS.exe")); launcher->Show(); @@ -205,7 +211,7 @@ bool BossGUI::OnInit() { return true; } -Launcher::Launcher(const wxChar *title, YAML::Node& settings, Game& game, const vector& detectedGames, const vector& undetectedGames) : wxFrame(NULL, wxID_ANY, title), _game(game), _settings(settings), _detectedGames(detectedGames), _undetectedGames(undetectedGames) { +Launcher::Launcher(const wxChar *title, YAML::Node& settings, Game& game, vector& games) : wxFrame(NULL, wxID_ANY, title), _game(game), _settings(settings), _games(games) { //Initialise menu items. wxMenuBar * MenuBar = new wxMenuBar(); @@ -231,14 +237,15 @@ Launcher::Launcher(const wxChar *title, YAML::Node& settings, Game& game, const EditMenu->Append(MENU_ShowSettings, translate("&Settings...")); MenuBar->Append(EditMenu, translate("&Edit")); //Game menu - set up initial item states here too. - for (size_t i=0,max=_detectedGames.size(); i < max; ++i) { - wxMenuItem * item = GameMenu->AppendRadioItem(MENU_LowestDynamicGameID + i, FromUTF8(_detectedGames[i].Name())); - if (_game == _detectedGames[i]) + for (size_t i=0,max=_games.size(); i < max; ++i) { + wxMenuItem * item = GameMenu->AppendRadioItem(MENU_LowestDynamicGameID + i, FromUTF8(_games[i].Name())); + if (_game == _games[i]) item->Check(); - Bind(wxEVT_COMMAND_MENU_SELECTED, &Launcher::OnGameChange, this, MENU_LowestDynamicGameID + i); - } - for (size_t i=0,max=undetectedGames.size(); i < max; ++i) { - GameMenu->AppendRadioItem(wxID_ANY, FromUTF8(undetectedGames[i].Name()))->Enable(false); + + if (_games[i].IsInstalled()) + Bind(wxEVT_COMMAND_MENU_SELECTED, &Launcher::OnGameChange, this, MENU_LowestDynamicGameID + i); + else + item->Enable(false); } MenuBar->Append(GameMenu, translate("&Game")); //About menu @@ -292,6 +299,8 @@ void Launcher::OnClose(wxCloseEvent& event) { _settings["Last Game"] = _game.FolderName(); + + _settings["Games"] = _games; //Save settings. YAML::Emitter yout; @@ -660,16 +669,15 @@ void Launcher::OnViewLastReport(wxCommandEvent& event) { } void Launcher::OnOpenSettings(wxCommandEvent& event) { - vector games = _detectedGames; - games.insert(games.end(), _undetectedGames.begin(), _undetectedGames.end()); - SettingsFrame *settings = new SettingsFrame(this, translate("BOSS: Settings"), _settings, games); + SettingsFrame *settings = new SettingsFrame(this, translate("BOSS: Settings"), _settings, _games); settings->ShowModal(); } void Launcher::OnGameChange(wxCommandEvent& event) { - _game = _detectedGames[event.GetId() - MENU_LowestDynamicGameID]; + _game = _games[event.GetId() - MENU_LowestDynamicGameID]; try { _game.Init(); //In case it hasn't already been done. + *find(_games.begin(), _games.end(), _game) = _game; //Sync changes. } catch (boss::error& e) { wxMessageBox( FromUTF8(format(loc::translate("Error: Game-specific settings could not be initialised. %1%")) % e.what()), diff --git a/src/gui/main.h b/src/gui/main.h index c317fd52..99aa36c0 100644 --- a/src/gui/main.h +++ b/src/gui/main.h @@ -37,14 +37,14 @@ public: private: wxLocale * wxLoc; - boss::Game _game; YAML::Node _settings; - std::vector _detectedGames, _undetectedGames; + std::vector _games; + boss::Game _game; }; class Launcher : public wxFrame { public: - Launcher(const wxChar *title, YAML::Node& settings, boss::Game& inGame, const std::vector& detectedGames, const std::vector& undetectedGames); + Launcher(const wxChar *title, YAML::Node& settings, boss::Game& inGame, std::vector& games); void OnSortPlugins(wxCommandEvent& event); void OnEditMetadata(wxCommandEvent& event); @@ -63,8 +63,7 @@ private: boss::Game& _game; YAML::Node& _settings; //BOSS Settings. - const std::vector& _detectedGames; - const std::vector& _undetectedGames; + std::vector& _games; static bool AlphaSortPlugins(const boss::Plugin& lhs, const boss::Plugin& rhs); }; diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 251c9041..0170a363 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -32,7 +32,7 @@ END_EVENT_TABLE() using namespace std; -SettingsFrame::SettingsFrame(wxWindow *parent, const wxString& title, YAML::Node& settings, const std::vector& games) : wxDialog(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER), _settings(settings), _games(games) { +SettingsFrame::SettingsFrame(wxWindow *parent, const wxString& title, YAML::Node& settings, std::vector& games) : wxDialog(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER), _settings(settings), _games(games) { //Initialise drop-down list contents. wxString DebugVerbosity[] = { diff --git a/src/gui/settings.h b/src/gui/settings.h index b927e665..22fa2235 100644 --- a/src/gui/settings.h +++ b/src/gui/settings.h @@ -32,7 +32,7 @@ class SettingsFrame : public wxDialog { public: - SettingsFrame(wxWindow *parent, const wxString& title, YAML::Node& settings, const std::vector& games); + SettingsFrame(wxWindow *parent, const wxString& title, YAML::Node& settings, std::vector& games); void OnQuit(wxCommandEvent& event); void SetDefaultValues(); DECLARE_EVENT_TABLE() @@ -48,6 +48,6 @@ private: wxTextCtrl *FONVURL; YAML::Node& _settings; - std::vector _games; + std::vector& _games; }; #endif diff --git a/src/parsers.h b/src/parsers.h index a109394e..092ce55a 100644 --- a/src/parsers.h +++ b/src/parsers.h @@ -52,6 +52,57 @@ namespace YAML { // Parser /////////////////////// + template<> + struct convert { + static Node encode(const boss::Game& rhs) { + Node node; + + node["type"] = boss::Game(rhs.Id()).FolderName(); + node["name"] = rhs.Name(); + node["folder"] = rhs.FolderName(); + node["master"] = rhs.Master(); + node["url"] = rhs.URL(); + node["path"] = rhs.GamePath().string(); + node["registry"] = rhs.RegistryKey(); + + return node; + } + + static bool decode(const Node& node, boss::Game& rhs) { + if (!node.IsMap() || !node["folder"] || !node["type"]) + return false; + + boss::Game game(node["folder"].as()); + + if (node["type"].as() == boss::Game(boss::GAME_TES4).FolderName()) + rhs = boss::Game(boss::GAME_TES4, game.FolderName()); + else if (node["type"].as() == boss::Game(boss::GAME_TES5).FolderName()) + rhs = boss::Game(boss::GAME_TES5, game.FolderName()); + else if (node["type"].as() == boss::Game(boss::GAME_FO3).FolderName()) + rhs = boss::Game(boss::GAME_FO3, game.FolderName()); + else if (node["type"].as() == boss::Game(boss::GAME_FONV).FolderName()) + rhs = boss::Game(boss::GAME_FONV, game.FolderName()); + else + return false; + + std::string name, master, url, path, registry; + if (node["name"]) + name = node["name"].as(); + if (node["master"]) + master = node["master"].as(); + if (node["url"]) + url = node["url"].as(); + if (node["path"]) + path = node["path"].as(); + if (node["registry"]) + registry = node["registry"].as(); + + rhs.SetDetails(name, master, url, path, registry); + + return true; + } + }; + template<> struct convert { static Node encode(const boss::Message& rhs) {