Refactored plugin loading code into new class.

PluginLoader now handles the details of how plugins are read, cleaning
up the Game and Plugin classes a little.
This commit is contained in:
Oliver Hamlet
2015-06-13 22:29:45 +01:00
parent 825e77d5ae
commit a400162811
6 changed files with 226 additions and 90 deletions
+2
View File
@@ -91,6 +91,7 @@ set (LOOT_SRC "${CMAKE_SOURCE_DIR}/src/backend/metadata/conditional_metadata.c
"${CMAKE_SOURCE_DIR}/src/backend/metadata_list.cpp"
"${CMAKE_SOURCE_DIR}/src/backend/masterlist.cpp"
"${CMAKE_SOURCE_DIR}/src/backend/plugin.cpp"
"${CMAKE_SOURCE_DIR}/src/backend/plugin_loader.cpp"
"${CMAKE_SOURCE_DIR}/src/backend/helpers.cpp"
"${CMAKE_SOURCE_DIR}/src/backend/language.cpp"
"${CMAKE_SOURCE_DIR}/src/backend/version.cpp"
@@ -113,6 +114,7 @@ set (LOOT_HEADERS "${CMAKE_SOURCE_DIR}/src/backend/metadata/condition_grammar.h"
"${CMAKE_SOURCE_DIR}/src/backend/metadata_list.h"
"${CMAKE_SOURCE_DIR}/src/backend/masterlist.h"
"${CMAKE_SOURCE_DIR}/src/backend/plugin.h"
"${CMAKE_SOURCE_DIR}/src/backend/plugin_loader.h"
"${CMAKE_SOURCE_DIR}/src/backend/helpers.h"
"${CMAKE_SOURCE_DIR}/src/backend/language.h"
"${CMAKE_SOURCE_DIR}/src/backend/version.h"
+1 -14
View File
@@ -51,20 +51,7 @@ namespace loot {
gameSettings.RegistryKey());
}
Game::Game(const unsigned int gameCode, const std::string& folder) : GameSettings(gameCode, folder), gh(nullptr) {
if (Id() == Game::tes4) {
espm_settings = espm::Settings("tes4");
}
else if (Id() == Game::tes5) {
espm_settings = espm::Settings("tes5");
}
else if (Id() == Game::fo3) {
espm_settings = espm::Settings("fo3");
}
else if (Id() == Game::fonv) {
espm_settings = espm::Settings("fonv");
}
}
Game::Game(const unsigned int gameCode, const std::string& folder) : GameSettings(gameCode, folder), gh(nullptr) {}
Game::~Game() {
lo_destroy_handle(gh);
+1 -5
View File
@@ -40,7 +40,6 @@
#include <boost/locale.hpp>
#include <api/libloadorder.h>
#include <src/libespm.h>
#include <yaml-cpp/yaml.h>
namespace loot {
@@ -62,12 +61,11 @@ namespace loot {
void GetLoadOrder(std::list<std::string>& loadOrder) const;
void SetLoadOrder(const std::list<std::string>& loadOrder) const; //Modifies game load order, even though const.
void SetLoadOrder(const char * const * const loadOrder, const size_t numPlugins) const; // For API.
void RefreshActivePluginsList();
void RedatePlugins(); //Change timestamps to match load order (Skyrim only).
void LoadPlugins(bool headersOnly); //Loads all installed plugins.
bool HasBeenLoaded(); // Checks if the game's plugins have already been loaded.
std::list<Plugin> Sort(const unsigned int language, std::function<void(const std::string&)> progressCallback);
//Caches for condition results, active plugins and CRCs.
@@ -79,8 +77,6 @@ namespace loot {
Masterlist masterlist;
MetadataList userlist;
std::unordered_map<std::string, Plugin> plugins; //Map so that plugin data can be edited.
espm::Settings espm_settings;
private:
boost::filesystem::path _gameLocalDataPath; // Path to the game's folder in %LOCALAPPDATA%.
+18 -71
View File
@@ -23,11 +23,10 @@
*/
#include "plugin.h"
#include "plugin_loader.h"
#include "game.h"
#include "helpers.h"
#include <src/libespm.h>
#include <boost/algorithm/string.hpp>
#include <boost/filesystem.hpp>
#include <boost/log/trivial.hpp>
@@ -42,71 +41,34 @@ using boost::regex_search;
using boost::smatch;
namespace loot {
Plugin::Plugin() : PluginMetadata(), isMaster(false), crc(0), numOverrideRecords(0) {}
Plugin::Plugin() : PluginMetadata(), _isEmpty(true), isMaster(false), crc(0), numOverrideRecords(0) {}
Plugin::Plugin(const std::string& n) : PluginMetadata(n), isMaster(false), crc(0), numOverrideRecords(0) {}
Plugin::Plugin(const std::string& n) : PluginMetadata(n), _isEmpty(true), isMaster(false), crc(0), numOverrideRecords(0) {}
Plugin::Plugin(loot::Game& game, const std::string& n, const bool headerOnly)
: PluginMetadata(n), isMaster(false), crc(0), numOverrideRecords(0) {
// Get data from file contents using libespm. Assumes libespm has already been initialised.
BOOST_LOG_TRIVIAL(trace) << name << ": " << "Opening with libespm...";
boost::filesystem::path filepath = game.DataPath() / name;
//In case the plugin is ghosted.
if (!boost::filesystem::exists(filepath) && boost::filesystem::exists(filepath.string() + ".ghost"))
filepath += ".ghost";
espm::File * file = nullptr;
: PluginMetadata(n), _isEmpty(true), isMaster(false), crc(0), numOverrideRecords(0) {
try {
if (game.Id() == Game::tes4)
file = new espm::tes4::File(filepath, game.espm_settings, false, headerOnly);
else if (game.Id() == Game::tes5)
file = new espm::tes5::File(filepath, game.espm_settings, false, headerOnly);
else if (game.Id() == Game::fo3)
file = new espm::fo3::File(filepath, game.espm_settings, false, headerOnly);
else
file = new espm::fonv::File(filepath, game.espm_settings, false, headerOnly);
BOOST_LOG_TRIVIAL(trace) << name << ": " << "Checking master flag.";
isMaster = file->isMaster(game.espm_settings);
BOOST_LOG_TRIVIAL(trace) << name << ": " << "Getting masters.";
for (const auto& master : file->getMasters()) {
masters.push_back(boost::locale::conv::to_utf<char>(master, "Windows-1252", boost::locale::conv::stop));
}
BOOST_LOG_TRIVIAL(trace) << name << ": " << "Number of masters: " << masters.size();
PluginLoader loader;
loader.Load(game, name, headerOnly, false);
isMaster = loader.IsMaster();
masters = loader.Masters();
_isEmpty = loader.IsEmpty();
formIDs.insert(loader.FormIDs().begin(), loader.FormIDs().end());
if (!headerOnly) {
BOOST_LOG_TRIVIAL(trace) << name << ": " << "Getting CRC.";
crc = file->crc;
BOOST_LOG_TRIVIAL(trace) << name << ": Caching CRC value.";
crc = loader.Crc();
game.crcCache.insert(pair<string, uint32_t>(boost::locale::to_lower(name), crc));
}
BOOST_LOG_TRIVIAL(trace) << name << ": " << "Getting the FormIDs.";
vector<uint32_t> records = file->getFormIDs();
vector<string> plugins = masters;
plugins.push_back(name);
for (const auto &record : records) {
FormID fid = FormID(plugins, record);
formIDs.insert(fid);
if (!boost::iequals(fid.Plugin(), name))
BOOST_LOG_TRIVIAL(trace) << name << ": Counting override FormIDs.";
for (const auto& formID : formIDs) {
if (!boost::iequals(formID.Plugin(), name))
++numOverrideRecords;
}
BOOST_LOG_TRIVIAL(trace) << name << ": " << "Checking if plugin is empty.";
if (headerOnly) {
// Check the header records count.
_isEmpty = file->getNumRecords() == 0;
}
else {
_isEmpty = formIDs.empty();
}
//Also read Bash Tags applied and version string in description.
BOOST_LOG_TRIVIAL(trace) << name << ": " << "Reading the description.";
string text = boost::locale::conv::to_utf<char>(file->getDescription(), "Windows-1252", boost::locale::conv::stop);
string text = loader.Description();
BOOST_LOG_TRIVIAL(trace) << name << ": " << "Attempting to read the version from the description.";
for (size_t i = 0; i < version_checks.size(); ++i) {
smatch what;
@@ -139,7 +101,6 @@ namespace loot {
}
}
catch (std::exception& e) {
delete file;
BOOST_LOG_TRIVIAL(error) << "Cannot read plugin file \"" << name << "\". Details: " << e.what();
messages.push_back(loot::Message(loot::Message::error, (boost::format(boost::locale::translate("Cannot read \"%1%\". Details: %2%")) % name % e.what()).str()));
}
@@ -222,22 +183,8 @@ namespace loot {
}
try {
boost::filesystem::path filepath = game.DataPath() / name;
//In case the plugin is ghosted.
if (!boost::filesystem::exists(filepath) && boost::filesystem::exists(filepath.string() + ".ghost"))
filepath += ".ghost";
espm::File * file = nullptr;
if (game.Id() == Game::tes4)
file = new espm::tes4::File(filepath, game.espm_settings, false, true);
else if (game.Id() == Game::tes5)
file = new espm::tes5::File(filepath, game.espm_settings, false, true);
else if (game.Id() == Game::fo3)
file = new espm::fo3::File(filepath, game.espm_settings, false, true);
else
file = new espm::fonv::File(filepath, game.espm_settings, false, true);
delete file;
PluginLoader loader;
return loader.Load(game, name, true, true);
}
catch (std::exception& /*e*/) {
BOOST_LOG_TRIVIAL(warning) << "The .es(p|m) file \"" << name << "\" is not a valid plugin.";
+145
View File
@@ -0,0 +1,145 @@
/* LOOT
A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and
Fallout: New Vegas.
Copyright (C) 2012-2015 WrinklyNinja
This file is part of LOOT.
LOOT 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.
LOOT 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 LOOT. If not, see
<http://www.gnu.org/licenses/>.
*/
#include "plugin_loader.h"
#include "game.h"
#include "helpers.h"
#include <src/libespm.h>
#include <boost/algorithm/string.hpp>
#include <boost/filesystem.hpp>
#include <boost/log/trivial.hpp>
#include <boost/locale.hpp>
using namespace std;
namespace loot {
PluginLoader::PluginLoader() : _isMaster(false), _crc(0) {}
bool PluginLoader::Load(const Game& game, const std::string& name, const bool headerOnly, const bool checkValidityOnly) {
espm::File * file = nullptr;
espm::Settings espmSettings;
// Get data from file contents using libespm.
BOOST_LOG_TRIVIAL(trace) << name << ": " << "Opening with libespm...";
boost::filesystem::path filepath = game.DataPath() / name;
//In case the plugin is ghosted.
if (!boost::filesystem::exists(filepath) && boost::filesystem::exists(filepath.string() + ".ghost"))
filepath += ".ghost";
try {
if (game.Id() == Game::tes4) {
espmSettings = espm::Settings("tes4");
file = new espm::tes4::File(filepath, espmSettings, false, headerOnly);
}
else if (game.Id() == Game::tes5) {
espmSettings = espm::Settings("tes5");
file = new espm::tes5::File(filepath, espmSettings, false, headerOnly);
}
else if (game.Id() == Game::fo3) {
espmSettings = espm::Settings("fo3");
file = new espm::fo3::File(filepath, espmSettings, false, headerOnly);
}
else {
espmSettings = espm::Settings("fonv");
file = new espm::fonv::File(filepath, espmSettings, false, headerOnly);
}
// If only wanting to test for valid parsing, quit now.
if (checkValidityOnly) {
delete file;
return true;
}
BOOST_LOG_TRIVIAL(trace) << filepath.filename() << ": " << "Checking master flag.";
_isMaster = file->isMaster(espmSettings);
BOOST_LOG_TRIVIAL(trace) << filepath.filename() << ": " << "Getting masters.";
for (const auto& master : file->getMasters()) {
_masters.push_back(boost::locale::conv::to_utf<char>(master, "Windows-1252", boost::locale::conv::stop));
}
BOOST_LOG_TRIVIAL(trace) << filepath.filename() << ": " << "Number of masters: " << _masters.size();
if (!headerOnly) {
BOOST_LOG_TRIVIAL(trace) << filepath.filename() << ": " << "Getting CRC.";
_crc = file->crc;
}
BOOST_LOG_TRIVIAL(trace) << name << ": " << "Getting the FormIDs.";
vector<uint32_t> records = file->getFormIDs();
vector<string> plugins = _masters;
plugins.push_back(name);
for (const auto &record : records) {
_formIDs.insert(FormID(plugins, record));
}
BOOST_LOG_TRIVIAL(trace) << name << ": " << "Checking if plugin is empty.";
if (headerOnly) {
// Check the header records count.
_isEmpty = file->getNumRecords() == 0;
}
else {
_isEmpty = _formIDs.empty();
}
//Also read Bash Tags applied and version string in description.
BOOST_LOG_TRIVIAL(trace) << name << ": " << "Reading the description.";
_description = boost::locale::conv::to_utf<char>(file->getDescription(), "Windows-1252", boost::locale::conv::stop);
}
catch (std::exception& e) {
delete file;
throw;
}
BOOST_LOG_TRIVIAL(trace) << name << ": " << "Plugin loading complete.";
return true;
}
bool PluginLoader::IsEmpty() const {
return _isEmpty;
}
bool PluginLoader::IsMaster() const {
return _isMaster;
}
const std::set<FormID>& PluginLoader::FormIDs() const {
return _formIDs;
}
std::vector<std::string> PluginLoader::Masters() const {
return _masters;
}
std::string PluginLoader::Description() const {
return _description;
}
uint32_t PluginLoader::Crc() const {
return _crc;
}
}
+59
View File
@@ -0,0 +1,59 @@
/* LOOT
A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and
Fallout: New Vegas.
Copyright (C) 2012-2015 WrinklyNinja
This file is part of LOOT.
LOOT 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.
LOOT 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 LOOT. If not, see
<http://www.gnu.org/licenses/>.
*/
#ifndef __LOOT_PLUGIN_LOADER__
#define __LOOT_PLUGIN_LOADER__
#include "metadata/formid.h"
#include <cstdint>
#include <string>
#include <vector>
#include <set>
namespace loot {
class Game;
class PluginLoader {
public:
PluginLoader();
bool Load(const Game& game, const std::string& name, const bool headerOnly, const bool checkValidityOnly);
bool IsEmpty() const;
bool IsMaster() const; //Checks master bit flag.
const std::set<FormID>& FormIDs() const;
std::vector<std::string> Masters() const;
std::string Description() const;
uint32_t Crc() const;
private:
bool _isEmpty;
bool _isMaster;
std::set<FormID> _formIDs;
std::vector<std::string> _masters;
std::string _description;
uint32_t _crc;
};
}
#endif