mirror of
https://github.com/loot/libloot.git
synced 2026-07-27 14:16:01 -07:00
Game install paths now recorded.
BOSS now uses the Registry key of a game to get its install path only if the path is empty or invalid, and saves the install path on exit. GUI settings window is currently outdated, will need a new interface for editing games' settings.
This commit is contained in:
+16
-66
@@ -26,6 +26,7 @@
|
||||
#include "helpers.h"
|
||||
#include "error.h"
|
||||
#include "metadata.h"
|
||||
#include "parsers.h"
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
@@ -38,74 +39,24 @@ namespace boss {
|
||||
std::vector<Game> GetGames(const YAML::Node& settings) {
|
||||
vector<Game> 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<Game> >();
|
||||
|
||||
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<string>());
|
||||
|
||||
vector<Game>::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<string>();
|
||||
if (it->second["master"])
|
||||
master = it->second["master"].as<string>();
|
||||
if (it->second["url"])
|
||||
url = it->second["url"].as<string>();
|
||||
if (it->second["path"])
|
||||
path = it->second["path"].as<string>();
|
||||
if (it->second["registry"])
|
||||
registry = it->second["registry"].as<string>();
|
||||
|
||||
if (jt != games.end())
|
||||
jt->SetDetails(name, master, url, path, registry);
|
||||
else {
|
||||
|
||||
if (boost::iequals(it->second["type"].as<string>(), Game(GAME_TES4).FolderName()))
|
||||
game = Game(GAME_TES4, game.FolderName());
|
||||
else if (boost::iequals(it->second["type"].as<string>(), Game(GAME_TES5).FolderName()))
|
||||
game = Game(GAME_TES5, game.FolderName());
|
||||
else if (boost::iequals(it->second["type"].as<string>(), Game(GAME_FO3).FolderName()))
|
||||
game = Game(GAME_FO3, game.FolderName());
|
||||
else if (boost::iequals(it->second["type"].as<string>(), 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<Game>& detected, std::vector<Game>& undetected) {
|
||||
//detected is also the input vector.
|
||||
vector<Game>::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;
|
||||
}
|
||||
|
||||
@@ -97,8 +97,6 @@ namespace boss {
|
||||
};
|
||||
|
||||
std::vector<Game> GetGames(const YAML::Node& settings);
|
||||
|
||||
void DetectGames(std::vector<Game>& detected, std::vector<Game>& undetected);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+2
-3
@@ -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) {
|
||||
|
||||
+39
-31
@@ -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<string>();
|
||||
|
||||
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<boss::Game>& detectedGames, const vector<boss::Game>& 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<boss::Game>& 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<boss::Game> 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()),
|
||||
|
||||
+4
-5
@@ -37,14 +37,14 @@ public:
|
||||
private:
|
||||
wxLocale * wxLoc;
|
||||
|
||||
boss::Game _game;
|
||||
YAML::Node _settings;
|
||||
std::vector<boss::Game> _detectedGames, _undetectedGames;
|
||||
std::vector<boss::Game> _games;
|
||||
boss::Game _game;
|
||||
};
|
||||
|
||||
class Launcher : public wxFrame {
|
||||
public:
|
||||
Launcher(const wxChar *title, YAML::Node& settings, boss::Game& inGame, const std::vector<boss::Game>& detectedGames, const std::vector<boss::Game>& undetectedGames);
|
||||
Launcher(const wxChar *title, YAML::Node& settings, boss::Game& inGame, std::vector<boss::Game>& 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<boss::Game>& _detectedGames;
|
||||
const std::vector<boss::Game>& _undetectedGames;
|
||||
std::vector<boss::Game>& _games;
|
||||
|
||||
static bool AlphaSortPlugins(const boss::Plugin& lhs, const boss::Plugin& rhs);
|
||||
};
|
||||
|
||||
@@ -32,7 +32,7 @@ END_EVENT_TABLE()
|
||||
|
||||
using namespace std;
|
||||
|
||||
SettingsFrame::SettingsFrame(wxWindow *parent, const wxString& title, YAML::Node& settings, const std::vector<boss::Game>& 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<boss::Game>& games) : wxDialog(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER), _settings(settings), _games(games) {
|
||||
|
||||
//Initialise drop-down list contents.
|
||||
wxString DebugVerbosity[] = {
|
||||
|
||||
+2
-2
@@ -32,7 +32,7 @@
|
||||
|
||||
class SettingsFrame : public wxDialog {
|
||||
public:
|
||||
SettingsFrame(wxWindow *parent, const wxString& title, YAML::Node& settings, const std::vector<boss::Game>& games);
|
||||
SettingsFrame(wxWindow *parent, const wxString& title, YAML::Node& settings, std::vector<boss::Game>& games);
|
||||
void OnQuit(wxCommandEvent& event);
|
||||
void SetDefaultValues();
|
||||
DECLARE_EVENT_TABLE()
|
||||
@@ -48,6 +48,6 @@ private:
|
||||
wxTextCtrl *FONVURL;
|
||||
|
||||
YAML::Node& _settings;
|
||||
std::vector<boss::Game> _games;
|
||||
std::vector<boss::Game>& _games;
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -52,6 +52,57 @@ namespace YAML {
|
||||
// Parser
|
||||
///////////////////////
|
||||
|
||||
template<>
|
||||
struct convert<boss::Game> {
|
||||
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<std::string>());
|
||||
|
||||
if (node["type"].as<std::string>() == boss::Game(boss::GAME_TES4).FolderName())
|
||||
rhs = boss::Game(boss::GAME_TES4, game.FolderName());
|
||||
else if (node["type"].as<std::string>() == boss::Game(boss::GAME_TES5).FolderName())
|
||||
rhs = boss::Game(boss::GAME_TES5, game.FolderName());
|
||||
else if (node["type"].as<std::string>() == boss::Game(boss::GAME_FO3).FolderName())
|
||||
rhs = boss::Game(boss::GAME_FO3, game.FolderName());
|
||||
else if (node["type"].as<std::string>() == 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<std::string>();
|
||||
if (node["master"])
|
||||
master = node["master"].as<std::string>();
|
||||
if (node["url"])
|
||||
url = node["url"].as<std::string>();
|
||||
if (node["path"])
|
||||
path = node["path"].as<std::string>();
|
||||
if (node["registry"])
|
||||
registry = node["registry"].as<std::string>();
|
||||
|
||||
rhs.SetDetails(name, master, url, path, registry);
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct convert<boss::Message> {
|
||||
static Node encode(const boss::Message& rhs) {
|
||||
|
||||
Reference in New Issue
Block a user