mirror of
https://github.com/loot/libloot.git
synced 2026-07-27 14:16:01 -07:00
Replace Boost.Log with spdlog
This removes a bunch of Boost dependencies, and doesn't cause LOOT to crash on launch, so fixes loot/loot#835 too.
This commit is contained in:
+1
-1
@@ -21,7 +21,7 @@ install:
|
||||
- export CXX="g++-6" CC="gcc-6"
|
||||
# Build Boost.
|
||||
- wget https://raw.githubusercontent.com/WrinklyNinja/ci-scripts/1.5.0/install_boost.py
|
||||
- python install_boost.py --directory ~ --boost-version 1.64.0 -a 64 -t gcc-6 atomic chrono date_time filesystem iostreams locale log regex system thread
|
||||
- python install_boost.py --directory ~ --boost-version 1.64.0 -a 64 -t gcc-6 filesystem locale system
|
||||
# Install packages for generating documentation
|
||||
- pip install --user -r docs/requirements.txt
|
||||
# Add sphinx-build to PATH
|
||||
|
||||
+14
-3
@@ -61,7 +61,7 @@ ELSE ()
|
||||
set(RUST_TARGET x86_64-unknown-linux-gnu)
|
||||
ENDIF ()
|
||||
|
||||
find_package(Boost REQUIRED COMPONENTS atomic log log_setup regex locale thread date_time chrono filesystem system iostreams)
|
||||
find_package(Boost REQUIRED COMPONENTS locale filesystem system)
|
||||
|
||||
ExternalProject_Add(GTest
|
||||
PREFIX "external"
|
||||
@@ -137,6 +137,15 @@ ExternalProject_Add(testing-plugins
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND "")
|
||||
|
||||
ExternalProject_Add(spdlog
|
||||
PREFIX "external"
|
||||
URL "https://github.com/gabime/spdlog/archive/v0.14.0.tar.gz"
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND "")
|
||||
ExternalProject_Get_Property(spdlog SOURCE_DIR)
|
||||
set(SPDLOG_INCLUDE_DIRS "${SOURCE_DIR}/include")
|
||||
|
||||
ExternalProject_Add(yaml-cpp
|
||||
PREFIX "external"
|
||||
URL "https://github.com/WrinklyNinja/yaml-cpp/archive/patched-for-loot.tar.gz"
|
||||
@@ -223,6 +232,7 @@ set (LOOT_API_HEADERS "${CMAKE_SOURCE_DIR}/include/loot/api.h"
|
||||
"${CMAKE_SOURCE_DIR}/src/api/plugin/plugin_sorter.h"
|
||||
"${CMAKE_SOURCE_DIR}/src/api/helpers/git_helper.h"
|
||||
"${CMAKE_SOURCE_DIR}/src/api/helpers/crc.h"
|
||||
"${CMAKE_SOURCE_DIR}/src/api/helpers/logging.h"
|
||||
"${CMAKE_SOURCE_DIR}/src/api/helpers/version.h"
|
||||
"${CMAKE_SOURCE_DIR}/src/api/helpers/windows_encoding_converters.h")
|
||||
|
||||
@@ -277,6 +287,7 @@ include_directories ("${CMAKE_SOURCE_DIR}/src"
|
||||
${LIBGIT2_INCLUDE_DIRS}
|
||||
${ESPLUGIN_INCLUDE_DIRS}
|
||||
${Boost_INCLUDE_DIRS}
|
||||
${SPDLOG_INCLUDE_DIRS}
|
||||
${YAML_CPP_INCLUDE_DIRS}
|
||||
${GTEST_INCLUDE_DIRS}
|
||||
${PSEUDOSEM_INCLUDE_DIRS})
|
||||
@@ -339,12 +350,12 @@ ENDIF ()
|
||||
|
||||
# Build tests.
|
||||
add_executable (loot_api_internals_tests ${LOOT_API_SRC} ${LOOT_API_HEADERS} ${LOOT_TESTS_SRC} ${LOOT_TESTS_HEADERS})
|
||||
add_dependencies (loot_api_internals_tests esplugin libgit2 libloadorder pseudosem yaml-cpp GTest testing-metadata testing-plugins)
|
||||
add_dependencies (loot_api_internals_tests esplugin libgit2 libloadorder pseudosem spdlog yaml-cpp GTest testing-metadata testing-plugins)
|
||||
target_link_libraries(loot_api_internals_tests ${Boost_LIBRARIES} ${LIBGIT2_LIBRARIES} ${ESPLUGIN_LIBRARIES} ${LIBLOADORDER_LIBRARIES} ${LOOT_LIBS} ${YAML_CPP_LIBRARIES} ${GTEST_LIBRARIES})
|
||||
|
||||
# Build API.
|
||||
add_library (loot_api ${LOOT_API_SRC} ${LOOT_API_HEADERS})
|
||||
add_dependencies (loot_api esplugin libgit2 libloadorder pseudosem yaml-cpp)
|
||||
add_dependencies (loot_api esplugin libgit2 libloadorder pseudosem spdlog yaml-cpp)
|
||||
target_link_libraries(loot_api ${Boost_LIBRARIES} ${LIBGIT2_LIBRARIES} ${ESPLUGIN_LIBRARIES} ${LIBLOADORDER_LIBRARIES} ${LOOT_LIBS} ${YAML_CPP_LIBRARIES})
|
||||
|
||||
# Build API tests.
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ install:
|
||||
- ps: (New-Object Net.WebClient).DownloadFile('https://raw.githubusercontent.com/WrinklyNinja/ci-scripts/1.1.0/install_boost.py', "$env:APPVEYOR_BUILD_FOLDER\install_boost.py")
|
||||
- ps: (New-Object Net.WebClient).DownloadFile('https://raw.githubusercontent.com/WrinklyNinja/ci-scripts/1.2.0/delete_old_bintray_versions.py', "$env:APPVEYOR_BUILD_FOLDER\delete_old_bintray_versions.py")
|
||||
- ps: if ($env:PLATFORM -eq "Win32") { $env:ADDRESS_MODEL = '32' } else { $env:ADDRESS_MODEL = '64' }
|
||||
- ps: python $env:APPVEYOR_BUILD_FOLDER\install_boost.py -d C:\projects -b 1.61.0 -a $env:ADDRESS_MODEL atomic chrono date_time filesystem iostreams locale log regex system thread
|
||||
- ps: python $env:APPVEYOR_BUILD_FOLDER\install_boost.py -d C:\projects -b 1.61.0 -a $env:ADDRESS_MODEL filesystem locale system
|
||||
|
||||
before_build:
|
||||
- cd %APPVEYOR_BUILD_FOLDER%
|
||||
|
||||
+6
-52
@@ -25,14 +25,9 @@
|
||||
#include "loot/api.h"
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/log/core.hpp>
|
||||
#include <boost/log/expressions.hpp>
|
||||
#include <boost/log/support/date_time.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/log/utility/setup/common_attributes.hpp>
|
||||
#include <boost/log/utility/setup/file.hpp>
|
||||
|
||||
#include "api/game/game.h"
|
||||
#include "api/helpers/logging.h"
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
@@ -46,54 +41,13 @@ std::string ResolvePath(const std::string& path) {
|
||||
return fs::read_symlink(path).string();
|
||||
}
|
||||
|
||||
LogLevel mapFromBoostLog(boost::log::trivial::severity_level severity) {
|
||||
using boost::log::trivial::severity_level;
|
||||
switch (severity) {
|
||||
case severity_level::trace:
|
||||
return LogLevel::trace;
|
||||
case severity_level::debug:
|
||||
return LogLevel::debug;
|
||||
case severity_level::info:
|
||||
return LogLevel::info;
|
||||
case severity_level::warning:
|
||||
return LogLevel::warning;
|
||||
case severity_level::error:
|
||||
return LogLevel::error;
|
||||
case severity_level::fatal:
|
||||
return LogLevel::fatal;
|
||||
default:
|
||||
return LogLevel::trace;
|
||||
}
|
||||
}
|
||||
|
||||
class LoggingSink : public boost::log::sinks::basic_formatted_sink_backend<char, boost::log::sinks::concurrent_feeding> {
|
||||
public:
|
||||
LoggingSink(std::function<void(LogLevel, const char*)> callback) {
|
||||
this->callback = callback;
|
||||
}
|
||||
|
||||
void consume(const boost::log::record_view& rec, const std::string& str) {
|
||||
using boost::log::trivial::severity_level;
|
||||
auto severity = rec.attribute_values()[boost::log::aux::default_attribute_names::severity()].extract<severity_level>();
|
||||
if (!severity) {
|
||||
return;
|
||||
}
|
||||
|
||||
callback(mapFromBoostLog(*severity), str.c_str());
|
||||
}
|
||||
|
||||
private:
|
||||
std::function<void(LogLevel, const char*)> callback;
|
||||
};
|
||||
|
||||
LOOT_API void SetLoggingCallback(std::function<void(LogLevel, const char*)> callback) {
|
||||
typedef boost::log::sinks::synchronous_sink<LoggingSink> sink_t;
|
||||
auto sink = std::make_shared<SpdLoggingSink>(callback);
|
||||
auto logger = std::make_shared<spdlog::logger>(LOGGER_NAME, sink);
|
||||
logger->set_level(spdlog::level::level_enum::trace);
|
||||
|
||||
auto sink_backend = boost::make_shared<LoggingSink>(callback);
|
||||
boost::shared_ptr<sink_t> sink(new sink_t(sink_backend));
|
||||
|
||||
boost::log::core::get()->remove_all_sinks();
|
||||
boost::log::core::get()->add_sink(sink);
|
||||
spdlog::drop(LOGGER_NAME);
|
||||
spdlog::register_logger(logger);
|
||||
}
|
||||
|
||||
LOOT_API bool IsCompatible(const unsigned int versionMajor, const unsigned int versionMinor, const unsigned int versionPatch) {
|
||||
|
||||
+25
-10
@@ -29,11 +29,9 @@
|
||||
#include <cmath>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/locale.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#include "api/api_database.h"
|
||||
#include "api/helpers/logging.h"
|
||||
#include "api/plugin/plugin_sorter.h"
|
||||
#include "loot/exception/file_access_error.h"
|
||||
|
||||
@@ -66,7 +64,10 @@ Game::Game(const GameType gameType,
|
||||
localDataPath_(localDataPath),
|
||||
cache_(std::make_shared<GameCache>()),
|
||||
loadOrderHandler_(std::make_shared<LoadOrderHandler>()) {
|
||||
BOOST_LOG_TRIVIAL(info) << "Initialising load order data for game of type " << (int)type_ << " at: " << gamePath_;
|
||||
auto logger = getLogger();
|
||||
if (logger) {
|
||||
logger->info("Initialising load order data for game of type {} at: {}", (int) type_, gamePath_.string());
|
||||
}
|
||||
|
||||
loadOrderHandler_->Init(type_, gamePath_, localDataPath_);
|
||||
|
||||
@@ -98,6 +99,7 @@ bool Game::IsValidPlugin(const std::string& plugin) const {
|
||||
}
|
||||
|
||||
void Game::LoadPlugins(const std::vector<std::string>& plugins, bool loadHeadersOnly) {
|
||||
auto logger = getLogger();
|
||||
uintmax_t meanFileSize = 0;
|
||||
std::multimap<uintmax_t, string> sizeMap;
|
||||
|
||||
@@ -125,15 +127,22 @@ void Game::LoadPlugins(const std::vector<std::string>& plugins, bool loadHeaders
|
||||
// Divide the plugins up by thread.
|
||||
unsigned int pluginsPerThread = ceil((double)sizeMap.size() / threadsToUse);
|
||||
vector<vector<string>> pluginGroups(threadsToUse);
|
||||
BOOST_LOG_TRIVIAL(info) << "Loading " << sizeMap.size() << " plugins using " << threadsToUse << " threads, with up to " << pluginsPerThread << " plugins per thread.";
|
||||
if (logger) {
|
||||
logger->info("Loading {} plugins using {} threads, with up to {} plugins per thread.", sizeMap.size(), threadsToUse, pluginsPerThread);
|
||||
}
|
||||
|
||||
// The plugins should be split between the threads so that the data
|
||||
// load is as evenly spread as possible.
|
||||
size_t currentGroup = 0;
|
||||
for (const auto& plugin : sizeMap) {
|
||||
if (currentGroup == threadsToUse)
|
||||
if (currentGroup == threadsToUse) {
|
||||
currentGroup = 0;
|
||||
BOOST_LOG_TRIVIAL(trace) << "Adding plugin " << plugin.second << " to loading group " << currentGroup;
|
||||
}
|
||||
|
||||
if (logger) {
|
||||
logger->trace("Adding plugin {} to loading group {}", plugin.second, currentGroup);
|
||||
}
|
||||
|
||||
pluginGroups[currentGroup].push_back(plugin.second);
|
||||
++currentGroup;
|
||||
}
|
||||
@@ -143,18 +152,24 @@ void Game::LoadPlugins(const std::vector<std::string>& plugins, bool loadHeaders
|
||||
loadOrderHandler_->LoadCurrentState();
|
||||
|
||||
// Load the plugins.
|
||||
BOOST_LOG_TRIVIAL(trace) << "Starting plugin loading.";
|
||||
if (logger) {
|
||||
logger->trace("Starting plugin loading.");
|
||||
}
|
||||
vector<thread> threads;
|
||||
while (threads.size() < threadsToUse) {
|
||||
vector<string>& pluginGroup = pluginGroups[threads.size()];
|
||||
threads.push_back(thread([&]() {
|
||||
for (auto pluginName : pluginGroup) {
|
||||
BOOST_LOG_TRIVIAL(trace) << "Loading " << pluginName;
|
||||
if (logger) {
|
||||
logger->trace("Loading {}", pluginName);
|
||||
}
|
||||
const bool loadHeader = boost::iequals(pluginName, masterFile_) || loadHeadersOnly;
|
||||
try {
|
||||
cache_->AddPlugin(Plugin(Type(), DataPath(), loadOrderHandler_, pluginName, loadHeader));
|
||||
} catch(std::exception& e) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Caught exception while trying to add " << pluginName << " to the cache: " << e.what();
|
||||
if (logger) {
|
||||
logger->trace("Caught exception while trying to add {} to the cache: {}", pluginName, e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/locale.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
using boost::locale::to_lower;
|
||||
using std::lock_guard;
|
||||
|
||||
@@ -26,9 +26,9 @@
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#include "loot/exception/error_categories.h"
|
||||
#include "api/helpers/logging.h"
|
||||
|
||||
using boost::format;
|
||||
using std::string;
|
||||
@@ -78,7 +78,10 @@ void LoadOrderHandler::Init(const GameType& gameType,
|
||||
}
|
||||
|
||||
void LoadOrderHandler::LoadCurrentState() {
|
||||
BOOST_LOG_TRIVIAL(debug) << "Loading the current load order state.";
|
||||
auto logger = getLogger();
|
||||
if (logger) {
|
||||
logger->debug("Loading the current load order state.");
|
||||
}
|
||||
|
||||
unsigned int ret = lo_load_current_state(gh_);
|
||||
|
||||
@@ -86,7 +89,10 @@ void LoadOrderHandler::LoadCurrentState() {
|
||||
}
|
||||
|
||||
bool LoadOrderHandler::IsPluginActive(const std::string& pluginName) const {
|
||||
BOOST_LOG_TRIVIAL(debug) << "Checking if plugin \"" << pluginName << "\" is active.";
|
||||
auto logger = getLogger();
|
||||
if (logger) {
|
||||
logger->debug("Checking if plugin \"{}\" is active.", pluginName);
|
||||
}
|
||||
|
||||
bool result = false;
|
||||
unsigned int ret = lo_get_plugin_active(gh_, pluginName.c_str(), &result);
|
||||
@@ -97,7 +103,10 @@ bool LoadOrderHandler::IsPluginActive(const std::string& pluginName) const {
|
||||
}
|
||||
|
||||
std::vector<std::string> LoadOrderHandler::GetLoadOrder() const {
|
||||
BOOST_LOG_TRIVIAL(debug) << "Getting load order.";
|
||||
auto logger = getLogger();
|
||||
if (logger) {
|
||||
logger->debug("Getting load order.");
|
||||
}
|
||||
|
||||
char ** pluginArr;
|
||||
size_t pluginArrSize;
|
||||
@@ -113,12 +122,19 @@ std::vector<std::string> LoadOrderHandler::GetLoadOrder() const {
|
||||
}
|
||||
|
||||
void LoadOrderHandler::SetLoadOrder(const std::vector<std::string>& loadOrder) const {
|
||||
BOOST_LOG_TRIVIAL(info) << "Setting load order.";
|
||||
auto logger = getLogger();
|
||||
if (logger) {
|
||||
logger->info("Setting load order.");
|
||||
}
|
||||
|
||||
size_t pluginArrSize = loadOrder.size();
|
||||
char ** pluginArr = new char*[pluginArrSize];
|
||||
int i = 0;
|
||||
for (const auto &plugin : loadOrder) {
|
||||
BOOST_LOG_TRIVIAL(info) << '\t' << '\t' << plugin;
|
||||
if (logger) {
|
||||
logger->info("\t\t{}", plugin);
|
||||
}
|
||||
|
||||
pluginArr[i] = new char[plugin.length() + 1];
|
||||
strcpy(pluginArr[i], plugin.c_str());
|
||||
++i;
|
||||
@@ -132,7 +148,9 @@ void LoadOrderHandler::SetLoadOrder(const std::vector<std::string>& loadOrder) c
|
||||
|
||||
HandleError("set the load order", ret);
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Load order set successfully.";
|
||||
if (logger) {
|
||||
logger->info("Load order set successfully.");
|
||||
}
|
||||
}
|
||||
|
||||
void LoadOrderHandler::HandleError(const std::string& operation, unsigned int returnCode) const {
|
||||
@@ -150,7 +168,11 @@ void LoadOrderHandler::HandleError(const std::string& operation, unsigned int re
|
||||
err = (format("libloadorder failed to " + operation + ". Details: %1%") % e).str();
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(error) << err;
|
||||
auto logger = getLogger();
|
||||
if (logger) {
|
||||
logger->error(err);
|
||||
}
|
||||
|
||||
throw std::system_error(returnCode, libloadorder_category(), err);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
#include <boost/crc.hpp>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include "api/helpers/logging.h"
|
||||
|
||||
#include "loot/exception/file_access_error.h"
|
||||
|
||||
@@ -48,7 +48,10 @@ size_t GetStreamSize(std::istream& stream) {
|
||||
//Calculate the CRC of the given file for comparison purposes.
|
||||
uint32_t GetCrc32(const boost::filesystem::path& filename) {
|
||||
try {
|
||||
BOOST_LOG_TRIVIAL(trace) << "Calculating CRC for: " << filename.string();
|
||||
auto logger = getLogger();
|
||||
if (logger) {
|
||||
logger->trace("Calculating CRC for: {}", filename.string());
|
||||
}
|
||||
|
||||
boost::filesystem::ifstream ifile(filename, std::ios::binary);
|
||||
ifile.exceptions(std::ios_base::badbit | std::ios_base::failbit);
|
||||
@@ -68,7 +71,9 @@ uint32_t GetCrc32(const boost::filesystem::path& filename) {
|
||||
}
|
||||
|
||||
uint32_t checksum = result.checksum();
|
||||
BOOST_LOG_TRIVIAL(debug) << "CRC32(\"" << filename.string() << "\"): " << std::hex << checksum << std::dec;
|
||||
if (logger) {
|
||||
logger->debug("CRC32(\"{}\"): {:x}", filename.string(), checksum);
|
||||
}
|
||||
return checksum;
|
||||
|
||||
} catch (std::exception& e) {
|
||||
|
||||
@@ -25,16 +25,30 @@
|
||||
#include "api/helpers/git_helper.h"
|
||||
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#include "loot/exception/error_categories.h"
|
||||
#include "loot/exception/git_state_error.h"
|
||||
#include "api/helpers/logging.h"
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
namespace loot {
|
||||
GitHelper::GitHelper() : logger_(getLogger()) {}
|
||||
|
||||
GitHelper::~GitHelper() {
|
||||
if (data_.repo != nullptr) {
|
||||
std::string path = git_repository_path(data_.repo);
|
||||
|
||||
if (!path.empty()) {
|
||||
try {
|
||||
FixRepoPermissions(path);
|
||||
} catch (std::exception&) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GitHelper::GitData::GitData() :
|
||||
repo(nullptr),
|
||||
remote(nullptr),
|
||||
@@ -78,12 +92,6 @@ GitHelper::GitData::~GitData() {
|
||||
delete[] checkout_options.paths.strings[i];
|
||||
}
|
||||
|
||||
if (!path.empty()) {
|
||||
try {
|
||||
FixRepoPermissions(path);
|
||||
} catch (std::exception&) {}
|
||||
}
|
||||
|
||||
git_libgit2_shutdown();
|
||||
}
|
||||
|
||||
@@ -110,20 +118,30 @@ bool GitHelper::IsRepository(const boost::filesystem::path& path) {
|
||||
|
||||
// Removes the read-only flag from some files in git repositories created by libgit2.
|
||||
void GitHelper::FixRepoPermissions(const boost::filesystem::path& path) {
|
||||
BOOST_LOG_TRIVIAL(trace) << "Recursively setting write permission on directory: " << path;
|
||||
if (logger_) {
|
||||
logger_->trace("Recursively setting write permission on directory: {}", path.string());
|
||||
}
|
||||
for (fs::recursive_directory_iterator it(path); it != fs::recursive_directory_iterator(); ++it) {
|
||||
if ((it->status().permissions() & (fs::owner_write | fs::group_write | fs::others_write)) == 0) {
|
||||
BOOST_LOG_TRIVIAL(trace) << "Setting write permission for: " << it->path();
|
||||
if (logger_) {
|
||||
logger_->trace("Setting write permission for: {}", it->path().string());
|
||||
}
|
||||
fs::permissions(it->path(), fs::add_perms | fs::owner_write);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int GitHelper::DiffFileCallback(const git_diff_delta *delta, float progress, void * payload) {
|
||||
BOOST_LOG_TRIVIAL(trace) << "Checking diff for: " << delta->old_file.path;
|
||||
auto logger = getLogger();
|
||||
if (logger) {
|
||||
logger->trace("Checking diff for: {}", delta->old_file.path);
|
||||
}
|
||||
|
||||
DiffPayload * gdp = (DiffPayload*)payload;
|
||||
if (strcmp(delta->old_file.path, gdp->fileToFind) == 0) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "Edited masterlist found.";
|
||||
if (logger) {
|
||||
logger->warn("Edited masterlist found.");
|
||||
}
|
||||
gdp->fileFound = true;
|
||||
}
|
||||
|
||||
@@ -136,7 +154,9 @@ void GitHelper::Clone(const boost::filesystem::path& path, const std::string& ur
|
||||
throw GitStateError("Cannot clone repository that has already been opened.");
|
||||
|
||||
// Clone the remote repository.
|
||||
BOOST_LOG_TRIVIAL(info) << "Repository doesn't exist, cloning the remote repository.";
|
||||
if (logger_) {
|
||||
logger_->info("Repository doesn't exist, cloning the remote repository.");
|
||||
}
|
||||
|
||||
fs::path tempPath = path.parent_path() / fs::unique_path();
|
||||
|
||||
@@ -147,7 +167,9 @@ void GitHelper::Clone(const boost::filesystem::path& path, const std::string& ur
|
||||
// Directory is non-empty. Delete the masterlist file and
|
||||
// .git folder, then move any remaining files to a temporary
|
||||
// folder while the repo is cloned, before moving them back.
|
||||
BOOST_LOG_TRIVIAL(trace) << "Repo path not empty, renaming folder.";
|
||||
if (logger_) {
|
||||
logger_->trace("Repo path not empty, renaming folder.");
|
||||
}
|
||||
|
||||
// Clear any read-only flags first.
|
||||
FixRepoPermissions(path);
|
||||
@@ -166,7 +188,9 @@ void GitHelper::Clone(const boost::filesystem::path& path, const std::string& ur
|
||||
|
||||
if (fs::exists(tempPath)) {
|
||||
//Move contents back in.
|
||||
BOOST_LOG_TRIVIAL(trace) << "Repo path wasn't empty, moving previous files back in.";
|
||||
if (logger_) {
|
||||
logger_->trace("Repo path wasn't empty, moving previous files back in.");
|
||||
}
|
||||
for (fs::directory_iterator it(tempPath); it != fs::directory_iterator(); ++it) {
|
||||
if (!fs::exists(path / it->path().filename())) {
|
||||
//No conflict, OK to move back in.
|
||||
@@ -182,7 +206,9 @@ void GitHelper::Fetch(const std::string& remote) {
|
||||
if (data_.repo == nullptr)
|
||||
throw GitStateError("Cannot fetch updates for repository that has not been opened.");
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "Fetching updates from remote.";
|
||||
if (logger_) {
|
||||
logger_->trace("Fetching updates from remote.");
|
||||
}
|
||||
|
||||
// Get the origin remote.
|
||||
Call(git_remote_lookup(&data_.remote, data_.repo, remote.c_str()));
|
||||
@@ -193,7 +219,10 @@ void GitHelper::Fetch(const std::string& remote) {
|
||||
|
||||
// Log some stats on what was fetched either during update or clone.
|
||||
const git_transfer_progress * stats = git_remote_stats(data_.remote);
|
||||
BOOST_LOG_TRIVIAL(info) << "Received " << stats->indexed_objects << " of " << stats->total_objects << " objects in " << stats->received_bytes << " bytes.";
|
||||
if (logger_) {
|
||||
logger_->trace("Received {} of {} objects in {} bytes.",
|
||||
stats->indexed_objects, stats->total_objects, stats->received_bytes);
|
||||
}
|
||||
|
||||
git_remote_free(data_.remote);
|
||||
data_.remote = nullptr;
|
||||
@@ -209,26 +238,34 @@ void GitHelper::CheckoutNewBranch(const std::string& remote, const std::string&
|
||||
else if (data_.reference != nullptr)
|
||||
throw GitStateError("Cannot fetch repository updates, reference memory already allocated.");
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "Looking up commit referred to by the remote branch \"" << branch << "\".";
|
||||
if (logger_) {
|
||||
logger_->trace("Looking up commit referred to by the remote branch \"{}\".", branch);
|
||||
}
|
||||
Call(git_revparse_single(&data_.object, data_.repo, (remote + "/" + branch).c_str()));
|
||||
const git_oid * commit_id = git_object_id(data_.object);
|
||||
|
||||
// Create a branch.
|
||||
BOOST_LOG_TRIVIAL(trace) << "Creating the new branch.";
|
||||
if (logger_) {
|
||||
logger_->trace("Creating the new branch.");
|
||||
}
|
||||
Call(git_commit_lookup(&data_.commit, data_.repo, commit_id));
|
||||
Call(git_branch_create(&data_.reference, data_.repo, branch.c_str(), data_.commit, 1));
|
||||
|
||||
// Set upstream.
|
||||
BOOST_LOG_TRIVIAL(trace) << "Setting the upstream for the new branch.";
|
||||
if (logger_) {
|
||||
logger_->trace("Setting the upstream for the new branch.");
|
||||
}
|
||||
Call(git_branch_set_upstream(data_.reference, (remote + "/" + branch).c_str()));
|
||||
|
||||
// Check if HEAD points to the desired branch and set it to if not.
|
||||
if (!git_branch_is_head(data_.reference)) {
|
||||
BOOST_LOG_TRIVIAL(trace) << "Setting HEAD to follow branch: " << branch;
|
||||
if (logger_) {
|
||||
logger_->trace("Setting HEAD to follow branch: {}", branch);
|
||||
}
|
||||
Call(git_repository_set_head(data_.repo, (string("refs/heads/") + branch).c_str()));
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "Performing a Git checkout of HEAD.";
|
||||
if (logger_) {
|
||||
logger_->trace("Performing a Git checkout of HEAD.");
|
||||
}
|
||||
Call(git_checkout_head(data_.repo, &data_.checkout_options));
|
||||
|
||||
// Free tree and commit pointers. Reference pointer is still used below.
|
||||
@@ -254,7 +291,9 @@ void GitHelper::CheckoutRevision(const std::string& revision) {
|
||||
Call(git_repository_set_head_detached(data_.repo, oid));
|
||||
|
||||
// Checkout the new HEAD.
|
||||
BOOST_LOG_TRIVIAL(trace) << "Performing a Git checkout of HEAD.";
|
||||
if (logger_) {
|
||||
logger_->trace("Performing a Git checkout of HEAD.");
|
||||
}
|
||||
Call(git_checkout_head(data_.repo, &data_.checkout_options));
|
||||
|
||||
git_object_free(data_.object);
|
||||
@@ -271,11 +310,15 @@ std::string GitHelper::GetHeadShortId() {
|
||||
else if (data_.buffer.ptr != nullptr)
|
||||
throw GitStateError("Cannot fetch repository updates, buffer memory already allocated.");
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "Getting the Git object for HEAD.";
|
||||
if (logger_) {
|
||||
logger_->trace("Getting the Git object for HEAD.");
|
||||
}
|
||||
Call(git_repository_head(&data_.reference, data_.repo));
|
||||
Call(git_reference_peel(&data_.object, data_.reference, GIT_OBJ_COMMIT));
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "Generating hex string for Git object ID.";
|
||||
if (logger_) {
|
||||
logger_->trace("Generating hex string for Git object ID.");
|
||||
}
|
||||
Call(git_object_short_id(&data_.buffer, data_.object));
|
||||
string revision = data_.buffer.ptr;
|
||||
|
||||
@@ -294,24 +337,36 @@ GitHelper::GitData& GitHelper::GetData() {
|
||||
}
|
||||
|
||||
bool GitHelper::IsFileDifferent(const boost::filesystem::path& repoRoot, const std::string& filename) {
|
||||
auto logger = getLogger();
|
||||
|
||||
if (!IsRepository(repoRoot)) {
|
||||
BOOST_LOG_TRIVIAL(info) << "Unknown masterlist revision: Git repository missing.";
|
||||
if (logger) {
|
||||
logger->info("Unknown masterlist revision: Git repository missing.");
|
||||
}
|
||||
throw GitStateError("Cannot check if the \"" + filename + "\" working copy is edited, Git repository missing.");
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "Existing repository found, attempting to open it.";
|
||||
if (logger) {
|
||||
logger->trace("Existing repository found, attempting to open it.");
|
||||
}
|
||||
GitHelper git;
|
||||
git.Call(git_repository_open(&git.data_.repo, repoRoot.string().c_str()));
|
||||
|
||||
// Perform a git diff, then iterate the deltas to see if one exists for the masterlist.
|
||||
BOOST_LOG_TRIVIAL(trace) << "Getting the tree for the HEAD revision.";
|
||||
if (logger) {
|
||||
logger->trace("Getting the tree for the HEAD revision.");
|
||||
}
|
||||
git.Call(git_revparse_single(&git.data_.object, git.data_.repo, "HEAD^{tree}"));
|
||||
git.Call(git_tree_lookup(&git.data_.tree, git.data_.repo, git_object_id(git.data_.object)));
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "Performing git diff.";
|
||||
if (logger) {
|
||||
logger->trace("Performing git diff.");
|
||||
}
|
||||
git.Call(git_diff_tree_to_workdir_with_index(&git.data_.diff, git.data_.repo, git.data_.tree, NULL));
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "Iterating over git diff deltas.";
|
||||
if (logger) {
|
||||
logger->trace("Iterating over git diff deltas.");
|
||||
}
|
||||
GitHelper::DiffPayload payload;
|
||||
payload.fileFound = false;
|
||||
payload.fileToFind = filename.c_str();
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <git2.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
namespace loot {
|
||||
class GitHelper {
|
||||
@@ -59,6 +60,9 @@ public:
|
||||
git_clone_options clone_options;
|
||||
};
|
||||
|
||||
GitHelper();
|
||||
~GitHelper();
|
||||
|
||||
void Call(int error_code);
|
||||
|
||||
static bool IsRepository(const boost::filesystem::path& path);
|
||||
@@ -77,9 +81,10 @@ public:
|
||||
private:
|
||||
// Removes the read-only flag from some files in git repositories
|
||||
// created by libgit2.
|
||||
static void FixRepoPermissions(const boost::filesystem::path& path);
|
||||
void FixRepoPermissions(const boost::filesystem::path& path);
|
||||
|
||||
GitData data_;
|
||||
std::shared_ptr<spdlog::logger> logger_;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
/* LOOT
|
||||
|
||||
A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and
|
||||
Fallout: New Vegas.
|
||||
|
||||
Copyright (C) 2012-2016 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
|
||||
<https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef LOOT_API_HELPERS_LOGGING
|
||||
#define LOOT_API_HELPERS_LOGGING
|
||||
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#include "loot/enum/log_level.h"
|
||||
|
||||
namespace loot {
|
||||
static const char * LOGGER_NAME = "loot_api_logger";
|
||||
|
||||
inline std::shared_ptr<spdlog::logger> getLogger() {
|
||||
return spdlog::get(LOGGER_NAME);
|
||||
}
|
||||
|
||||
class SpdLoggingSink : public spdlog::sinks::base_sink<std::mutex> {
|
||||
public:
|
||||
SpdLoggingSink(std::function<void(LogLevel, const char*)> callback) {
|
||||
this->callback = callback;
|
||||
}
|
||||
|
||||
protected:
|
||||
void _sink_it(const spdlog::details::log_msg& msg) override {
|
||||
callback(mapFromSpdlog(msg.level), msg.raw.str().c_str());
|
||||
}
|
||||
|
||||
void _flush() override {}
|
||||
|
||||
private:
|
||||
std::function<void(LogLevel, const char*)> callback;
|
||||
|
||||
static LogLevel mapFromSpdlog(spdlog::level::level_enum severity) {
|
||||
using spdlog::level::level_enum;
|
||||
switch (severity) {
|
||||
case level_enum::trace:
|
||||
return LogLevel::trace;
|
||||
case level_enum::debug:
|
||||
return LogLevel::debug;
|
||||
case level_enum::info:
|
||||
return LogLevel::info;
|
||||
case level_enum::warn:
|
||||
return LogLevel::warning;
|
||||
case level_enum::err:
|
||||
return LogLevel::error;
|
||||
case level_enum::critical:
|
||||
return LogLevel::fatal;
|
||||
default:
|
||||
return LogLevel::trace;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
+83
-30
@@ -28,12 +28,12 @@
|
||||
#include <sstream>
|
||||
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#include "loot/exception/file_access_error.h"
|
||||
#include "loot/exception/git_state_error.h"
|
||||
#include "api/game/game.h"
|
||||
#include "api/helpers/git_helper.h"
|
||||
#include "api/helpers/logging.h"
|
||||
|
||||
using boost::format;
|
||||
using std::string;
|
||||
@@ -46,22 +46,34 @@ MasterlistInfo Masterlist::GetInfo(const boost::filesystem::path& path, bool sho
|
||||
GitHelper git;
|
||||
MasterlistInfo info;
|
||||
|
||||
auto logger = getLogger();
|
||||
|
||||
if (!fs::exists(path)) {
|
||||
BOOST_LOG_TRIVIAL(info) << "Unknown masterlist revision: No masterlist present.";
|
||||
if (logger) {
|
||||
logger->info("Unknown masterlist revision: No masterlist present.");
|
||||
}
|
||||
throw FileAccessError(string("N/A: No masterlist present at ") + path.string());
|
||||
} else if (!git.IsRepository(path.parent_path())) {
|
||||
BOOST_LOG_TRIVIAL(info) << "Unknown masterlist revision: Git repository missing.";
|
||||
if (logger) {
|
||||
logger->info("Unknown masterlist revision: Git repository missing.");
|
||||
}
|
||||
throw GitStateError(string("Unknown: \"") + path.parent_path().string() + "\" is not a Git repository.");
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "Existing repository found, attempting to open it.";
|
||||
if (logger) {
|
||||
logger->debug("Existing repository found, attempting to open it.");
|
||||
}
|
||||
git.Call(git_repository_open(&git.GetData().repo, path.parent_path().string().c_str()));
|
||||
|
||||
//Need to get the HEAD object, because the individual file has a different SHA.
|
||||
BOOST_LOG_TRIVIAL(info) << "Getting the Git object for the tree at HEAD.";
|
||||
if (logger) {
|
||||
logger->info("Getting the Git object for the tree at HEAD.");
|
||||
}
|
||||
git.Call(git_revparse_single(&git.GetData().object, git.GetData().repo, "HEAD"));
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "Generating hex string for Git object ID.";
|
||||
if (logger) {
|
||||
logger->trace("Generating hex string for Git object ID.");
|
||||
}
|
||||
if (shortID) {
|
||||
git.Call(git_object_short_id(&git.GetData().buffer, git.GetData().object));
|
||||
info.revision_id = git.GetData().buffer.ptr;
|
||||
@@ -70,7 +82,9 @@ MasterlistInfo Masterlist::GetInfo(const boost::filesystem::path& path, bool sho
|
||||
info.revision_id = git_oid_tostr(c_rev, GIT_OID_HEXSZ + 1, git_object_id(git.GetData().object));
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "Getting date for Git object.";
|
||||
if (logger) {
|
||||
logger->trace("Getting date for Git object.");
|
||||
}
|
||||
const git_oid * oid = git_object_id(git.GetData().object);
|
||||
git.Call(git_commit_lookup(&git.GetData().commit, git.GetData().repo, oid));
|
||||
git_time_t time = git_commit_time(git.GetData().commit);
|
||||
@@ -79,7 +93,9 @@ MasterlistInfo Masterlist::GetInfo(const boost::filesystem::path& path, bool sho
|
||||
out << std::put_time(std::gmtime(&time), "%Y-%m-%d");
|
||||
info.revision_date = out.str();
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "Diffing masterlist HEAD and working copy.";
|
||||
if (logger) {
|
||||
logger->trace("Diffing masterlist HEAD and working copy.");
|
||||
}
|
||||
info.is_modified = GitHelper::IsFileDifferent(path.parent_path(), path.filename().string());
|
||||
|
||||
return info;
|
||||
@@ -91,13 +107,18 @@ bool Masterlist::IsLatest(const boost::filesystem::path& path,
|
||||
throw std::invalid_argument("Repository branch must not be empty.");
|
||||
|
||||
GitHelper git;
|
||||
auto logger = getLogger();
|
||||
|
||||
if (!git.IsRepository(path.parent_path())) {
|
||||
BOOST_LOG_TRIVIAL(info) << "Cannot get latest masterlist revision: Git repository missing.";
|
||||
if (logger) {
|
||||
logger->info("Cannot get latest masterlist revision: Git repository missing.");
|
||||
}
|
||||
throw GitStateError(string("Unknown: \"") + path.parent_path().string() + "\" is not a Git repository.");
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Attempting to open repository.";
|
||||
if (logger) {
|
||||
logger->info("Attempting to open repository.");
|
||||
}
|
||||
git.Call(git_repository_open(&git.GetData().repo, path.parent_path().string().c_str()));
|
||||
|
||||
git.Fetch("origin");
|
||||
@@ -115,6 +136,7 @@ bool Masterlist::IsLatest(const boost::filesystem::path& path,
|
||||
|
||||
bool Masterlist::Update(const boost::filesystem::path& path, const std::string& repoUrl, const std::string& repoBranch) {
|
||||
GitHelper git;
|
||||
auto logger = getLogger();
|
||||
fs::path repoPath = path.parent_path();
|
||||
string filename = path.filename().string();
|
||||
|
||||
@@ -122,7 +144,9 @@ bool Masterlist::Update(const boost::filesystem::path& path, const std::string&
|
||||
throw std::invalid_argument("Repository URL and branch must not be empty.");
|
||||
|
||||
// Initialise checkout options.
|
||||
BOOST_LOG_TRIVIAL(debug) << "Setting up checkout options.";
|
||||
if (logger) {
|
||||
logger->debug("Setting up checkout options.");
|
||||
}
|
||||
char * paths = new char[filename.length() + 1];
|
||||
strcpy(paths, filename.c_str());
|
||||
git.GetData().checkout_options.checkout_strategy = GIT_CHECKOUT_FORCE | GIT_CHECKOUT_DONT_REMOVE_EXISTING;
|
||||
@@ -135,18 +159,24 @@ bool Masterlist::Update(const boost::filesystem::path& path, const std::string&
|
||||
git.GetData().clone_options.checkout_branch = repoBranch.c_str();
|
||||
|
||||
// Now try to access the repository if it exists, or clone one if it doesn't.
|
||||
BOOST_LOG_TRIVIAL(trace) << "Attempting to open the Git repository at: " << repoPath;
|
||||
if (logger) {
|
||||
logger->trace("Attempting to open the Git repository at: {}", repoPath.string());
|
||||
}
|
||||
if (!git.IsRepository(repoPath))
|
||||
git.Clone(repoPath, repoUrl);
|
||||
else {
|
||||
// Repository exists: check settings are correct, then pull updates.
|
||||
|
||||
// Open the repository.
|
||||
BOOST_LOG_TRIVIAL(info) << "Existing repository found, attempting to open it.";
|
||||
if (logger) {
|
||||
logger->info("Existing repository found, attempting to open it.");
|
||||
}
|
||||
git.Call(git_repository_open(&git.GetData().repo, repoPath.string().c_str()));
|
||||
|
||||
// Set the remote URL.
|
||||
BOOST_LOG_TRIVIAL(info) << "Using remote URL: " << repoUrl;
|
||||
if (logger) {
|
||||
logger->info("Using remote URL: {}", repoUrl);
|
||||
}
|
||||
git.Call(git_remote_set_url(git.GetData().repo, "origin", repoUrl.c_str()));
|
||||
|
||||
// Now fetch updates from the remote.
|
||||
@@ -164,14 +194,18 @@ bool Masterlist::Update(const boost::filesystem::path& path, const std::string&
|
||||
|
||||
// Check if HEAD points to the desired branch and set it to if not.
|
||||
if (!git_branch_is_head(git.GetData().reference)) {
|
||||
BOOST_LOG_TRIVIAL(trace) << "Setting HEAD to follow branch: " << repoBranch;
|
||||
if (logger) {
|
||||
logger->trace("Setting HEAD to follow branch: {}", repoBranch);
|
||||
}
|
||||
git.Call(git_repository_set_head(git.GetData().repo, (string("refs/heads/") + repoBranch).c_str()));
|
||||
}
|
||||
|
||||
// Get remote branch reference.
|
||||
git.Call(git_branch_upstream(&git.GetData().reference2, git.GetData().reference));
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "Checking HEAD and remote branch's mergeability.";
|
||||
if (logger) {
|
||||
logger->trace("Checking HEAD and remote branch's mergeability.");
|
||||
}
|
||||
git_merge_analysis_t analysis;
|
||||
git_merge_preference_t pref;
|
||||
git.Call(git_annotated_commit_from_ref(&git.GetData().annotated_commit, git.GetData().repo, git.GetData().reference2));
|
||||
@@ -179,9 +213,13 @@ bool Masterlist::Update(const boost::filesystem::path& path, const std::string&
|
||||
|
||||
if ((analysis & GIT_MERGE_ANALYSIS_FASTFORWARD) == 0 && (analysis & GIT_MERGE_ANALYSIS_UP_TO_DATE) == 0) {
|
||||
// The local branch can't be easily merged. Best just to delete and recreate it.
|
||||
BOOST_LOG_TRIVIAL(trace) << "Local branch cannot be easily merged with remote branch.";
|
||||
if (logger) {
|
||||
logger->trace("Local branch cannot be easily merged with remote branch.");
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "Detaching HEAD so that the branch can be recreated.";
|
||||
if (logger) {
|
||||
logger->trace("Detaching HEAD so that the branch can be recreated.");
|
||||
}
|
||||
git.Call(git_repository_detach_head(git.GetData().repo));
|
||||
|
||||
// Need to free ref before calling git.CheckoutNewBranch()
|
||||
@@ -206,8 +244,9 @@ bool Masterlist::Update(const boost::filesystem::path& path, const std::string&
|
||||
// No merge is required, but HEAD might be ahead of the remote branch. Check
|
||||
// to see if that's the case, and move HEAD back to match the remote branch
|
||||
// if so.
|
||||
BOOST_LOG_TRIVIAL(trace) << "Local branch is up-to-date with remote branch.";
|
||||
BOOST_LOG_TRIVIAL(trace) << "Checking to see if local and remote branch heads are equal.";
|
||||
if (logger) {
|
||||
logger->trace("Local branch is up-to-date with remote branch. Checking to see if local and remote branch heads are equal.");
|
||||
}
|
||||
|
||||
// Get local branch commit ID.
|
||||
git.Call(git_reference_peel(&git.GetData().object, git.GetData().reference, GIT_OBJ_COMMIT));
|
||||
@@ -223,21 +262,29 @@ bool Masterlist::Update(const boost::filesystem::path& path, const std::string&
|
||||
// action needs to be taken. Otherwise, a checkout
|
||||
// must be performed and the checked-out file parsed.
|
||||
if (!updateBranchHead) {
|
||||
BOOST_LOG_TRIVIAL(trace) << "Local and remote branch heads are equal.";
|
||||
if (logger) {
|
||||
logger->trace("Local and remote branch heads are equal.");
|
||||
}
|
||||
if (!GitHelper::IsFileDifferent(repoPath, filename)) {
|
||||
BOOST_LOG_TRIVIAL(info) << "Local branch and masterlist file are already up to date.";
|
||||
if (logger) {
|
||||
logger->info("Local branch and masterlist file are already up to date.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} else
|
||||
BOOST_LOG_TRIVIAL(trace) << "Local branch heads is ahead of remote branch head.";
|
||||
} else
|
||||
BOOST_LOG_TRIVIAL(trace) << "Local branch can be fast-forwarded to remote branch.";
|
||||
} else if (logger) {
|
||||
logger->trace("Local branch heads is ahead of remote branch head.");
|
||||
}
|
||||
} else if (logger) {
|
||||
logger->trace("Local branch can be fast-forwarded to remote branch.");
|
||||
}
|
||||
|
||||
if (updateBranchHead) {
|
||||
// The remote branch reference points to a particular
|
||||
// commit. Update the local branch reference to point
|
||||
// to the same commit.
|
||||
BOOST_LOG_TRIVIAL(trace) << "Syncing local branch head with remote branch head.";
|
||||
if (logger) {
|
||||
logger->trace("Syncing local branch head with remote branch head.");
|
||||
}
|
||||
git.Call(git_reference_set_target(&git.GetData().reference2, git.GetData().reference, remote_commit_id, "Setting branch reference."));
|
||||
|
||||
git_reference_free(git.GetData().reference2);
|
||||
@@ -247,7 +294,9 @@ bool Masterlist::Update(const boost::filesystem::path& path, const std::string&
|
||||
git_reference_free(git.GetData().reference);
|
||||
git.GetData().reference = nullptr;
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "Performing a Git checkout of HEAD.";
|
||||
if (logger) {
|
||||
logger->trace("Performing a Git checkout of HEAD.");
|
||||
}
|
||||
git.Call(git_checkout_head(git.GetData().repo, &git.GetData().checkout_options));
|
||||
}
|
||||
}
|
||||
@@ -263,7 +312,9 @@ bool Masterlist::Update(const boost::filesystem::path& path, const std::string&
|
||||
string revision = git.GetHeadShortId();
|
||||
|
||||
//Now try parsing the masterlist.
|
||||
BOOST_LOG_TRIVIAL(debug) << "Testing masterlist parsing.";
|
||||
if (logger) {
|
||||
logger->debug("Testing masterlist parsing.");
|
||||
}
|
||||
try {
|
||||
this->Load(path);
|
||||
|
||||
@@ -272,7 +323,9 @@ bool Masterlist::Update(const boost::filesystem::path& path, const std::string&
|
||||
parsingFailed = true;
|
||||
|
||||
//There was an error, roll back one revision.
|
||||
BOOST_LOG_TRIVIAL(error) << "Masterlist parsing failed. Masterlist revision " + string(revision) + ": " + e.what();
|
||||
if (logger) {
|
||||
logger->error("Masterlist parsing failed. Masterlist revision {}: {}", revision, e.what());
|
||||
}
|
||||
git.CheckoutRevision("HEAD^");
|
||||
}
|
||||
} while (parsingFailed);
|
||||
|
||||
@@ -26,9 +26,9 @@
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#include "api/helpers/crc.h"
|
||||
#include "api/helpers/logging.h"
|
||||
#include "api/metadata/condition_grammar.h"
|
||||
#include "loot/exception/condition_syntax_error.h"
|
||||
|
||||
@@ -55,7 +55,10 @@ bool ConditionEvaluator::evaluate(const std::string& condition) const {
|
||||
if (condition.empty())
|
||||
return true;
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "Evaluating condition: " << condition;
|
||||
auto logger = getLogger();
|
||||
if (logger) {
|
||||
logger->trace("Evaluating condition: {}", condition);
|
||||
}
|
||||
|
||||
auto cachedValue = gameCache_->GetCachedCondition(condition);
|
||||
if (cachedValue.second)
|
||||
@@ -272,7 +275,10 @@ bool ConditionEvaluator::compareVersions(const std::string & filePath, const std
|
||||
Version givenVersion = Version(testVersion);
|
||||
Version trueVersion = getVersion(filePath);
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "Version extracted: " << trueVersion.AsString();
|
||||
auto logger = getLogger();
|
||||
if (logger) {
|
||||
logger->trace("Version extracted: {}", trueVersion.AsString());
|
||||
}
|
||||
|
||||
return ((comparator == "==" && trueVersion == givenVersion)
|
||||
|| (comparator == "!=" && trueVersion != givenVersion)
|
||||
@@ -283,7 +289,10 @@ bool ConditionEvaluator::compareVersions(const std::string & filePath, const std
|
||||
}
|
||||
|
||||
void ConditionEvaluator::validatePath(const boost::filesystem::path& path) {
|
||||
BOOST_LOG_TRIVIAL(trace) << "Checking to see if the path \"" << path << "\" is safe.";
|
||||
auto logger = getLogger();
|
||||
if (logger) {
|
||||
logger->trace("Checking to see if the path \"{}\" is safe.", path.string());
|
||||
}
|
||||
|
||||
boost::filesystem::path temp;
|
||||
for (const auto& component : path) {
|
||||
@@ -357,7 +366,10 @@ bool ConditionEvaluator::isRegexMatchInDataDirectory(const std::pair<boost::file
|
||||
// Now we have a valid parent path and a regex filename. Check that the
|
||||
// parent path exists and is a directory.
|
||||
if (!isGameSubdirectory(pathRegex.first)) {
|
||||
BOOST_LOG_TRIVIAL(trace) << "The path \"" << pathRegex.first << "\" is not a game subdirectory.";
|
||||
auto logger = getLogger();
|
||||
if (logger) {
|
||||
logger->trace("The path \"{}\" is not a game subdirectory.", pathRegex.first.string());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -36,10 +36,8 @@
|
||||
#include <cstdint>
|
||||
#include <regex>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/algorithm/string/regex.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/spirit/include/phoenix_core.hpp>
|
||||
#include <boost/spirit/include/phoenix_operator.hpp>
|
||||
#include <boost/spirit/include/phoenix_bind.hpp>
|
||||
@@ -47,6 +45,7 @@
|
||||
|
||||
#include "loot/exception/condition_syntax_error.h"
|
||||
#include "api/game/game.h"
|
||||
#include "api/helpers/logging.h"
|
||||
#include "api/helpers/version.h"
|
||||
#include "api/metadata/condition_evaluator.h"
|
||||
#include "api/plugin/plugin.h"
|
||||
@@ -127,6 +126,8 @@ public:
|
||||
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));
|
||||
|
||||
logger_ = getLogger();
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -139,7 +140,9 @@ private:
|
||||
|
||||
//Eval's exact paths. Check for files and ghosted plugins.
|
||||
void CheckFile(bool& result, const std::string& file) const {
|
||||
BOOST_LOG_TRIVIAL(trace) << "Checking to see if the file \"" << file << "\" exists.";
|
||||
if (logger_) {
|
||||
logger_->trace("Checking to see if the file \"{}\" exists.", file);
|
||||
}
|
||||
|
||||
result = false;
|
||||
if (IsRegex(file))
|
||||
@@ -147,30 +150,40 @@ private:
|
||||
else
|
||||
result = evaluator_.fileExists(file);
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "File check result: " << result;
|
||||
if (logger_) {
|
||||
logger_->trace("File check result: {}", result);
|
||||
}
|
||||
}
|
||||
|
||||
void CheckMany(bool& result, const std::string& regexStr) const {
|
||||
BOOST_LOG_TRIVIAL(trace) << "Checking to see if more than one file matching the regex \"" << regexStr << "\" exist.";
|
||||
if (logger_) {
|
||||
logger_->trace("Checking to see if more than one file matching the regex \"{}\" exists.", regexStr);
|
||||
}
|
||||
|
||||
result = false;
|
||||
result = evaluator_.regexMatchesExist(regexStr);
|
||||
}
|
||||
|
||||
void CheckSum(bool& result, const std::string& file, const uint32_t checksum) {
|
||||
BOOST_LOG_TRIVIAL(trace) << "Checking the CRC of the file \"" << file << "\".";
|
||||
if (logger_) {
|
||||
logger_->trace("Checking the CRC of the file \"{}\".", file);
|
||||
}
|
||||
|
||||
result = false;
|
||||
result = evaluator_.checksumMatches(file, checksum);
|
||||
}
|
||||
|
||||
void CheckVersion(bool& result, const std::string& file, const std::string& version, const std::string& comparator) const {
|
||||
BOOST_LOG_TRIVIAL(trace) << "Checking version of file \"" << file << "\".";
|
||||
if (logger_) {
|
||||
logger_->trace("Checking the version of the file \"{}\".", file);
|
||||
}
|
||||
|
||||
result = false;
|
||||
result = evaluator_.compareVersions(file, version, comparator);
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "Version check result: " << result;
|
||||
if (logger_) {
|
||||
logger_->trace("Version check result: {}", result);
|
||||
}
|
||||
}
|
||||
|
||||
void CheckActive(bool& result, const std::string& file) const {
|
||||
@@ -180,11 +193,15 @@ private:
|
||||
else
|
||||
result = evaluator_.isPluginActive(file);
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "Active check result: " << result;
|
||||
if (logger_) {
|
||||
logger_->trace("Active check result: {}", result);
|
||||
}
|
||||
}
|
||||
|
||||
void CheckManyActive(bool& result, const std::string& regexStr) const {
|
||||
BOOST_LOG_TRIVIAL(trace) << "Checking to see if more than one file matching the regex \"" << regexStr << "\" exist.";
|
||||
if (logger_) {
|
||||
logger_->trace("Checking to see if more than one file matching the regex \"{}\" is active.", regexStr);
|
||||
}
|
||||
|
||||
result = false;
|
||||
result = evaluator_.arePluginsActive(regexStr);
|
||||
@@ -203,6 +220,7 @@ private:
|
||||
boost::spirit::qi::rule<Iterator, char()> invalidPathChars_;
|
||||
|
||||
const ConditionEvaluator& evaluator_;
|
||||
std::shared_ptr<spdlog::logger> logger_;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -24,10 +24,8 @@
|
||||
|
||||
#include "loot/metadata/conditional_metadata.h"
|
||||
|
||||
#include <boost/locale.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#include "api/game/game.h"
|
||||
#include "api/helpers/logging.h"
|
||||
#include "api/metadata/condition_evaluator.h"
|
||||
|
||||
using std::string;
|
||||
@@ -46,7 +44,12 @@ std::string ConditionalMetadata::GetCondition() const {
|
||||
}
|
||||
|
||||
void ConditionalMetadata::ParseCondition() const {
|
||||
BOOST_LOG_TRIVIAL(trace) << "Testing condition syntax: " << condition_;
|
||||
ConditionEvaluator().evaluate(condition_);
|
||||
if (!condition_.empty()) {
|
||||
auto logger = getLogger();
|
||||
if (logger) {
|
||||
logger->trace("Testing condition syntax: {}", condition_);
|
||||
}
|
||||
ConditionEvaluator().evaluate(condition_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
#include "loot/metadata/message.h"
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#include "api/game/game.h"
|
||||
|
||||
|
||||
@@ -26,10 +26,10 @@
|
||||
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/locale.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#include "api/game/game.h"
|
||||
#include "api/helpers/crc.h"
|
||||
#include "api/helpers/logging.h"
|
||||
|
||||
namespace loot {
|
||||
PluginCleaningData::PluginCleaningData() : crc_(0), itm_(0), ref_(0), nav_(0) {}
|
||||
@@ -78,7 +78,10 @@ std::vector<MessageContent> PluginCleaningData::GetInfo() const {
|
||||
}
|
||||
|
||||
MessageContent PluginCleaningData::ChooseInfo(const std::string& language) const {
|
||||
BOOST_LOG_TRIVIAL(trace) << "Choosing dirty info content.";
|
||||
auto logger = getLogger();
|
||||
if (logger) {
|
||||
logger->trace("Choosing dirty info content.");
|
||||
}
|
||||
return MessageContent::Choose(info_, language);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,9 +30,9 @@
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/locale.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#include "api/game/game.h"
|
||||
#include "api/helpers/logging.h"
|
||||
|
||||
using std::inserter;
|
||||
using std::regex;
|
||||
@@ -50,7 +50,11 @@ PluginMetadata::PluginMetadata(const std::string& n) : name_(n), enabled_(true)
|
||||
}
|
||||
|
||||
void PluginMetadata::MergeMetadata(const PluginMetadata& plugin) {
|
||||
BOOST_LOG_TRIVIAL(trace) << "Merging metadata for: " << name_;
|
||||
auto logger = getLogger();
|
||||
if (logger) {
|
||||
logger->trace("Merging metadata for: {}", name_);
|
||||
}
|
||||
|
||||
if (plugin.HasNameOnly())
|
||||
return;
|
||||
|
||||
@@ -92,7 +96,11 @@ void PluginMetadata::MergeMetadata(const PluginMetadata& plugin) {
|
||||
PluginMetadata PluginMetadata::NewMetadata(const PluginMetadata& plugin) const {
|
||||
using std::set_difference;
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "Comparing new metadata for: " << name_;
|
||||
auto logger = getLogger();
|
||||
if (logger) {
|
||||
logger->trace("Comparing new metadata for: {}", name_);
|
||||
}
|
||||
|
||||
PluginMetadata p(*this);
|
||||
|
||||
//Compare this plugin against the given plugin.
|
||||
|
||||
@@ -26,18 +26,21 @@
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#include "loot/exception/file_access_error.h"
|
||||
#include "api/metadata/yaml/plugin_metadata.h"
|
||||
#include "api/game/game.h"
|
||||
#include "api/helpers/logging.h"
|
||||
#include "api/metadata/condition_evaluator.h"
|
||||
#include "api/metadata/yaml/plugin_metadata.h"
|
||||
|
||||
namespace loot {
|
||||
void MetadataList::Load(const boost::filesystem::path& filepath) {
|
||||
Clear();
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "Loading file: " << filepath;
|
||||
auto logger = getLogger();
|
||||
if (logger) {
|
||||
logger->debug("Loading file: {}", filepath.string());
|
||||
}
|
||||
|
||||
boost::filesystem::ifstream in(filepath);
|
||||
if (!in.good())
|
||||
@@ -64,11 +67,16 @@ void MetadataList::Load(const boost::filesystem::path& filepath) {
|
||||
if (metadataList["bash_tags"])
|
||||
bashTags_ = metadataList["bash_tags"].as<std::set<std::string>>();
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "File loaded successfully.";
|
||||
if (logger) {
|
||||
logger->debug("File loaded successfully.");
|
||||
}
|
||||
}
|
||||
|
||||
void MetadataList::Save(const boost::filesystem::path& filepath) const {
|
||||
BOOST_LOG_TRIVIAL(trace) << "Saving metadata list to: " << filepath;
|
||||
auto logger = getLogger();
|
||||
if (logger) {
|
||||
logger->trace("Saving metadata list to: {}", filepath.string());
|
||||
}
|
||||
YAML::Emitter emitter;
|
||||
emitter.SetIndent(2);
|
||||
emitter << YAML::BeginMap;
|
||||
|
||||
+35
-11
@@ -28,12 +28,12 @@
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/locale.hpp>
|
||||
|
||||
#include "api/game/game.h"
|
||||
#include "api/helpers/crc.h"
|
||||
#include "api/helpers/logging.h"
|
||||
#include "api/helpers/version.h"
|
||||
#include "loot/exception/file_access_error.h"
|
||||
|
||||
@@ -53,6 +53,8 @@ Plugin::Plugin(const GameType gameType,
|
||||
loadsArchive_(false),
|
||||
crc_(0),
|
||||
numOverrideRecords_(0) {
|
||||
auto logger = getLogger();
|
||||
|
||||
try {
|
||||
boost::filesystem::path filepath = dataPath / name_;
|
||||
|
||||
@@ -68,10 +70,14 @@ Plugin::Plugin(const GameType gameType,
|
||||
}
|
||||
|
||||
if (!headerOnly) {
|
||||
BOOST_LOG_TRIVIAL(trace) << name_ << ": Caching CRC value.";
|
||||
if (logger) {
|
||||
logger->trace("{}: Caching CRC value.", name_);
|
||||
}
|
||||
crc_ = GetCrc32(filepath);
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << name_ << ": Counting override FormIDs.";
|
||||
if (logger) {
|
||||
logger->trace("{}: Counting override FormIDs.", name_);
|
||||
}
|
||||
ret = esp_plugin_count_override_records(esPlugin.get(), &numOverrideRecords_);
|
||||
if (ret != ESP_OK) {
|
||||
throw FileAccessError(name + " : Libespm error code: " + std::to_string(ret));
|
||||
@@ -79,8 +85,11 @@ Plugin::Plugin(const GameType gameType,
|
||||
}
|
||||
|
||||
//Also read Bash Tags applied and version string in description.
|
||||
if (logger) {
|
||||
logger->trace("{}: Attempting to extract Bash Tags from the description.", name_);
|
||||
}
|
||||
|
||||
string text = GetDescription();
|
||||
BOOST_LOG_TRIVIAL(trace) << name_ << ": " << "Attempting to extract Bash Tags from the description.";
|
||||
size_t pos1 = text.find("{{BASH:");
|
||||
if (pos1 != string::npos && pos1 + 7 != text.length()) {
|
||||
pos1 += 7;
|
||||
@@ -94,8 +103,11 @@ Plugin::Plugin(const GameType gameType,
|
||||
|
||||
for (auto &tag : bashTags) {
|
||||
boost::trim(tag);
|
||||
BOOST_LOG_TRIVIAL(trace) << name_ << ": " << "Extracted Bash Tag: " << tag;
|
||||
tags_.insert(Tag(tag));
|
||||
|
||||
if (logger) {
|
||||
logger->trace("{}: Extracted Bash Tag: {}", name_, tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -104,11 +116,15 @@ Plugin::Plugin(const GameType gameType,
|
||||
|
||||
loadsArchive_ = LoadsArchive(name_, gameType, dataPath);
|
||||
} catch (std::exception& e) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Cannot read plugin file \"" << name << "\". Details: " << e.what();
|
||||
if (logger) {
|
||||
logger->error("Cannot read plugin file \"{}\". Details: {}", name_, e.what());
|
||||
}
|
||||
throw FileAccessError((boost::format("Cannot read \"%1%\". Details: %2%") % name % e.what()).str());
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << name_ << ": " << "Plugin loading complete.";
|
||||
if (logger) {
|
||||
logger->trace("{}: Plugin loading complete.", name_);
|
||||
}
|
||||
}
|
||||
|
||||
std::string Plugin::GetName() const {
|
||||
@@ -185,7 +201,10 @@ bool Plugin::DoFormIDsOverlap(const PluginInterface& plugin) const {
|
||||
|
||||
return doPluginsOverlap;
|
||||
} catch (std::bad_cast&) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Tried to check if FormIDs overlapped with a non-Plugin implementation of PluginInterface.";
|
||||
auto logger = getLogger();
|
||||
if (logger) {
|
||||
logger->error("Tried to check if FormIDs overlapped with a non-Plugin implementation of PluginInterface.");
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -196,7 +215,10 @@ size_t Plugin::NumOverrideFormIDs() const {
|
||||
}
|
||||
|
||||
bool Plugin::IsValid(const std::string& filename, const GameType gameType, const boost::filesystem::path& dataPath) {
|
||||
BOOST_LOG_TRIVIAL(trace) << "Checking to see if \"" << filename << "\" is a valid plugin.";
|
||||
auto logger = getLogger();
|
||||
if (logger) {
|
||||
logger->trace("Checking to see if \"{}\" is a valid plugin.", filename);
|
||||
}
|
||||
|
||||
//If the filename passed ends in '.ghost', that should be trimmed.
|
||||
std::string name;
|
||||
@@ -214,7 +236,9 @@ bool Plugin::IsValid(const std::string& filename, const GameType gameType, const
|
||||
int ret = esp_plugin_is_valid(GetEspluginGameId(gameType), path.string().c_str(), true, &isValid);
|
||||
|
||||
if (ret != ESP_OK || !isValid) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "The .es(p|m) file \"" << filename << "\" is not a valid plugin.";
|
||||
if (logger) {
|
||||
logger->warn("The file \"{}\" is not a valid plugin.", filename);
|
||||
}
|
||||
}
|
||||
|
||||
return (ret == ESP_OK && isValid)
|
||||
@@ -314,7 +338,7 @@ unsigned int Plugin::GetEspluginGameId(GameType gameType) {
|
||||
|
||||
bool hasPluginFileExtension(const std::string& filename, GameType gameType) {
|
||||
bool espOrEsm = boost::iends_with(filename, ".esp") || boost::iends_with(filename, ".esm");
|
||||
bool lightMaster = (gameType == GameType::fo4 || gameType == GameType::tes5se)
|
||||
bool lightMaster = (gameType == GameType::fo4 || gameType == GameType::tes5se)
|
||||
&& boost::iends_with(filename, ".esl");
|
||||
|
||||
return espOrEsm || lightMaster;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user