mirror of
https://github.com/loot/libloot.git
synced 2026-07-27 14:16:01 -07:00
Refactored C++ classes into separate files.
The YAML converter/emitter code for the metadata and Game classes has been moved into the class files, and the set/unordered_set converter/emitter templates have been moved into yaml_set_helpers.h. For #449. Also renamed ConditionStruct to ConditionalMetadata, and condition_grammar to ConditionGrammar, the former to make its purpose clearer, and the latter to conform to the class naming style.
This commit is contained in:
+34
-7
@@ -77,28 +77,53 @@ find_package(Boost REQUIRED COMPONENTS log log_setup regex locale thread date_ti
|
||||
find_package(yaml-cpp)
|
||||
find_package(GTest)
|
||||
|
||||
set (LOOT_SRC "${CMAKE_SOURCE_DIR}/src/backend/metadata.cpp"
|
||||
set (LOOT_SRC "${CMAKE_SOURCE_DIR}/src/backend/metadata/conditional_metadata.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/backend/metadata/file.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/backend/metadata/formid.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/backend/metadata/location.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/backend/metadata/message.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/backend/metadata/message_content.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/backend/metadata/plugin_dirty_info.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/backend/metadata/tag.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/backend/game.cpp"
|
||||
"${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/helpers.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/backend/generators.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/backend/language.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/backend/version.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/backend/graph.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/backend/git.cpp"
|
||||
"${CMAKE_BINARY_DIR}/generated/globals.cpp")
|
||||
|
||||
set (LOOT_HEADERS "${CMAKE_SOURCE_DIR}/src/backend/metadata.h"
|
||||
set (LOOT_HEADERS "${CMAKE_SOURCE_DIR}/src/backend/metadata/condition_grammar.h"
|
||||
"${CMAKE_SOURCE_DIR}/src/backend/metadata/conditional_metadata.h"
|
||||
"${CMAKE_SOURCE_DIR}/src/backend/metadata/file.h"
|
||||
"${CMAKE_SOURCE_DIR}/src/backend/metadata/formid.h"
|
||||
"${CMAKE_SOURCE_DIR}/src/backend/metadata/location.h"
|
||||
"${CMAKE_SOURCE_DIR}/src/backend/metadata/message.h"
|
||||
"${CMAKE_SOURCE_DIR}/src/backend/metadata/message_content.h"
|
||||
"${CMAKE_SOURCE_DIR}/src/backend/metadata/plugin_dirty_info.h"
|
||||
"${CMAKE_SOURCE_DIR}/src/backend/metadata/tag.h"
|
||||
"${CMAKE_SOURCE_DIR}/src/backend/game.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/helpers.h"
|
||||
"${CMAKE_SOURCE_DIR}/src/backend/language.h"
|
||||
"${CMAKE_SOURCE_DIR}/src/backend/version.h"
|
||||
"${CMAKE_SOURCE_DIR}/src/backend/globals.h"
|
||||
"${CMAKE_SOURCE_DIR}/src/backend/generators.h"
|
||||
"${CMAKE_SOURCE_DIR}/src/backend/yaml_set_helpers.h"
|
||||
"${CMAKE_SOURCE_DIR}/src/backend/graph.h"
|
||||
"${CMAKE_SOURCE_DIR}/src/backend/error.h"
|
||||
"${CMAKE_SOURCE_DIR}/src/backend/parsers.h"
|
||||
"${CMAKE_SOURCE_DIR}/src/backend/streams.h")
|
||||
|
||||
set (LOOT_GUI_SRC ${LOOT_SRC}
|
||||
"${CMAKE_SOURCE_DIR}/src/gui/main_win.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/gui/handler.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/gui/app.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/gui/loot_handler.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/gui/loot_app.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/gui/loot_state.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/gui/scheme.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/resource.rc")
|
||||
|
||||
@@ -107,7 +132,9 @@ set (LOOT_GUI_HEADERS ${LOOT_HEADERS}
|
||||
"${CMAKE_SOURCE_DIR}/src/backend/json.h"
|
||||
# Actual GUI code.
|
||||
"${CMAKE_SOURCE_DIR}/src/gui/handler.h"
|
||||
"${CMAKE_SOURCE_DIR}/src/gui/app.h"
|
||||
"${CMAKE_SOURCE_DIR}/src/gui/loot_handler.h"
|
||||
"${CMAKE_SOURCE_DIR}/src/gui/loot_app.h"
|
||||
"${CMAKE_SOURCE_DIR}/src/gui/loot_state.h"
|
||||
"${CMAKE_SOURCE_DIR}/src/gui/scheme.h"
|
||||
"${CMAKE_SOURCE_DIR}/src/gui/resource.h")
|
||||
|
||||
|
||||
+3
-3
@@ -24,9 +24,8 @@
|
||||
|
||||
#include "api.h"
|
||||
#include "../backend/game.h"
|
||||
#include "../backend/metadata.h"
|
||||
#include "../backend/parsers.h"
|
||||
#include "../backend/generators.h"
|
||||
#include "../backend/globals.h"
|
||||
#include "../backend/plugin.h"
|
||||
#include "../backend/error.h"
|
||||
#include "../backend/streams.h"
|
||||
|
||||
@@ -41,6 +40,7 @@
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/log/core.hpp>
|
||||
|
||||
const unsigned int loot_ok = loot::error::ok;
|
||||
const unsigned int loot_error_liblo_error = loot::error::liblo_error;
|
||||
|
||||
+18
-190
@@ -26,10 +26,8 @@
|
||||
#include "globals.h"
|
||||
#include "helpers.h"
|
||||
#include "error.h"
|
||||
#include "metadata.h"
|
||||
#include "parsers.h"
|
||||
#include "plugin.h"
|
||||
#include "streams.h"
|
||||
#include "generators.h"
|
||||
#include "graph.h"
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
@@ -67,193 +65,6 @@ namespace loot {
|
||||
return games;
|
||||
}
|
||||
|
||||
// MetadataList member functions
|
||||
//------------------------------
|
||||
|
||||
void MetadataList::Load(const boost::filesystem::path& filepath) {
|
||||
plugins.clear();
|
||||
messages.clear();
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "Loading file: " << filepath;
|
||||
|
||||
loot::ifstream in(filepath);
|
||||
YAML::Node metadataList = YAML::Load(in);
|
||||
in.close();
|
||||
|
||||
if (metadataList["plugins"]) {
|
||||
for (const auto& node : metadataList["plugins"]) {
|
||||
Plugin plugin(node.as<Plugin>());
|
||||
if (plugin.IsRegexPlugin())
|
||||
regexPlugins.push_back(plugin);
|
||||
else
|
||||
plugins.insert(plugin);
|
||||
}
|
||||
}
|
||||
if (metadataList["globals"])
|
||||
messages = metadataList["globals"].as< list<Message> >();
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "File loaded successfully.";
|
||||
}
|
||||
|
||||
void MetadataList::Save(const boost::filesystem::path& filepath) {
|
||||
BOOST_LOG_TRIVIAL(trace) << "Saving metadata list to: " << filepath;
|
||||
YAML::Emitter yout;
|
||||
yout.SetIndent(2);
|
||||
yout << YAML::BeginMap
|
||||
<< YAML::Key << "plugins" << YAML::Value << Plugins()
|
||||
<< YAML::Key << "globals" << YAML::Value << messages
|
||||
<< YAML::EndMap;
|
||||
|
||||
loot::ofstream uout(filepath);
|
||||
uout << yout.c_str();
|
||||
uout.close();
|
||||
}
|
||||
|
||||
void MetadataList::clear() {
|
||||
plugins.clear();
|
||||
messages.clear();
|
||||
}
|
||||
|
||||
bool MetadataList::operator == (const MetadataList& rhs) const {
|
||||
if (this->plugins.size() != rhs.plugins.size() || this->messages.size() != rhs.messages.size() || this->regexPlugins.size() != rhs.regexPlugins.size()) {
|
||||
BOOST_LOG_TRIVIAL(info) << "Metadata edited for some plugin, new and old userlists differ in size.";
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
for (const auto& rhsPlugin : rhs.plugins) {
|
||||
const auto it = this->plugins.find(rhsPlugin);
|
||||
|
||||
if (it == this->plugins.end()) {
|
||||
BOOST_LOG_TRIVIAL(info) << "Metadata added for plugin: " << it->Name();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!it->DiffMetadata(rhsPlugin).HasNameOnly()) {
|
||||
BOOST_LOG_TRIVIAL(info) << "Metadata edited for plugin: " << it->Name();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (const auto& rhsPlugin : rhs.regexPlugins) {
|
||||
const auto it = find(regexPlugins.begin(), regexPlugins.end(), rhsPlugin);
|
||||
|
||||
if (it == this->regexPlugins.end()) {
|
||||
BOOST_LOG_TRIVIAL(info) << "Metadata added for plugin: " << it->Name();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!it->DiffMetadata(rhsPlugin).HasNameOnly()) {
|
||||
BOOST_LOG_TRIVIAL(info) << "Metadata edited for plugin: " << it->Name();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Messages are compared exactly by the '==' operator, so there's no need to do a more
|
||||
// fine-grained check.
|
||||
for (const auto& rhsMessage : rhs.messages) {
|
||||
const auto it = std::find(this->messages.begin(), this->messages.end(), rhsMessage);
|
||||
|
||||
if (it == this->messages.end()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::list<Plugin> MetadataList::Plugins() const {
|
||||
list<Plugin> pluginList(plugins.begin(), plugins.end());
|
||||
|
||||
pluginList.insert(pluginList.end(), regexPlugins.begin(), regexPlugins.end());
|
||||
|
||||
return pluginList;
|
||||
}
|
||||
|
||||
// Merges multiple matching regex entries if any are found.
|
||||
Plugin MetadataList::FindPlugin(const Plugin& plugin) const {
|
||||
Plugin match(plugin.Name());
|
||||
|
||||
auto it = plugins.find(plugin);
|
||||
|
||||
if (it != plugins.end())
|
||||
match = *it;
|
||||
|
||||
// Now we want to also match possibly multiple regex entries.
|
||||
auto regIt = find(regexPlugins.begin(), regexPlugins.end(), plugin);
|
||||
while (regIt != regexPlugins.end()) {
|
||||
match.MergeMetadata(*regIt);
|
||||
|
||||
regIt = find(++regIt, regexPlugins.end(), plugin);
|
||||
}
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
void MetadataList::AddPlugin(const Plugin& plugin) {
|
||||
if (plugin.IsRegexPlugin())
|
||||
regexPlugins.push_back(plugin);
|
||||
else
|
||||
plugins.insert(plugin);
|
||||
}
|
||||
|
||||
// Doesn't erase matching regex entries, because they might also
|
||||
// be required for other plugins.
|
||||
void MetadataList::ErasePlugin(const Plugin& plugin) {
|
||||
auto it = plugins.find(plugin);
|
||||
|
||||
if (it != plugins.end()) {
|
||||
plugins.erase(it);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void MetadataList::EvalAllConditions(Game& game, const unsigned int language) {
|
||||
unordered_set<Plugin> replacementSet;
|
||||
for (auto &plugin : plugins) {
|
||||
Plugin p(plugin);
|
||||
p.EvalAllConditions(game, language);
|
||||
replacementSet.insert(p);
|
||||
}
|
||||
plugins = replacementSet;
|
||||
for (auto &plugin : regexPlugins) {
|
||||
plugin.EvalAllConditions(game, language);
|
||||
}
|
||||
for (auto &message : messages) {
|
||||
message.EvalCondition(game, language);
|
||||
}
|
||||
}
|
||||
|
||||
// Masterlist member functions
|
||||
//----------------------------
|
||||
|
||||
bool Masterlist::Load(Game& game, const unsigned int language) {
|
||||
try {
|
||||
return Update(game);
|
||||
}
|
||||
catch (error& e) {
|
||||
if (e.code() != error::ok) {
|
||||
// Error wasn't a parsing error. Need to try parsing masterlist if it exists.
|
||||
try {
|
||||
MetadataList::Load(game.MasterlistPath());
|
||||
}
|
||||
catch (...) {}
|
||||
}
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
std::string Masterlist::GetRevision(const boost::filesystem::path& path, bool shortID) {
|
||||
if (revision.empty() || (shortID && revision.length() == 40) || (!shortID && revision.length() < 40))
|
||||
GetGitInfo(path, shortID);
|
||||
|
||||
return revision;
|
||||
}
|
||||
|
||||
std::string Masterlist::GetDate(const boost::filesystem::path& path) {
|
||||
if (date.empty())
|
||||
GetGitInfo(path, true);
|
||||
|
||||
return date;
|
||||
}
|
||||
|
||||
// Game member functions
|
||||
//----------------------
|
||||
|
||||
@@ -834,3 +645,20 @@ namespace loot {
|
||||
return loot::Sort(graph, loadorder);
|
||||
}
|
||||
}
|
||||
|
||||
namespace YAML {
|
||||
Emitter& operator << (Emitter& out, const loot::Game& rhs) {
|
||||
out << BeginMap
|
||||
<< Key << "type" << Value << YAML::SingleQuoted << loot::Game(rhs.Id()).FolderName()
|
||||
<< Key << "folder" << Value << YAML::SingleQuoted << rhs.FolderName()
|
||||
<< Key << "name" << Value << YAML::SingleQuoted << rhs.Name()
|
||||
<< Key << "master" << Value << YAML::SingleQuoted << rhs.Master()
|
||||
<< Key << "repo" << Value << YAML::SingleQuoted << rhs.RepoURL()
|
||||
<< Key << "branch" << Value << YAML::SingleQuoted << rhs.RepoBranch()
|
||||
<< Key << "path" << Value << YAML::SingleQuoted << rhs.GamePath().string()
|
||||
<< Key << "registry" << Value << YAML::SingleQuoted << rhs.RegistryKey()
|
||||
<< EndMap;
|
||||
|
||||
return out;
|
||||
}
|
||||
}
|
||||
+59
-59
@@ -25,7 +25,9 @@
|
||||
#ifndef __LOOT_GAME__
|
||||
#define __LOOT_GAME__
|
||||
|
||||
#include "metadata.h"
|
||||
#include "plugin.h"
|
||||
#include "metadata_list.h"
|
||||
#include "masterlist.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -41,64 +43,6 @@
|
||||
#include <yaml-cpp/yaml.h>
|
||||
|
||||
namespace loot {
|
||||
class Game;
|
||||
|
||||
/* Each Game object should store the config details specific to that game.
|
||||
It should also store the plugin and masterlist data for that game.
|
||||
Plugin data should be stored as an unordered hashset, the elements of which are
|
||||
referenced by ordered lists and other structures.
|
||||
Masterlist / userlist data should be stored as structures which hold plugin and
|
||||
global message lists.
|
||||
Each game should have functions to load this plugin and masterlist / userlist
|
||||
data. Plugin data should be loaded as header-only and as full data.
|
||||
*/
|
||||
|
||||
class MetadataList {
|
||||
public:
|
||||
void Load(const boost::filesystem::path& filepath);
|
||||
void Save(const boost::filesystem::path& filepath);
|
||||
void clear();
|
||||
|
||||
bool operator == (const MetadataList& rhs) const; //Compares content.
|
||||
|
||||
std::list<Plugin> Plugins() const;
|
||||
|
||||
// Merges multiple matching regex entries if any are found.
|
||||
Plugin FindPlugin(const Plugin& plugin) const;
|
||||
void AddPlugin(const Plugin& plugin);
|
||||
|
||||
// Doesn't erase matching regex entries, because they might also
|
||||
// be required for other plugins.
|
||||
void ErasePlugin(const Plugin& plugin);
|
||||
|
||||
// Eval plugin conditions.
|
||||
void EvalAllConditions(Game& game, const unsigned int language);
|
||||
|
||||
std::list<Message> messages;
|
||||
protected:
|
||||
std::unordered_set<Plugin> plugins;
|
||||
std::list<Plugin> regexPlugins;
|
||||
};
|
||||
|
||||
class Masterlist : public MetadataList {
|
||||
public:
|
||||
|
||||
bool Load(Game& game, const unsigned int language); //Handles update with load fallback.
|
||||
bool Update(const Game& game);
|
||||
bool Update(const boost::filesystem::path& path,
|
||||
const std::string& repoURL,
|
||||
const std::string& repoBranch);
|
||||
|
||||
std::string GetRevision(const boost::filesystem::path& path, bool shortID);
|
||||
std::string GetDate(const boost::filesystem::path& path);
|
||||
|
||||
private:
|
||||
void GetGitInfo(const boost::filesystem::path& path, bool shortID);
|
||||
|
||||
std::string revision;
|
||||
std::string date;
|
||||
};
|
||||
|
||||
class Game {
|
||||
public:
|
||||
//Game functions.
|
||||
@@ -188,4 +132,60 @@ namespace loot {
|
||||
std::list<Game> GetGames(YAML::Node& settings);
|
||||
}
|
||||
|
||||
namespace YAML {
|
||||
template<>
|
||||
struct convert < loot::Game > {
|
||||
static Node encode(const loot::Game& rhs) {
|
||||
Node node;
|
||||
|
||||
node["type"] = loot::Game(rhs.Id()).FolderName();
|
||||
node["name"] = rhs.Name();
|
||||
node["folder"] = rhs.FolderName();
|
||||
node["master"] = rhs.Master();
|
||||
node["repo"] = rhs.RepoURL();
|
||||
node["branch"] = rhs.RepoBranch();
|
||||
node["path"] = rhs.GamePath().string();
|
||||
node["registry"] = rhs.RegistryKey();
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static bool decode(const Node& node, loot::Game& rhs) {
|
||||
if (!node.IsMap() || !node["folder"] || !node["type"])
|
||||
return false;
|
||||
|
||||
if (node["type"].as<std::string>() == loot::Game(loot::Game::tes4).FolderName())
|
||||
rhs = loot::Game(loot::Game::tes4, node["folder"].as<std::string>());
|
||||
else if (node["type"].as<std::string>() == loot::Game(loot::Game::tes5).FolderName())
|
||||
rhs = loot::Game(loot::Game::tes5, node["folder"].as<std::string>());
|
||||
else if (node["type"].as<std::string>() == loot::Game(loot::Game::fo3).FolderName())
|
||||
rhs = loot::Game(loot::Game::fo3, node["folder"].as<std::string>());
|
||||
else if (node["type"].as<std::string>() == loot::Game(loot::Game::fonv).FolderName())
|
||||
rhs = loot::Game(loot::Game::fonv, node["folder"].as<std::string>());
|
||||
else
|
||||
return false;
|
||||
|
||||
std::string name, master, repo, branch, path, registry;
|
||||
if (node["name"])
|
||||
name = node["name"].as<std::string>();
|
||||
if (node["master"])
|
||||
master = node["master"].as<std::string>();
|
||||
if (node["repo"])
|
||||
repo = node["repo"].as<std::string>();
|
||||
if (node["branch"])
|
||||
branch = node["branch"].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, repo, branch, path, registry);
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
Emitter& operator << (Emitter& out, const loot::Game& rhs);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,193 +0,0 @@
|
||||
/* LOOT
|
||||
|
||||
A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and
|
||||
Fallout: New Vegas.
|
||||
|
||||
Copyright (C) 2013-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 "generators.h"
|
||||
#include "helpers.h"
|
||||
#include "globals.h"
|
||||
#include "parsers.h"
|
||||
#include "streams.h"
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/locale.hpp>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace YAML {
|
||||
Emitter& operator << (Emitter& out, const loot::PluginDirtyInfo& rhs) {
|
||||
out << BeginMap
|
||||
<< Key << "crc" << Value << Hex << rhs.CRC() << Dec
|
||||
<< Key << "util" << Value << YAML::SingleQuoted << rhs.CleaningUtility();
|
||||
|
||||
if (rhs.ITMs() > 0)
|
||||
out << Key << "itm" << Value << rhs.ITMs();
|
||||
if (rhs.DeletedRefs() > 0)
|
||||
out << Key << "udr" << Value << rhs.DeletedRefs();
|
||||
if (rhs.DeletedNavmeshes() > 0)
|
||||
out << Key << "nav" << Value << rhs.DeletedNavmeshes();
|
||||
|
||||
out << EndMap;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
Emitter& operator << (Emitter& out, const loot::Game& rhs) {
|
||||
out << BeginMap
|
||||
<< Key << "type" << Value << YAML::SingleQuoted << loot::Game(rhs.Id()).FolderName()
|
||||
<< Key << "folder" << Value << YAML::SingleQuoted << rhs.FolderName()
|
||||
<< Key << "name" << Value << YAML::SingleQuoted << rhs.Name()
|
||||
<< Key << "master" << Value << YAML::SingleQuoted << rhs.Master()
|
||||
<< Key << "repo" << Value << YAML::SingleQuoted << rhs.RepoURL()
|
||||
<< Key << "branch" << Value << YAML::SingleQuoted << rhs.RepoBranch()
|
||||
<< Key << "path" << Value << YAML::SingleQuoted << rhs.GamePath().string()
|
||||
<< Key << "registry" << Value << YAML::SingleQuoted << rhs.RegistryKey()
|
||||
<< EndMap;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
Emitter& operator << (Emitter& out, const loot::MessageContent& rhs) {
|
||||
out << BeginMap;
|
||||
|
||||
out << Key << "lang" << Value << loot::Language(rhs.Language()).Locale();
|
||||
|
||||
out << Key << "str" << Value << YAML::SingleQuoted << rhs.Str();
|
||||
|
||||
out << EndMap;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
Emitter& operator << (Emitter& out, const loot::Message& rhs) {
|
||||
out << BeginMap;
|
||||
|
||||
if (rhs.Type() == loot::Message::say)
|
||||
out << Key << "type" << Value << "say";
|
||||
else if (rhs.Type() == loot::Message::warn)
|
||||
out << Key << "type" << Value << "warn";
|
||||
else
|
||||
out << Key << "type" << Value << "error";
|
||||
|
||||
if (rhs.Content().size() == 1)
|
||||
out << Key << "content" << Value << YAML::SingleQuoted << rhs.Content().front().Str();
|
||||
else
|
||||
out << Key << "content" << Value << rhs.Content();
|
||||
|
||||
if (!rhs.Condition().empty())
|
||||
out << Key << "condition" << Value << YAML::SingleQuoted << rhs.Condition();
|
||||
|
||||
out << EndMap;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
Emitter& operator << (Emitter& out, const loot::File& rhs) {
|
||||
if (!rhs.IsConditional() && rhs.DisplayName().empty())
|
||||
out << rhs.Name();
|
||||
else {
|
||||
out << BeginMap
|
||||
<< Key << "name" << Value << YAML::SingleQuoted << rhs.Name();
|
||||
|
||||
if (rhs.IsConditional())
|
||||
out << Key << "condition" << Value << YAML::SingleQuoted << rhs.Condition();
|
||||
|
||||
if (rhs.DisplayName() != rhs.Name())
|
||||
out << Key << "display" << Value << YAML::SingleQuoted << rhs.DisplayName();
|
||||
|
||||
out << EndMap;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
Emitter& operator << (Emitter& out, const loot::Tag& rhs) {
|
||||
if (!rhs.IsConditional()) {
|
||||
if (rhs.IsAddition())
|
||||
out << rhs.Name();
|
||||
else
|
||||
out << ('-' + rhs.Name());
|
||||
}
|
||||
else {
|
||||
out << BeginMap;
|
||||
if (rhs.IsAddition())
|
||||
out << Key << "name" << Value << rhs.Name();
|
||||
else
|
||||
out << Key << "name" << Value << ('-' + rhs.Name());
|
||||
|
||||
out << Key << "condition" << Value << YAML::SingleQuoted << rhs.Condition()
|
||||
<< EndMap;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
Emitter& operator << (Emitter& out, const loot::Location& rhs) {
|
||||
if (rhs.Versions().empty())
|
||||
out << rhs.URL();
|
||||
else {
|
||||
out << BeginMap
|
||||
<< Key << "link" << Value << YAML::SingleQuoted << rhs.URL()
|
||||
<< Key << "ver" << Value << YAML::SingleQuoted << rhs.Versions()
|
||||
<< EndMap;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
Emitter& operator << (Emitter& out, const loot::Plugin& rhs) {
|
||||
if (!rhs.HasNameOnly()) {
|
||||
out << BeginMap
|
||||
<< Key << "name" << Value << YAML::SingleQuoted << rhs.Name();
|
||||
|
||||
if (rhs.IsPriorityExplicit())
|
||||
out << Key << "priority" << Value << rhs.Priority();
|
||||
|
||||
if (!rhs.Enabled())
|
||||
out << Key << "enabled" << Value << rhs.Enabled();
|
||||
|
||||
if (!rhs.LoadAfter().empty())
|
||||
out << Key << "after" << Value << rhs.LoadAfter();
|
||||
|
||||
if (!rhs.Reqs().empty())
|
||||
out << Key << "req" << Value << rhs.Reqs();
|
||||
|
||||
if (!rhs.Incs().empty())
|
||||
out << Key << "inc" << Value << rhs.Incs();
|
||||
|
||||
if (!rhs.Messages().empty())
|
||||
out << Key << "msg" << Value << rhs.Messages();
|
||||
|
||||
if (!rhs.Tags().empty())
|
||||
out << Key << "tag" << Value << rhs.Tags();
|
||||
|
||||
if (!rhs.DirtyInfo().empty())
|
||||
out << Key << "dirty" << Value << rhs.DirtyInfo();
|
||||
|
||||
if (!rhs.Locations().empty())
|
||||
out << Key << "url" << Value << rhs.Locations();
|
||||
|
||||
out << EndMap;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,6 @@
|
||||
*/
|
||||
|
||||
#include "error.h"
|
||||
#include "parsers.h"
|
||||
#include "streams.h"
|
||||
#include "helpers.h"
|
||||
#include "game.h"
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@
|
||||
#ifndef __LOOT_GRAPH__
|
||||
#define __LOOT_GRAPH__
|
||||
|
||||
#include "metadata.h"
|
||||
#include "plugin.h"
|
||||
|
||||
#include <boost/graph/graph_traits.hpp>
|
||||
#include <boost/graph/adjacency_list.hpp>
|
||||
|
||||
@@ -34,8 +34,6 @@
|
||||
#include <boost/locale.hpp>
|
||||
#include <boost/regex.hpp>
|
||||
|
||||
#include <alphanum.hpp>
|
||||
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <cctype>
|
||||
@@ -243,227 +241,4 @@ namespace loot {
|
||||
return str;
|
||||
}
|
||||
#endif
|
||||
|
||||
Language::Language(const unsigned int code) {
|
||||
Construct(code);
|
||||
}
|
||||
|
||||
Language::Language(const std::string& nameOrCode) {
|
||||
if (nameOrCode == Language(Language::english).Name() || nameOrCode == Language(Language::english).Locale())
|
||||
Construct(Language::english);
|
||||
else if (nameOrCode == Language(Language::spanish).Name() || nameOrCode == Language(Language::spanish).Locale())
|
||||
Construct(Language::spanish);
|
||||
else if (nameOrCode == Language(Language::russian).Name() || nameOrCode == Language(Language::russian).Locale())
|
||||
Construct(Language::russian);
|
||||
else if (nameOrCode == Language(Language::french).Name() || nameOrCode == Language(Language::french).Locale())
|
||||
Construct(Language::french);
|
||||
else if (nameOrCode == Language(Language::chinese).Name() || nameOrCode == Language(Language::chinese).Locale())
|
||||
Construct(Language::chinese);
|
||||
else if (nameOrCode == Language(Language::polish).Name() || nameOrCode == Language(Language::polish).Locale())
|
||||
Construct(Language::polish);
|
||||
else if (nameOrCode == Language(Language::brazilian_portuguese).Name() || nameOrCode == Language(Language::brazilian_portuguese).Locale())
|
||||
Construct(Language::brazilian_portuguese);
|
||||
else if (nameOrCode == Language(Language::finnish).Name() || nameOrCode == Language(Language::finnish).Locale())
|
||||
Construct(Language::finnish);
|
||||
else if (nameOrCode == Language(Language::german).Name() || nameOrCode == Language(Language::german).Locale())
|
||||
Construct(Language::german);
|
||||
else if (nameOrCode == Language(Language::danish).Name() || nameOrCode == Language(Language::danish).Locale())
|
||||
Construct(Language::danish);
|
||||
else if (nameOrCode == Language(Language::korean).Name() || nameOrCode == Language(Language::korean).Locale())
|
||||
Construct(Language::korean);
|
||||
else
|
||||
Construct(Language::english);
|
||||
}
|
||||
|
||||
void Language::Construct(const unsigned int code) {
|
||||
_code = code;
|
||||
if (_code == Language::spanish) {
|
||||
_name = "Español";
|
||||
_locale = "es";
|
||||
}
|
||||
else if (_code == Language::russian) {
|
||||
_name = "Русский";
|
||||
_locale = "ru";
|
||||
}
|
||||
else if (_code == Language::french) {
|
||||
_name = "Français";
|
||||
_locale = "fr";
|
||||
}
|
||||
else if (_code == Language::chinese) {
|
||||
_name = "简体中文";
|
||||
_locale = "zh_CN";
|
||||
}
|
||||
else if (_code == Language::polish) {
|
||||
_name = "Polski";
|
||||
_locale = "pl";
|
||||
}
|
||||
else if (_code == Language::brazilian_portuguese) {
|
||||
_name = "Português do Brasil";
|
||||
_locale = "pt_BR";
|
||||
}
|
||||
else if (_code == Language::finnish) {
|
||||
_name = "suomi";
|
||||
_locale = "fi";
|
||||
}
|
||||
else if (_code == Language::german) {
|
||||
_name = "Deutsch";
|
||||
_locale = "de";
|
||||
}
|
||||
else if (_code == Language::danish) {
|
||||
_name = "Dansk";
|
||||
_locale = "da";
|
||||
}
|
||||
else if (_code == Language::korean) {
|
||||
_name = "한국어";
|
||||
_locale = "ko";
|
||||
}
|
||||
else {
|
||||
_name = "English";
|
||||
_locale = "en";
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int Language::Code() const {
|
||||
return _code;
|
||||
}
|
||||
|
||||
std::string Language::Name() const {
|
||||
return _name;
|
||||
}
|
||||
|
||||
std::string Language::Locale() const {
|
||||
return _locale;
|
||||
}
|
||||
|
||||
const std::vector<std::string> Language::Names({
|
||||
Language(Language::english).Name(),
|
||||
Language(Language::spanish).Name(),
|
||||
Language(Language::russian).Name(),
|
||||
Language(Language::french).Name(),
|
||||
Language(Language::chinese).Name(),
|
||||
Language(Language::polish).Name(),
|
||||
Language(Language::brazilian_portuguese).Name(),
|
||||
Language(Language::finnish).Name(),
|
||||
Language(Language::german).Name(),
|
||||
Language(Language::danish).Name(),
|
||||
Language(Language::korean).Name()
|
||||
});
|
||||
|
||||
//////////////////////////////
|
||||
// Version Class Functions
|
||||
//////////////////////////////
|
||||
|
||||
Version::Version() {}
|
||||
|
||||
Version::Version(const std::string& ver)
|
||||
: verString(ver) {}
|
||||
|
||||
Version::Version(const fs::path& file) {
|
||||
#ifdef _WIN32
|
||||
DWORD dummy = 0;
|
||||
DWORD size = GetFileVersionInfoSize(ToWinWide(file.string()).c_str(), &dummy);
|
||||
|
||||
if (size > 0) {
|
||||
LPBYTE point = new BYTE[size];
|
||||
UINT uLen;
|
||||
VS_FIXEDFILEINFO *info;
|
||||
|
||||
GetFileVersionInfo(ToWinWide(file.string()).c_str(), 0, size, point);
|
||||
|
||||
VerQueryValue(point, L"\\", (LPVOID *)&info, &uLen);
|
||||
|
||||
DWORD dwLeftMost = HIWORD(info->dwFileVersionMS);
|
||||
DWORD dwSecondLeft = LOWORD(info->dwFileVersionMS);
|
||||
DWORD dwSecondRight = HIWORD(info->dwFileVersionLS);
|
||||
DWORD dwRightMost = LOWORD(info->dwFileVersionLS);
|
||||
|
||||
delete[] point;
|
||||
|
||||
verString = to_string(dwLeftMost) + '.' + to_string(dwSecondLeft) + '.' + to_string(dwSecondRight) + '.' + to_string(dwRightMost);
|
||||
}
|
||||
#else
|
||||
// ensure filename has no quote characters in it to avoid command injection attacks
|
||||
if (string::npos != file.string().find('"')) {
|
||||
// command mostly borrowed from the gnome-exe-thumbnailer.sh script
|
||||
// wrestool is part of the icoutils package
|
||||
string cmd = "wrestool --extract --raw --type=version \"" + file.string() + "\" | tr '\\0, ' '\\t.\\0' | sed 's/\\t\\t/_/g' | tr -c -d '[:print:]' | sed -r 's/.*Version[^0-9]*([0-9]+(\\.[0-9]+)+).*/\\1/'";
|
||||
|
||||
FILE *fp = popen(cmd.c_str(), "r");
|
||||
|
||||
// read out the version string
|
||||
static const uint32_t BUFSIZE = 32;
|
||||
char buf[BUFSIZE];
|
||||
if (nullptr != fgets(buf, BUFSIZE, fp)) {
|
||||
verString = string(buf);
|
||||
}
|
||||
pclose(fp);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Version::Version(const Plugin& plugin) : verString(plugin.Version()) {}
|
||||
|
||||
string Version::AsString() const {
|
||||
return verString;
|
||||
}
|
||||
|
||||
bool Version::operator < (const Version& ver) const {
|
||||
//Version string could have a wide variety of formats. Use regex to choose specific comparison types.
|
||||
|
||||
regex reg1("(\\d+\\.?)+"); //a.b.c.d.e.f.... where the letters are all integers, and 'a' is the shortest possible match.
|
||||
|
||||
//regex reg2("(\\d+\\.?)+([a-zA-Z\\-]+(\\d+\\.?)*)+"); //Matches a mix of letters and numbers - from "0.99.xx", "1.35Alpha2", "0.9.9MB8b1", "10.52EV-D", "1.62EV" to "10.0EV-D1.62EV".
|
||||
|
||||
if (regex_match(verString, reg1) && regex_match(ver.AsString(), reg1)) {
|
||||
//First type: numbers separated by periods. If two versions have a different number of numbers, then the shorter should be padded
|
||||
//with zeros. An arbitrary number of numbers should be supported.
|
||||
istringstream parser1(verString);
|
||||
istringstream parser2(ver.AsString());
|
||||
while (parser1.good() || parser2.good()) {
|
||||
//Check if each stringstream is OK for i/o before doing anything with it. If not, replace its extracted value with a 0.
|
||||
uint32_t n1, n2;
|
||||
if (parser1.good()) {
|
||||
parser1 >> n1;
|
||||
parser1.get();
|
||||
}
|
||||
else
|
||||
n1 = 0;
|
||||
if (parser2.good()) {
|
||||
parser2 >> n2;
|
||||
parser2.get();
|
||||
}
|
||||
else
|
||||
n2 = 0;
|
||||
if (n1 < n2)
|
||||
return true;
|
||||
else if (n1 > n2)
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
//Wacky format. Use the Alphanum Algorithm. (what a name!)
|
||||
return (doj::alphanum_comp(verString, ver.AsString()) < 0);
|
||||
}
|
||||
}
|
||||
|
||||
bool Version::operator > (const Version& ver) const {
|
||||
return (*this != ver && !(*this < ver));
|
||||
}
|
||||
|
||||
bool Version::operator >= (const Version& ver) const {
|
||||
return (*this == ver || *this > ver);
|
||||
}
|
||||
|
||||
bool Version::operator <= (const Version& ver) const {
|
||||
return (*this == ver || *this < ver);
|
||||
}
|
||||
|
||||
bool Version::operator == (const Version& ver) const {
|
||||
return (verString == ver.AsString());
|
||||
}
|
||||
|
||||
bool Version::operator != (const Version& ver) const {
|
||||
return !(*this == ver);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-53
@@ -25,7 +25,7 @@
|
||||
#ifndef __LOOT_HELPERS__
|
||||
#define __LOOT_HELPERS__
|
||||
|
||||
#include "metadata.h"
|
||||
#include "plugin.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
@@ -65,58 +65,6 @@ namespace loot {
|
||||
|
||||
std::string FromWinWide(const std::wstring& wstr);
|
||||
#endif
|
||||
|
||||
//Language class for simpler language support.
|
||||
class Language {
|
||||
public:
|
||||
Language(const unsigned int code);
|
||||
Language(const std::string& nameOrCode);
|
||||
|
||||
unsigned int Code() const;
|
||||
std::string Name() const;
|
||||
std::string Locale() const;
|
||||
|
||||
static const unsigned int any = 0; // This shouldn't be used as a selectable language, just for when picking any string in a message.
|
||||
static const unsigned int english = 1;
|
||||
static const unsigned int spanish = 2;
|
||||
static const unsigned int russian = 3;
|
||||
static const unsigned int french = 4;
|
||||
static const unsigned int chinese = 5;
|
||||
static const unsigned int polish = 6;
|
||||
static const unsigned int brazilian_portuguese = 7;
|
||||
static const unsigned int finnish = 8;
|
||||
static const unsigned int german = 9;
|
||||
static const unsigned int danish = 10;
|
||||
static const unsigned int korean = 11;
|
||||
|
||||
static const std::vector<std::string> Names;
|
||||
private:
|
||||
unsigned int _code;
|
||||
std::string _name;
|
||||
std::string _locale;
|
||||
|
||||
void Construct(const unsigned int code);
|
||||
};
|
||||
|
||||
//Version class for more robust version comparisons.
|
||||
class Version {
|
||||
private:
|
||||
std::string verString;
|
||||
public:
|
||||
Version();
|
||||
Version(const std::string& ver);
|
||||
Version(const boost::filesystem::path& file);
|
||||
Version(const Plugin& plugin);
|
||||
|
||||
std::string AsString() const;
|
||||
|
||||
bool operator > (const Version&) const;
|
||||
bool operator < (const Version&) const;
|
||||
bool operator >= (const Version&) const;
|
||||
bool operator <= (const Version&) const;
|
||||
bool operator == (const Version&) const;
|
||||
bool operator != (const Version&) const;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -28,6 +28,7 @@ along with LOOT. If not, see
|
||||
#include <yaml-cpp/yaml.h>
|
||||
|
||||
#include <boost/regex.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
namespace loot {
|
||||
// Handy class for turning YAML objects into JSON and vice-versa.
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
/* 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 "language.h"
|
||||
|
||||
namespace loot {
|
||||
Language::Language(const unsigned int code) {
|
||||
Construct(code);
|
||||
}
|
||||
|
||||
Language::Language(const std::string& nameOrCode) {
|
||||
if (nameOrCode == Language(Language::english).Name() || nameOrCode == Language(Language::english).Locale())
|
||||
Construct(Language::english);
|
||||
else if (nameOrCode == Language(Language::spanish).Name() || nameOrCode == Language(Language::spanish).Locale())
|
||||
Construct(Language::spanish);
|
||||
else if (nameOrCode == Language(Language::russian).Name() || nameOrCode == Language(Language::russian).Locale())
|
||||
Construct(Language::russian);
|
||||
else if (nameOrCode == Language(Language::french).Name() || nameOrCode == Language(Language::french).Locale())
|
||||
Construct(Language::french);
|
||||
else if (nameOrCode == Language(Language::chinese).Name() || nameOrCode == Language(Language::chinese).Locale())
|
||||
Construct(Language::chinese);
|
||||
else if (nameOrCode == Language(Language::polish).Name() || nameOrCode == Language(Language::polish).Locale())
|
||||
Construct(Language::polish);
|
||||
else if (nameOrCode == Language(Language::brazilian_portuguese).Name() || nameOrCode == Language(Language::brazilian_portuguese).Locale())
|
||||
Construct(Language::brazilian_portuguese);
|
||||
else if (nameOrCode == Language(Language::finnish).Name() || nameOrCode == Language(Language::finnish).Locale())
|
||||
Construct(Language::finnish);
|
||||
else if (nameOrCode == Language(Language::german).Name() || nameOrCode == Language(Language::german).Locale())
|
||||
Construct(Language::german);
|
||||
else if (nameOrCode == Language(Language::danish).Name() || nameOrCode == Language(Language::danish).Locale())
|
||||
Construct(Language::danish);
|
||||
else if (nameOrCode == Language(Language::korean).Name() || nameOrCode == Language(Language::korean).Locale())
|
||||
Construct(Language::korean);
|
||||
else
|
||||
Construct(Language::english);
|
||||
}
|
||||
|
||||
void Language::Construct(const unsigned int code) {
|
||||
_code = code;
|
||||
if (_code == Language::spanish) {
|
||||
_name = "Español";
|
||||
_locale = "es";
|
||||
}
|
||||
else if (_code == Language::russian) {
|
||||
_name = "Русский";
|
||||
_locale = "ru";
|
||||
}
|
||||
else if (_code == Language::french) {
|
||||
_name = "Français";
|
||||
_locale = "fr";
|
||||
}
|
||||
else if (_code == Language::chinese) {
|
||||
_name = "简体中文";
|
||||
_locale = "zh_CN";
|
||||
}
|
||||
else if (_code == Language::polish) {
|
||||
_name = "Polski";
|
||||
_locale = "pl";
|
||||
}
|
||||
else if (_code == Language::brazilian_portuguese) {
|
||||
_name = "Português do Brasil";
|
||||
_locale = "pt_BR";
|
||||
}
|
||||
else if (_code == Language::finnish) {
|
||||
_name = "suomi";
|
||||
_locale = "fi";
|
||||
}
|
||||
else if (_code == Language::german) {
|
||||
_name = "Deutsch";
|
||||
_locale = "de";
|
||||
}
|
||||
else if (_code == Language::danish) {
|
||||
_name = "Dansk";
|
||||
_locale = "da";
|
||||
}
|
||||
else if (_code == Language::korean) {
|
||||
_name = "한국어";
|
||||
_locale = "ko";
|
||||
}
|
||||
else {
|
||||
_name = "English";
|
||||
_locale = "en";
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int Language::Code() const {
|
||||
return _code;
|
||||
}
|
||||
|
||||
std::string Language::Name() const {
|
||||
return _name;
|
||||
}
|
||||
|
||||
std::string Language::Locale() const {
|
||||
return _locale;
|
||||
}
|
||||
|
||||
const std::vector<std::string> Language::Names({
|
||||
Language(Language::english).Name(),
|
||||
Language(Language::spanish).Name(),
|
||||
Language(Language::russian).Name(),
|
||||
Language(Language::french).Name(),
|
||||
Language(Language::chinese).Name(),
|
||||
Language(Language::polish).Name(),
|
||||
Language(Language::brazilian_portuguese).Name(),
|
||||
Language(Language::finnish).Name(),
|
||||
Language(Language::german).Name(),
|
||||
Language(Language::danish).Name(),
|
||||
Language(Language::korean).Name()
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/* 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_LANGUAGE__
|
||||
#define __LOOT_LANGUAGE__
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace loot {
|
||||
//Language class for simpler language support.
|
||||
class Language {
|
||||
public:
|
||||
Language(const unsigned int code);
|
||||
Language(const std::string& nameOrCode);
|
||||
|
||||
unsigned int Code() const;
|
||||
std::string Name() const;
|
||||
std::string Locale() const;
|
||||
|
||||
static const unsigned int any = 0; // This shouldn't be used as a selectable language, just for when picking any string in a message.
|
||||
static const unsigned int english = 1;
|
||||
static const unsigned int spanish = 2;
|
||||
static const unsigned int russian = 3;
|
||||
static const unsigned int french = 4;
|
||||
static const unsigned int chinese = 5;
|
||||
static const unsigned int polish = 6;
|
||||
static const unsigned int brazilian_portuguese = 7;
|
||||
static const unsigned int finnish = 8;
|
||||
static const unsigned int german = 9;
|
||||
static const unsigned int danish = 10;
|
||||
static const unsigned int korean = 11;
|
||||
|
||||
static const std::vector<std::string> Names;
|
||||
private:
|
||||
unsigned int _code;
|
||||
std::string _name;
|
||||
std::string _locale;
|
||||
|
||||
void Construct(const unsigned int code);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,64 @@
|
||||
/* 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 "masterlist.h"
|
||||
#include "game.h"
|
||||
#include "error.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
namespace lc = boost::locale;
|
||||
|
||||
namespace loot {
|
||||
bool Masterlist::Load(Game& game, const unsigned int language) {
|
||||
try {
|
||||
return Update(game);
|
||||
}
|
||||
catch (error& e) {
|
||||
if (e.code() != error::ok) {
|
||||
// Error wasn't a parsing error. Need to try parsing masterlist if it exists.
|
||||
try {
|
||||
MetadataList::Load(game.MasterlistPath());
|
||||
}
|
||||
catch (...) {}
|
||||
}
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
std::string Masterlist::GetRevision(const boost::filesystem::path& path, bool shortID) {
|
||||
if (revision.empty() || (shortID && revision.length() == 40) || (!shortID && revision.length() < 40))
|
||||
GetGitInfo(path, shortID);
|
||||
|
||||
return revision;
|
||||
}
|
||||
|
||||
std::string Masterlist::GetDate(const boost::filesystem::path& path) {
|
||||
if (date.empty())
|
||||
GetGitInfo(path, true);
|
||||
|
||||
return date;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/* 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_MASTERLIST__
|
||||
#define __LOOT_MASTERLIST__
|
||||
|
||||
#include "metadata_list.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
namespace loot {
|
||||
class Game;
|
||||
|
||||
class Masterlist : public MetadataList {
|
||||
public:
|
||||
|
||||
bool Load(Game& game, const unsigned int language); //Handles update with load fallback.
|
||||
bool Update(const Game& game);
|
||||
bool Update(const boost::filesystem::path& path,
|
||||
const std::string& repoURL,
|
||||
const std::string& repoBranch);
|
||||
|
||||
std::string GetRevision(const boost::filesystem::path& path, bool shortID);
|
||||
std::string GetDate(const boost::filesystem::path& path);
|
||||
|
||||
private:
|
||||
void GetGitInfo(const boost::filesystem::path& path, bool shortID);
|
||||
|
||||
std::string revision;
|
||||
std::string date;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,353 @@
|
||||
/* 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_CONDITION_PARSER__
|
||||
#define __LOOT_CONDITION_PARSER__
|
||||
|
||||
#ifndef BOOST_SPIRIT_UNICODE
|
||||
#define BOOST_SPIRIT_UNICODE
|
||||
#endif
|
||||
|
||||
#ifndef BOOST_SPIRIT_USE_PHOENIX_V3
|
||||
#define BOOST_SPIRIT_USE_PHOENIX_V3 1
|
||||
#endif
|
||||
|
||||
#include "../game.h"
|
||||
#include "../helpers.h"
|
||||
#include "../plugin.h"
|
||||
#include "../version.h"
|
||||
#include "../error.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <boost/regex.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/algorithm/string/regex.hpp>
|
||||
#include <boost/spirit/include/qi.hpp>
|
||||
#include <boost/spirit/include/phoenix_core.hpp>
|
||||
#include <boost/spirit/include/phoenix_operator.hpp>
|
||||
#include <boost/spirit/include/phoenix_bind.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/locale.hpp>
|
||||
#include <boost/format.hpp>
|
||||
|
||||
namespace loot {
|
||||
///////////////////////////////
|
||||
// Condition parser/evaluator
|
||||
///////////////////////////////
|
||||
|
||||
namespace qi = boost::spirit::qi;
|
||||
namespace unicode = boost::spirit::unicode;
|
||||
namespace phoenix = boost::phoenix;
|
||||
|
||||
template<typename Iterator, typename Skipper>
|
||||
class ConditionGrammar : public qi::grammar < Iterator, bool(), Skipper > {
|
||||
public:
|
||||
ConditionGrammar(Game * game, bool parseOnly) : ConditionGrammar::base_type(expression, "condition grammar"), _game(game), _parseOnly(parseOnly) {
|
||||
if (!_parseOnly && _game == nullptr)
|
||||
throw error(error::invalid_args, "A valid game pointer was not passed during a condition evaluation.");
|
||||
|
||||
expression =
|
||||
compound[qi::labels::_val = qi::labels::_1]
|
||||
>> *((qi::lit("or") >> compound)[qi::labels::_val = qi::labels::_val || qi::labels::_1])
|
||||
;
|
||||
|
||||
compound =
|
||||
condition[qi::labels::_val = qi::labels::_1]
|
||||
>> *((qi::lit("and") >> condition)[qi::labels::_val = qi::labels::_val && qi::labels::_1])
|
||||
;
|
||||
|
||||
condition =
|
||||
function[qi::labels::_val = qi::labels::_1]
|
||||
| (qi::lit("not") > condition)[qi::labels::_val = !qi::labels::_1]
|
||||
| ('(' > expression > ')')[qi::labels::_val = qi::labels::_1]
|
||||
;
|
||||
|
||||
function =
|
||||
("file(" > filePath > ')')[phoenix::bind(&ConditionGrammar::CheckFile, this, qi::labels::_val, qi::labels::_1)]
|
||||
| ("regex(" > quotedStr > ')')[phoenix::bind(&ConditionGrammar::CheckRegex, this, qi::labels::_val, qi::labels::_1)]
|
||||
| ("checksum(" > filePath > ',' > qi::hex > ')')[phoenix::bind(&ConditionGrammar::CheckSum, this, qi::labels::_val, qi::labels::_1, qi::labels::_2)]
|
||||
| ("version(" > filePath > ',' > quotedStr > ',' > comparator > ')')[phoenix::bind(&ConditionGrammar::CheckVersion, this, qi::labels::_val, qi::labels::_1, qi::labels::_2, qi::labels::_3)]
|
||||
| ("active(" > filePath > ')')[phoenix::bind(&ConditionGrammar::CheckActive, this, qi::labels::_val, qi::labels::_1)]
|
||||
;
|
||||
|
||||
quotedStr %= '"' > +(unicode::char_ - '"') > '"';
|
||||
|
||||
filePath %= '"' > +(unicode::char_ - invalidPathChars) > '"';
|
||||
|
||||
invalidPathChars %=
|
||||
unicode::char_(':')
|
||||
| unicode::char_('*')
|
||||
| unicode::char_('?')
|
||||
| unicode::char_('"')
|
||||
| unicode::char_('<')
|
||||
| unicode::char_('>')
|
||||
| unicode::char_('|')
|
||||
;
|
||||
|
||||
comparator %=
|
||||
unicode::string("==")
|
||||
| unicode::string("!=")
|
||||
| unicode::string("<=")
|
||||
| unicode::string(">=")
|
||||
| unicode::string("<")
|
||||
| unicode::string(">")
|
||||
;
|
||||
|
||||
expression.name("expression");
|
||||
compound.name("compound condition");
|
||||
condition.name("condition");
|
||||
function.name("function");
|
||||
quotedStr.name("quoted string");
|
||||
filePath.name("file path");
|
||||
comparator.name("comparator");
|
||||
invalidPathChars.name("invalid file path characters");
|
||||
|
||||
qi::on_error<qi::fail>(expression, phoenix::bind(&ConditionGrammar::SyntaxError, this, qi::labels::_1, qi::labels::_2, qi::labels::_3, qi::labels::_4));
|
||||
qi::on_error<qi::fail>(compound, phoenix::bind(&ConditionGrammar::SyntaxError, this, qi::labels::_1, qi::labels::_2, qi::labels::_3, qi::labels::_4));
|
||||
qi::on_error<qi::fail>(condition, phoenix::bind(&ConditionGrammar::SyntaxError, this, qi::labels::_1, qi::labels::_2, qi::labels::_3, qi::labels::_4));
|
||||
qi::on_error<qi::fail>(function, phoenix::bind(&ConditionGrammar::SyntaxError, this, qi::labels::_1, qi::labels::_2, qi::labels::_3, qi::labels::_4));
|
||||
qi::on_error<qi::fail>(quotedStr, phoenix::bind(&ConditionGrammar::SyntaxError, this, qi::labels::_1, qi::labels::_2, qi::labels::_3, qi::labels::_4));
|
||||
qi::on_error<qi::fail>(filePath, phoenix::bind(&ConditionGrammar::SyntaxError, this, qi::labels::_1, qi::labels::_2, qi::labels::_3, qi::labels::_4));
|
||||
qi::on_error<qi::fail>(comparator, phoenix::bind(&ConditionGrammar::SyntaxError, this, qi::labels::_1, qi::labels::_2, qi::labels::_3, qi::labels::_4));
|
||||
qi::on_error<qi::fail>(invalidPathChars, phoenix::bind(&ConditionGrammar::SyntaxError, this, qi::labels::_1, qi::labels::_2, qi::labels::_3, qi::labels::_4));
|
||||
}
|
||||
|
||||
private:
|
||||
qi::rule<Iterator, bool(), Skipper> expression, compound, condition, function;
|
||||
qi::rule<Iterator, std::string()> quotedStr, filePath, comparator;
|
||||
qi::rule<Iterator, char()> invalidPathChars;
|
||||
|
||||
Game * _game;
|
||||
bool _parseOnly;
|
||||
|
||||
//Eval's exact paths. Check for files and ghosted plugins.
|
||||
void CheckFile(bool& result, const std::string& file) {
|
||||
if (_parseOnly)
|
||||
return;
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "Checking to see if the file \"" << file << "\" exists.";
|
||||
|
||||
if (file == "LOOT") {
|
||||
result = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsSafePath(file)) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Invalid file path: " << file;
|
||||
throw loot::error(loot::error::invalid_args, boost::locale::translate("Invalid file path:").str() + " " + file);
|
||||
}
|
||||
|
||||
if (boost::iends_with(file, ".esp") || boost::iends_with(file, ".esm"))
|
||||
result = boost::filesystem::exists(_game->DataPath() / file) || boost::filesystem::exists(_game->DataPath() / (file + ".ghost"));
|
||||
else
|
||||
result = boost::filesystem::exists(_game->DataPath() / file);
|
||||
|
||||
if (result)
|
||||
BOOST_LOG_TRIVIAL(trace) << "The file does exist.";
|
||||
else
|
||||
BOOST_LOG_TRIVIAL(trace) << "The file does not exist.";
|
||||
}
|
||||
|
||||
void CheckRegex(bool& result, const std::string& regexStr) {
|
||||
if (_parseOnly)
|
||||
return;
|
||||
result = false;
|
||||
//Can't support a regex string where all path components may be regex, since this could
|
||||
//lead to massive scanning if an unfortunately-named directory is encountered.
|
||||
//As such, only the filename portion can be a regex. Need to separate that from the rest
|
||||
//of the string.
|
||||
|
||||
/* Look for directory separators: in non-regex strings, they are '/' and '\'. In regex,
|
||||
the backslash is special so must be escaped using another backslash, so look for '/' and "\\".
|
||||
In C++ string literals, the backslash must be escaped once more to give "\\\\".
|
||||
Split the regex with another regex! */
|
||||
|
||||
//Need to also check if the regex is for a safe path.
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "Checking to see if any files matching the regex \"" << regexStr << "\" exist.";
|
||||
|
||||
boost::regex sepReg("/|(\\\\\\\\)", boost::regex::ECMAScript | boost::regex::icase);
|
||||
|
||||
std::vector<std::string> components;
|
||||
boost::sregex_token_iterator it(regexStr.begin(), regexStr.end(), sepReg, -1);
|
||||
boost::sregex_token_iterator itend;
|
||||
for (; it != itend; ++it) {
|
||||
components.push_back(*it);
|
||||
}
|
||||
|
||||
std::string filename = components.back();
|
||||
components.pop_back();
|
||||
|
||||
std::string parent;
|
||||
for (std::vector<std::string>::const_iterator it = components.begin(), endIt = components.end()--; it != endIt; ++it) {
|
||||
if (*it == ".")
|
||||
continue;
|
||||
|
||||
parent += *it + '/';
|
||||
}
|
||||
|
||||
if (boost::contains(parent, "../../")) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Invalid folder path: " << parent;
|
||||
throw loot::error(loot::error::invalid_args, boost::locale::translate("Invalid folder path:").str() + " " + parent);
|
||||
}
|
||||
|
||||
//Now we have a valid parent path and a regex filename. Check that
|
||||
//the parent path exists and is a directory.
|
||||
|
||||
boost::filesystem::path parent_path = _game->DataPath() / parent;
|
||||
if (!boost::filesystem::exists(parent_path) || !boost::filesystem::is_directory(parent_path)) {
|
||||
BOOST_LOG_TRIVIAL(trace) << "The path \"" << parent_path << "\" does not exist or is not a directory.";
|
||||
return;
|
||||
}
|
||||
|
||||
boost::regex reg;
|
||||
try {
|
||||
reg = boost::regex(filename, boost::regex::ECMAScript | boost::regex::icase);
|
||||
}
|
||||
catch (std::exception& /*e*/) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Invalid regex string:" << filename;
|
||||
throw loot::error(loot::error::invalid_args, boost::locale::translate("Invalid regex string:").str() + " " + filename);
|
||||
}
|
||||
|
||||
for (boost::filesystem::directory_iterator itr(parent_path); itr != boost::filesystem::directory_iterator(); ++itr) {
|
||||
if (boost::regex_match(itr->path().filename().string(), reg)) {
|
||||
result = true;
|
||||
BOOST_LOG_TRIVIAL(trace) << "Matching file found: " << itr->path();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CheckSum(bool& result, const std::string& file, const uint32_t checksum) {
|
||||
if (_parseOnly)
|
||||
return;
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "Checking the CRC of the file \"" << file << "\".";
|
||||
|
||||
if (!IsSafePath(file)) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Invalid file path: " << file;
|
||||
throw loot::error(loot::error::invalid_args, boost::locale::translate("Invalid file path:").str() + " " + file);
|
||||
}
|
||||
|
||||
uint32_t crc;
|
||||
std::unordered_map<std::string, uint32_t>::iterator it = _game->crcCache.find(boost::locale::to_lower(file));
|
||||
|
||||
if (it != _game->crcCache.end())
|
||||
crc = it->second;
|
||||
else {
|
||||
if (file == "LOOT")
|
||||
crc = GetCrc32(boost::filesystem::absolute("LOOT.exe"));
|
||||
if (boost::filesystem::exists(_game->DataPath() / file))
|
||||
crc = GetCrc32(_game->DataPath() / file);
|
||||
else if ((boost::iends_with(file, ".esp") || boost::iends_with(file, ".esm")) && boost::filesystem::exists(_game->DataPath() / (file + ".ghost")))
|
||||
crc = GetCrc32(_game->DataPath() / (file + ".ghost"));
|
||||
else {
|
||||
result = false;
|
||||
return;
|
||||
}
|
||||
|
||||
_game->crcCache.insert(std::pair<std::string, uint32_t>(boost::locale::to_lower(file), crc));
|
||||
}
|
||||
|
||||
result = checksum == crc;
|
||||
}
|
||||
|
||||
void CheckVersion(bool& result, const std::string& file, const std::string& version, const std::string& comparator) {
|
||||
if (_parseOnly)
|
||||
return;
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "Checking version of file \"" << file << "\".";
|
||||
|
||||
CheckFile(result, file);
|
||||
if (!result) {
|
||||
if (comparator == "!=" || comparator == "<" || comparator == "<=")
|
||||
result = true;
|
||||
BOOST_LOG_TRIVIAL(trace) << "Version check result: " << result;
|
||||
return;
|
||||
}
|
||||
|
||||
Version givenVersion = Version(version);
|
||||
Version trueVersion;
|
||||
if (file == "LOOT")
|
||||
trueVersion = Version(boost::filesystem::absolute("LOOT.exe"));
|
||||
else if (_game->IsValidPlugin(file)) {
|
||||
Plugin plugin(*_game, file, true);
|
||||
trueVersion = Version(plugin.Version());
|
||||
}
|
||||
else
|
||||
trueVersion = Version(_game->DataPath() / file);
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "Version extracted: " << trueVersion.AsString();
|
||||
|
||||
if ((comparator == "==" && trueVersion != givenVersion)
|
||||
|| (comparator == "!=" && trueVersion == givenVersion)
|
||||
|| (comparator == "<" && trueVersion >= givenVersion)
|
||||
|| (comparator == ">" && trueVersion <= givenVersion)
|
||||
|| (comparator == "<=" && trueVersion > givenVersion)
|
||||
|| (comparator == ">=" && trueVersion < givenVersion))
|
||||
result = false;
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "Version check result: " << result;
|
||||
}
|
||||
|
||||
void CheckActive(bool& result, const std::string& file) {
|
||||
if (_parseOnly)
|
||||
return;
|
||||
|
||||
if (file == "LOOT")
|
||||
result = false;
|
||||
else
|
||||
result = _game->IsActive(file);
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "Active check result: " << result;
|
||||
}
|
||||
|
||||
void SyntaxError(Iterator const& /*first*/, Iterator const& last, Iterator const& errorpos, boost::spirit::info const& what) {
|
||||
std::string context(errorpos, min(errorpos + 50, last));
|
||||
boost::trim(context);
|
||||
|
||||
BOOST_LOG_TRIVIAL(error) << "Expected \"" << what.tag << "\" at \"" << context << "\".";
|
||||
|
||||
throw loot::error(loot::error::condition_eval_fail, (boost::format(boost::locale::translate("Expected \"%1%\" at \"%2%\".")) % what.tag % context).str());
|
||||
}
|
||||
|
||||
//Checks that the path (not regex) doesn't go outside any game folders.
|
||||
bool IsSafePath(const std::string& path) {
|
||||
BOOST_LOG_TRIVIAL(trace) << "Checking to see if the path \"" << path << "\" is safe.";
|
||||
|
||||
std::vector<std::string> components;
|
||||
boost::split(components, path, boost::is_any_of("/\\"));
|
||||
components.pop_back();
|
||||
std::string parent_path;
|
||||
for (auto it = components.cbegin(), endIt = components.cend()--; it != endIt; ++it) {
|
||||
if (*it == ".")
|
||||
continue;
|
||||
parent_path += *it + '/';
|
||||
}
|
||||
return !boost::contains(parent_path, "../../");
|
||||
}
|
||||
};
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,112 @@
|
||||
/* 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 "conditional_metadata.h"
|
||||
#include "condition_grammar.h"
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/locale.hpp>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace loot {
|
||||
namespace lc = boost::locale;
|
||||
|
||||
ConditionalMetadata::ConditionalMetadata() {}
|
||||
|
||||
ConditionalMetadata::ConditionalMetadata(const string& condition) : _condition(condition) {}
|
||||
|
||||
bool ConditionalMetadata::IsConditional() const {
|
||||
return !_condition.empty();
|
||||
}
|
||||
|
||||
std::string ConditionalMetadata::Condition() const {
|
||||
return _condition;
|
||||
}
|
||||
|
||||
bool ConditionalMetadata::EvalCondition(Game& game) const {
|
||||
if (_condition.empty())
|
||||
return true;
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "Evaluating condition: " << _condition;
|
||||
|
||||
unordered_map<std::string, bool>::const_iterator it = game.conditionCache.find(boost::locale::to_lower(_condition));
|
||||
if (it != game.conditionCache.end())
|
||||
return it->second;
|
||||
|
||||
ConditionGrammar<std::string::const_iterator, boost::spirit::qi::space_type> grammar(&game, false);
|
||||
boost::spirit::qi::space_type skipper;
|
||||
std::string::const_iterator begin, end;
|
||||
bool eval;
|
||||
|
||||
begin = _condition.begin();
|
||||
end = _condition.end();
|
||||
|
||||
bool r;
|
||||
try {
|
||||
r = boost::spirit::qi::phrase_parse(begin, end, grammar, skipper, eval);
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Failed to parse condition \"" << _condition << "\": " << e.what();
|
||||
throw loot::error(loot::error::condition_eval_fail, (boost::format(lc::translate("Failed to parse condition \"%1%\": %2%")) % _condition % e.what()).str());
|
||||
}
|
||||
|
||||
if (!r || begin != end) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Failed to parse condition \"" << _condition << "\".";
|
||||
throw loot::error(loot::error::condition_eval_fail, (boost::format(lc::translate("Failed to parse condition \"%1%\".")) % _condition).str());
|
||||
}
|
||||
|
||||
game.conditionCache.insert(pair<string, bool>(boost::locale::to_lower(_condition), eval));
|
||||
|
||||
return eval;
|
||||
}
|
||||
|
||||
void ConditionalMetadata::ParseCondition() const {
|
||||
if (_condition.empty())
|
||||
return;
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "Testing condition syntax: " << _condition;
|
||||
|
||||
ConditionGrammar<std::string::const_iterator, boost::spirit::qi::space_type> grammar(nullptr, true);
|
||||
boost::spirit::qi::space_type skipper;
|
||||
std::string::const_iterator begin, end;
|
||||
|
||||
begin = _condition.begin();
|
||||
end = _condition.end();
|
||||
|
||||
bool r;
|
||||
try {
|
||||
r = boost::spirit::qi::phrase_parse(begin, end, grammar, skipper);
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Failed to parse condition \"" << _condition << "\": " << e.what();
|
||||
throw loot::error(loot::error::condition_eval_fail, (boost::format(lc::translate("Failed to parse condition \"%1%\": %2%")) % _condition % e.what()).str());
|
||||
}
|
||||
|
||||
if (!r || begin != end) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Failed to parse condition \"" << _condition << "\".";
|
||||
throw loot::error(loot::error::condition_eval_fail, (boost::format(lc::translate("Failed to parse condition \"%1%\".")) % _condition).str());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/* 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_METADATA_CONDITIONAL_METADATA__
|
||||
#define __LOOT_METADATA_CONDITIONAL_METADATA__
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace loot {
|
||||
class Game;
|
||||
|
||||
class ConditionalMetadata {
|
||||
public:
|
||||
ConditionalMetadata();
|
||||
ConditionalMetadata(const std::string& condition);
|
||||
|
||||
bool IsConditional() const;
|
||||
bool EvalCondition(Game& game) const;
|
||||
void ParseCondition() const; // Throws error on parsing failure.
|
||||
|
||||
std::string Condition() const;
|
||||
private:
|
||||
std::string _condition;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,76 @@
|
||||
/* 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 "file.h"
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace loot {
|
||||
File::File() {}
|
||||
|
||||
File::File(const std::string& name, const std::string& display, const std::string& condition)
|
||||
: _name(name), _display(display), ConditionalMetadata(condition) {}
|
||||
|
||||
bool File::operator < (const File& rhs) const {
|
||||
return boost::ilexicographical_compare(Name(), rhs.Name());
|
||||
}
|
||||
|
||||
bool File::operator == (const File& rhs) const {
|
||||
return boost::iequals(Name(), rhs.Name());
|
||||
}
|
||||
|
||||
std::string File::Name() const {
|
||||
return _name;
|
||||
}
|
||||
|
||||
std::string File::DisplayName() const {
|
||||
if (_display.empty())
|
||||
return _name;
|
||||
else
|
||||
return _display;
|
||||
}
|
||||
}
|
||||
|
||||
namespace YAML {
|
||||
Emitter& operator << (Emitter& out, const loot::File& rhs) {
|
||||
if (!rhs.IsConditional() && rhs.DisplayName().empty())
|
||||
out << rhs.Name();
|
||||
else {
|
||||
out << BeginMap
|
||||
<< Key << "name" << Value << YAML::SingleQuoted << rhs.Name();
|
||||
|
||||
if (rhs.IsConditional())
|
||||
out << Key << "condition" << Value << YAML::SingleQuoted << rhs.Condition();
|
||||
|
||||
if (rhs.DisplayName() != rhs.Name())
|
||||
out << Key << "display" << Value << YAML::SingleQuoted << rhs.DisplayName();
|
||||
|
||||
out << EndMap;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/* 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_METADATA_FILE__
|
||||
#define __LOOT_METADATA_FILE__
|
||||
|
||||
#include "conditional_metadata.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <yaml-cpp/yaml.h>
|
||||
|
||||
namespace loot {
|
||||
class File : public ConditionalMetadata {
|
||||
public:
|
||||
File();
|
||||
File(const std::string& name, const std::string& display = "",
|
||||
const std::string& condition = "");
|
||||
|
||||
bool operator < (const File& rhs) const;
|
||||
bool operator == (const File& rhs) const;
|
||||
|
||||
std::string Name() const;
|
||||
std::string DisplayName() const;
|
||||
private:
|
||||
std::string _name;
|
||||
std::string _display;
|
||||
};
|
||||
}
|
||||
|
||||
namespace YAML {
|
||||
template<>
|
||||
struct convert < loot::File > {
|
||||
static Node encode(const loot::File& rhs) {
|
||||
Node node;
|
||||
node["condition"] = rhs.Condition();
|
||||
node["name"] = rhs.Name();
|
||||
node["display"] = rhs.DisplayName();
|
||||
return node;
|
||||
}
|
||||
|
||||
static bool decode(const Node& node, loot::File& rhs) {
|
||||
if (node.IsMap()) {
|
||||
if (!node["name"])
|
||||
return false;
|
||||
|
||||
std::string condition, name, display;
|
||||
if (node["condition"])
|
||||
condition = node["condition"].as<std::string>();
|
||||
if (node["name"])
|
||||
name = node["name"].as<std::string>();
|
||||
if (node["display"])
|
||||
display = node["display"].as<std::string>();
|
||||
rhs = loot::File(name, display, condition);
|
||||
}
|
||||
else
|
||||
rhs = loot::File(node.as<std::string>());
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
Emitter& operator << (Emitter& out, const loot::File& rhs);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,67 @@
|
||||
/* 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 "formid.h"
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace loot {
|
||||
FormID::FormID() : id(0) {}
|
||||
|
||||
FormID::FormID(const std::string& sourcePlugin, const uint32_t objectID) : plugin(sourcePlugin), id(objectID) {}
|
||||
|
||||
FormID::FormID(const std::vector<std::string>& sourcePlugins, const uint32_t formID) {
|
||||
unsigned int index = formID >> 24;
|
||||
id = formID & ~((uint32_t)index << 24);
|
||||
|
||||
if (index >= sourcePlugins.size()) {
|
||||
BOOST_LOG_TRIVIAL(trace) << hex << formID << dec << " in " << sourcePlugins.back() << " has a higher modIndex than expected.";
|
||||
index = sourcePlugins.size() - 1;
|
||||
}
|
||||
|
||||
plugin = sourcePlugins[index];
|
||||
}
|
||||
|
||||
bool FormID::operator == (const FormID& rhs) const {
|
||||
return (id == rhs.Id() && boost::iequals(plugin, rhs.Plugin()));
|
||||
}
|
||||
|
||||
bool FormID::operator < (const FormID& rhs) const {
|
||||
if (id != rhs.Id())
|
||||
return id < rhs.Id();
|
||||
else
|
||||
return boost::ilexicographical_compare(plugin, rhs.Plugin());
|
||||
}
|
||||
|
||||
std::string FormID::Plugin() const {
|
||||
return plugin;
|
||||
}
|
||||
|
||||
uint32_t FormID::Id() const {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user