From 1e39f4a5c8f22108936f991f5f69a5ea072e219b Mon Sep 17 00:00:00 2001 From: WrinklyNinja Date: Fri, 10 May 2013 17:52:54 +0100 Subject: [PATCH] Metadata editor now saves userlist on 'OK' exit, fixed linker errors. --- src/gui/editor.cpp | 35 ++++-------- src/gui/editor.h | 9 ++- src/gui/ids.h | 2 + src/gui/main.cpp | 137 ++++++++++++++++++--------------------------- src/gui/main.h | 2 - src/metadata.cpp | 33 +++++++++++ src/network.cpp | 0 src/network.h | 39 +++++++++++++ src/parsers.h | 40 ++----------- 9 files changed, 150 insertions(+), 147 deletions(-) create mode 100644 src/network.cpp create mode 100644 src/network.h diff --git a/src/gui/editor.cpp b/src/gui/editor.cpp index 3ff5e873..1a74199a 100644 --- a/src/gui/editor.cpp +++ b/src/gui/editor.cpp @@ -22,8 +22,8 @@ */ #include "editor.h" +#include "../parsers.h" -#include #include #include @@ -65,7 +65,7 @@ wxString State[2] = { translate("Remove") }; -Editor::Editor(wxWindow *parent, const wxString& title) : wxFrame(parent, wxID_ANY, title) { +Editor::Editor(wxWindow *parent, const wxString& title, const boss::Game& game, const std::vector& basePlugins, std::vector& editedPlugins, bool showRecalcButton) : wxFrame(parent, wxID_ANY, title), _game(game), _basePlugins(basePlugins), _editedPlugins(editedPlugins) { //Initialise child windows. listBook = new wxNotebook(this, BOOK_Lists); @@ -189,33 +189,22 @@ Editor::Editor(wxWindow *parent, const wxString& title) : wxFrame(parent, wxID_A mainBox->Add(hbox6, 0, wxALIGN_RIGHT); bigBox->Add(mainBox, 2, wxEXPAND|wxTOP|wxBOTTOM|wxRIGHT, 10); - - SetBackgroundColour(wxColour(255,255,255)); - SetIcon(wxIconLocation("BOSS.exe")); - SetSizerAndFit(bigBox); -} - -void Editor::SetList(const std::vector& basePlugins, const std::vector& editedPlugins) { - _basePlugins = basePlugins; - _editedPlugins = editedPlugins; - //Fill pluginList with the contents of basePlugins. for (int i=0, max=_basePlugins.size(); i < max; ++i) { pluginList->InsertItem(i, FromUTF8(_basePlugins[i].Name())); } pluginList->SetColumnWidth(0, wxLIST_AUTOSIZE); - Layout(); -} -void Editor::IsSorted(bool sorted) { - if (sorted) { - applyBtn->SetLabel(translate("Apply Load Order")); - } else { + if (!showRecalcButton) { recalcBtn->Show(false); applyBtn->SetLabel(translate("Save Changes")); } - Layout(); + + SetBackgroundColour(wxColour(255,255,255)); + SetIcon(wxIconLocation("BOSS.exe")); + + SetSizerAndFit(bigBox); } void Editor::OnPluginSelect(wxListEvent& event) { @@ -644,18 +633,18 @@ void Editor::OnRowSelect(wxListEvent& event) { void Editor::OnQuit(wxCommandEvent& event) { if (event.GetId() == BUTTON_Apply) { //Save edits to userlist. - /* YAML::Emitter yout; + YAML::Emitter yout; yout.SetIndent(2); yout << YAML::BeginMap << YAML::Key << "plugins" << YAML::Value << _editedPlugins << YAML::EndMap; - ofstream out(game.UserlistPath().string().c_str()); + ofstream out(_game.UserlistPath().string().c_str()); out << yout.c_str(); out.close(); - */ + if (recalcBtn->IsShown()) { - //Signal that the load order should be written. + //Signal that the load order should be written and the log opened. } } Close(); diff --git a/src/gui/editor.h b/src/gui/editor.h index 98502d0c..7fb7d8ab 100644 --- a/src/gui/editor.h +++ b/src/gui/editor.h @@ -34,10 +34,7 @@ class Editor : public wxFrame { public: - Editor(wxWindow *parent, const wxString& title); - - void SetList(const std::vector& basePlugins, const std::vector& editedPlugins); - void IsSorted(bool sorted); + Editor(wxWindow *parent, const wxString& title, const boss::Game& game, const std::vector& basePlugins, std::vector& editedPlugins, bool showRecalcButton); void OnPluginSelect(wxListEvent& event); void OnEnabledToggle(wxCommandEvent& event); @@ -69,7 +66,9 @@ private: wxSpinCtrl * prioritySpin; wxStaticText * pluginText; - std::vector _basePlugins, _editedPlugins; + const boss::Game& _game; + const std::vector _basePlugins; + std::vector _editedPlugins; void ApplyEdits(const wxString& plugin); diff --git a/src/gui/ids.h b/src/gui/ids.h index b18cfe46..f47cf3e4 100644 --- a/src/gui/ids.h +++ b/src/gui/ids.h @@ -28,6 +28,8 @@ #include #include "wx/wxprec.h" +#include "error.h" //Something in wxWidgets has a #define that conflicts with the error codes. + #ifndef WX_PRECOMP # include "wx/wx.h" #endif diff --git a/src/gui/main.cpp b/src/gui/main.cpp index a57771b9..c1552366 100644 --- a/src/gui/main.cpp +++ b/src/gui/main.cpp @@ -20,13 +20,15 @@ along with BOSS. If not, see . */ +#include "main.h" +#include "settings.h" +#include "editor.h" #include "../globals.h" #include "../metadata.h" #include "../parsers.h" -#include "main.h" -#include "settings.h" -#include "editor.h" +#include "../error.h" +#include "../helpers.h" #include #include @@ -256,8 +258,28 @@ Launcher::Launcher(const wxChar *title, YAML::Node& settings, Game& game, const if (!fs::exists(_game.ResultsPath())) ViewButton->Enable(false); - - DisableUndetectedGames(); + + //Disable the menu items for the undetected games. + GameMenu->FindItem(MENU_Oblivion)->Enable(false); + GameMenu->FindItem(MENU_Nehrim)->Enable(false); + GameMenu->FindItem(MENU_Skyrim)->Enable(false); + GameMenu->FindItem(MENU_Fallout3)->Enable(false); + GameMenu->FindItem(MENU_FalloutNewVegas)->Enable(false); + for (size_t i=0; i < _detectedGames.size(); i++) { + if (_detectedGames[i] == GAME_TES4) + GameMenu->FindItem(MENU_Oblivion)->Enable(); + else if (_detectedGames[i] == GAME_NEHRIM) + GameMenu->FindItem(MENU_Nehrim)->Enable(); + else if (_detectedGames[i] == GAME_TES5) + GameMenu->FindItem(MENU_Skyrim)->Enable(); + else if (_detectedGames[i] == GAME_FO3) + GameMenu->FindItem(MENU_Fallout3)->Enable(); + else if (_detectedGames[i] == GAME_FONV) + GameMenu->FindItem(MENU_FalloutNewVegas)->Enable(); + } + + //Set title bar text. + SetTitle(FromUTF8("BOSS - " + _game.Name())); //Now set the layout and sizes. SetMenuBar(MenuBar); @@ -330,6 +352,7 @@ void Launcher::OnSortPlugins(wxCommandEvent& event) { translate("BOSS: Error"), wxOK | wxICON_ERROR, this); + return; } if (mlist["globals"]) mlist_messages = mlist["globals"].as< list >(); @@ -353,6 +376,7 @@ void Launcher::OnSortPlugins(wxCommandEvent& event) { translate("BOSS: Error"), wxOK | wxICON_ERROR, this); + return; } if (ulist["globals"]) ulist_messages = ulist["globals"].as< list >(); @@ -398,7 +422,7 @@ void Launcher::OnSortPlugins(wxCommandEvent& event) { for (list::iterator it=plugins.begin(), endIt=plugins.end(); it != endIt; ++it) { try { - it->EvalAllConditions(game); + it->EvalAllConditions(game); } catch (boss::error& e) { //LOG_ERROR("Error: %s", e.what()); wxMessageBox( @@ -406,6 +430,7 @@ void Launcher::OnSortPlugins(wxCommandEvent& event) { translate("BOSS: Error"), wxOK | wxICON_ERROR, this); + return; } } @@ -491,64 +516,42 @@ void Launcher::OnSortPlugins(wxCommandEvent& event) { progDia->Destroy(); - //Create editor window. - Editor *editor = new Editor(this, translate("BOSS: Metadata Editor")); - - //The 'plugins' list contains the merged plugin info, but we want to pass a vector of plugins with only masterlist info in its place, so have to construct that. - vector pluginVec; - for (list::const_iterator it=plugins.begin(), endit=plugins.end(); it != endit; ++it) { - boss::Plugin p = *it; - list::iterator pos = std::find(mlist_plugins.begin(), mlist_plugins.end(), *it); - - if (pos != mlist_plugins.end()) - p.Merge(*pos); - - pluginVec.push_back(p); - } - vector ulistPluginVec(ulist_plugins.begin(), ulist_plugins.end()); - - //Pass plugin lists to editor window. - editor->SetList(pluginVec, ulistPluginVec); - - //Tell the editor that it's not being shown as part of the load order sort process. - editor->IsSorted(true); - - editor->Show(); - + //Now a results report definitely exists. ViewButton->Enable(true); } void Launcher::OnEditMetadata(wxCommandEvent& event) { - //Parse the userlist and masterlist and get a list of installed plugins. - vector plugins; - YAML::Node mlist, ulist; - vector mlist_plugins, ulist_plugins; - + //Should probably check for masterlist updates before opening metadata editor. + vector installed, mlist_plugins, ulist_plugins; + + //Scan for installed plugins. for (fs::directory_iterator it(_game.DataPath()); it != fs::directory_iterator(); ++it) { if (fs::is_regular_file(it->status()) && (it->path().extension().string() == ".esp" || it->path().extension().string() == ".esm")) { - - boss::Plugin plugin(it->path().filename().string()); - plugins.push_back(plugin); + installed.push_back(boss::Plugin(it->path().filename().string())); } } + //Parse masterlist. if (fs::exists(_game.MasterlistPath())) { + YAML::Node mlist; try { mlist = YAML::LoadFile(_game.MasterlistPath().string()); } catch (YAML::ParserException& e) { //LOG_ERROR("Error: %s", e.getString().c_str()); wxMessageBox( - FromUTF8(format(loc::translate("Error: Masterlist parsing failed. %1%")) % e.what()), - translate("BOSS: Error"), - wxOK | wxICON_ERROR, - this); + FromUTF8(format(loc::translate("Error: Masterlist parsing failed. %1%")) % e.what()), + translate("BOSS: Error"), + wxOK | wxICON_ERROR, + this); } if (mlist["plugins"]) mlist_plugins = mlist["plugins"].as< vector >(); } + //Parse userlist. if (fs::exists(_game.UserlistPath())) { + YAML::Node ulist; try { ulist = YAML::LoadFile(_game.UserlistPath().string()); } catch (YAML::ParserException& e) { @@ -563,31 +566,25 @@ void Launcher::OnEditMetadata(wxCommandEvent& event) { ulist_plugins = ulist["plugins"].as< vector >(); } - //Cut out any plugins in the masterlist that aren't installed or in the userlist. - //Merge down to the plugin list so that it holds all uneditable metadata. - list slim_mlist; - + //Merge the masterlist down into the installed mods list. for (vector::const_iterator it=mlist_plugins.begin(), endit=mlist_plugins.end(); it != endit; ++it) { - vector::iterator pos = std::find(plugins.begin(), plugins.end(), *it); + vector::iterator pos = find(installed.begin(), installed.end(), *it); - if (pos != plugins.end()) + if (pos != installed.end()) pos->Merge(*it); - else if (std::find(ulist_plugins.begin(), ulist_plugins.end(), *it) != ulist_plugins.end()) - plugins.push_back(*it); + } + + //Add empty entries for any userlist entries that aren't installed. + for (vector::const_iterator it=ulist_plugins.begin(), endit=ulist_plugins.end(); it != endit; ++it) { + if (find(installed.begin(), installed.end(), *it) == installed.end()) + installed.push_back(boss::Plugin(it->Name())); } //Sort into alphabetical order. - std::sort(plugins.begin(), plugins.end(), AlphaSortPlugins); - std::sort(ulist_plugins.begin(), ulist_plugins.end(), AlphaSortPlugins); + std::sort(installed.begin(), installed.end(), AlphaSortPlugins); //Create editor window. - Editor *editor = new Editor(this, translate("BOSS: Metadata Editor")); - - //Pass plugin lists to editor window. - editor->SetList(plugins, ulist_plugins); - - //Tell the editor that it's not being shown as part of the load order sort process. - editor->IsSorted(false); + Editor *editor = new Editor(this, translate("BOSS: Metadata Editor"), _game, installed, ulist_plugins, false); editor->Show(); } @@ -671,28 +668,6 @@ void Launcher::OnAbout(wxCommandEvent& event) { wxAboutBox(aboutInfo); } -void Launcher::DisableUndetectedGames() { - GameMenu->FindItem(MENU_Oblivion)->Enable(false); - GameMenu->FindItem(MENU_Nehrim)->Enable(false); - GameMenu->FindItem(MENU_Skyrim)->Enable(false); - GameMenu->FindItem(MENU_Fallout3)->Enable(false); - GameMenu->FindItem(MENU_FalloutNewVegas)->Enable(false); - for (size_t i=0; i < _detectedGames.size(); i++) { - if (_detectedGames[i] == GAME_TES4) - GameMenu->FindItem(MENU_Oblivion)->Enable(); - else if (_detectedGames[i] == GAME_NEHRIM) - GameMenu->FindItem(MENU_Nehrim)->Enable(); - else if (_detectedGames[i] == GAME_TES5) - GameMenu->FindItem(MENU_Skyrim)->Enable(); - else if (_detectedGames[i] == GAME_FO3) - GameMenu->FindItem(MENU_Fallout3)->Enable(); - else if (_detectedGames[i] == GAME_FONV) - GameMenu->FindItem(MENU_FalloutNewVegas)->Enable(); - } - - SetTitle(FromUTF8("BOSS - " + _game.Name())); -} - bool Launcher::AlphaSortPlugins(const boss::Plugin& lhs, const boss::Plugin& rhs) { return boost::to_lower_copy(lhs.Name()) < boost::to_lower_copy(rhs.Name()); } diff --git a/src/gui/main.h b/src/gui/main.h index 5bdbcc0c..40b48469 100644 --- a/src/gui/main.h +++ b/src/gui/main.h @@ -64,8 +64,6 @@ private: boss::Game& _game; YAML::Node& _settings; //BOSS Settings. const std::vector& _detectedGames; - - void DisableUndetectedGames(); static bool AlphaSortPlugins(const boss::Plugin& lhs, const boss::Plugin& rhs); }; diff --git a/src/metadata.cpp b/src/metadata.cpp index 196be232..992c46d6 100644 --- a/src/metadata.cpp +++ b/src/metadata.cpp @@ -23,6 +23,7 @@ #include "helpers.h" #include "metadata.h" +#include "parsers.h" #include @@ -106,6 +107,38 @@ namespace boss { data = d; } + bool ConditionalData::EvalCondition(boss::Game& game) const { + if (condition.empty()) + return true; + + boost::unordered_map::const_iterator it = game.conditionCache.find(boost::to_lower_copy(condition)); + if (it != game.conditionCache.end()) + return it->second; + + condition_grammar grammar; + boost::spirit::qi::space_type skipper; + std::string::const_iterator begin, end; + bool eval; + + grammar.SetGame(game); + begin = condition.begin(); + end = condition.end(); + + bool r; + try { + r = boost::spirit::qi::phrase_parse(begin, end, grammar, skipper, eval); + } catch (boss::error& e) { + throw boss::error(boss::ERROR_PATH_READ_FAIL, "Parsing of condition \"" + condition + "\" failed: " + e.what()); + } + + if (!r || begin != end) + throw boss::error(boss::ERROR_PATH_READ_FAIL, "Parsing of condition \"" + condition + "\" failed!"); + + game.conditionCache.emplace(boost::to_lower_copy(condition), eval); + + return eval; + } + Message::Message() {} Message::Message(const std::string& t, const std::string& cont) diff --git a/src/network.cpp b/src/network.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/network.h b/src/network.h new file mode 100644 index 00000000..c51e2c27 --- /dev/null +++ b/src/network.h @@ -0,0 +1,39 @@ +/* BOSS + + A plugin load order optimiser for games that use the esp/esm plugin system. + + Copyright (C) 2012-2013 WrinklyNinja + + This file is part of BOSS. + + BOSS 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 3 of + the License, or (at your option) any later version. + + BOSS 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 BOSS. If not, see + . +*/ + +#ifndef __BOSS_NETWORK__ +#define __BOSS_NETWORK__ + +namespace boss { + + /* Need to be able to: + + - Check if there's an internet connection. + - Get the revision number for the local masterlist, if present. + - Get the revision number for the remote masterlist. + - Compare the two revision numbers. + - Download the remote masterlist if it has a higher revision number. + - Test that the downloaded masterlist is valid by parsing it to see if any errors come up. + - If the downloaded masterlist is parsed OK, replace the local masterlist with it. + */ +} diff --git a/src/parsers.h b/src/parsers.h index 92c85e9d..d08f0bc0 100644 --- a/src/parsers.h +++ b/src/parsers.h @@ -211,7 +211,7 @@ namespace YAML { out << EndSeq; } - Emitter& operator << (Emitter& out, const boss::Message& rhs) { + inline Emitter& operator << (Emitter& out, const boss::Message& rhs) { out << BeginMap << Key << "type" << Value << rhs.Type() << Key << "content" << Value << rhs.Content(); @@ -225,7 +225,7 @@ namespace YAML { out << EndMap; } - Emitter& operator << (Emitter& out, const boss::File& rhs) { + inline Emitter& operator << (Emitter& out, const boss::File& rhs) { if (!rhs.IsConditional() && rhs.DisplayName().empty()) out << rhs.Name(); else { @@ -242,7 +242,7 @@ namespace YAML { } } - Emitter& operator << (Emitter& out, const boss::Tag& rhs) { + inline Emitter& operator << (Emitter& out, const boss::Tag& rhs) { if (!rhs.IsConditional()) out << rhs.PrefixedName(); else { @@ -253,7 +253,7 @@ namespace YAML { } } - Emitter& operator << (Emitter& out, const boss::Plugin& rhs) { + inline Emitter& operator << (Emitter& out, const boss::Plugin& rhs) { //if (!rhs.HasNameOnly()) { out << BeginMap @@ -521,37 +521,5 @@ namespace boss { return !boost::contains(parent_path, "../../"); } }; - - bool ConditionalData::EvalCondition(boss::Game& game) const { - if (condition.empty()) - return true; - - boost::unordered_map::const_iterator it = game.conditionCache.find(boost::to_lower_copy(condition)); - if (it != game.conditionCache.end()) - return it->second; - - condition_grammar grammar; - qi::space_type skipper; - std::string::const_iterator begin, end; - bool eval; - - grammar.SetGame(game); - begin = condition.begin(); - end = condition.end(); - - bool r; - try { - r = qi::phrase_parse(begin, end, grammar, skipper, eval); - } catch (boss::error& e) { - throw boss::error(boss::ERROR_PATH_READ_FAIL, "Parsing of condition \"" + condition + "\" failed: " + e.what()); - } - - if (!r || begin != end) - throw boss::error(boss::ERROR_PATH_READ_FAIL, "Parsing of condition \"" + condition + "\" failed!"); - - game.conditionCache.emplace(boost::to_lower_copy(condition), eval); - - return eval; - } } #endif