Rename cxx directory to cpp

Also update the crate name and references to it. This helps distinguish the C++ wrapper that's built using CXX from CXX itself.
This commit is contained in:
Oliver Hamlet
2025-04-29 17:42:46 +01:00
parent 2b1b67daba
commit 83d9420422
90 changed files with 34 additions and 36 deletions
+111
View File
@@ -0,0 +1,111 @@
#include "loot/api.h"
#include "api/game.h"
#include "libloot-cpp/src/lib.rs.h"
#include "rust/cxx.h"
extern "C" {
extern const unsigned int LIBLOOT_VERSION_MAJOR;
extern const unsigned int LIBLOOT_VERSION_MINOR;
extern const unsigned int LIBLOOT_VERSION_PATCH;
extern const uint8_t LIBLOOT_LOG_LEVEL_TRACE;
extern const uint8_t LIBLOOT_LOG_LEVEL_DEBUG;
extern const uint8_t LIBLOOT_LOG_LEVEL_INFO;
extern const uint8_t LIBLOOT_LOG_LEVEL_WARNING;
extern const uint8_t LIBLOOT_LOG_LEVEL_ERROR;
extern const uint8_t LIBLOOT_LOG_LEVEL_FATAL;
void libloot_set_logging_callback(void (*callback)(uint8_t, const char*, void*),
void* context);
}
namespace {
using loot::LogLevel;
typedef std::function<void(LogLevel, std::string_view)> Callback;
static Callback STORED_CALLBACK;
LogLevel convert(uint8_t level) {
if (level == LIBLOOT_LOG_LEVEL_TRACE) {
return LogLevel::trace;
} else if (level == LIBLOOT_LOG_LEVEL_DEBUG) {
return LogLevel::debug;
} else if (level == LIBLOOT_LOG_LEVEL_INFO) {
return LogLevel::info;
} else if (level == LIBLOOT_LOG_LEVEL_WARNING) {
return LogLevel::warning;
} else if (level == LIBLOOT_LOG_LEVEL_ERROR) {
return LogLevel::error;
} else {
return LogLevel::fatal;
}
}
loot::rust::LogLevel convert(LogLevel level) {
switch (level) {
case LogLevel::trace:
return loot::rust::LogLevel::Trace;
case LogLevel::debug:
return loot::rust::LogLevel::Debug;
case LogLevel::info:
return loot::rust::LogLevel::Info;
case LogLevel::warning:
return loot::rust::LogLevel::Warning;
case LogLevel::error:
return loot::rust::LogLevel::Error;
case LogLevel::fatal:
return loot::rust::LogLevel::Fatal;
default:
return loot::rust::LogLevel::Trace;
}
}
void logging_callback(uint8_t level, const char* message, void* context) {
auto& callback = *static_cast<Callback*>(context);
callback(convert(level), message);
}
}
namespace loot {
LOOT_API void SetLoggingCallback(Callback callback) {
STORED_CALLBACK = callback;
libloot_set_logging_callback(logging_callback, &STORED_CALLBACK);
}
LOOT_API void SetLogLevel(LogLevel level) {
loot::rust::set_log_level(convert(level));
}
LOOT_API bool IsCompatible(const unsigned int versionMajor,
const unsigned int versionMinor,
const unsigned int versionPatch) {
return loot::rust::is_compatible(versionMajor, versionMinor, versionPatch);
}
LOOT_API std::unique_ptr<GameInterface> CreateGameHandle(
const GameType game,
const std::filesystem::path& gamePath,
const std::filesystem::path& gameLocalPath) {
return std::make_unique<Game>(game, gamePath, gameLocalPath);
}
LOOT_API std::string GetLiblootVersion() {
return std::string(loot::rust::libloot_version());
}
LOOT_API std::string GetLiblootRevision() {
return std::string(loot::rust::libloot_revision());
}
}
+271
View File
@@ -0,0 +1,271 @@
#include "api/convert.h"
#include "api/exception.h"
namespace loot {
// To public types
/////////////////////
std::string convert(const ::rust::String& string) {
return std::string(string);
}
// Although there's an explicit conversion operator declared, it seems that
// building the CXX wrapper with MSVC doesn't set __cplusplus correctly as using
// the operator causes a linker error, so this just reimpls it as a function.
std::string_view convert(::rust::Str str) {
return std::string_view(str.data(), str.length());
}
loot::Group convert(const loot::rust::Group& group) {
return loot::Group(convert(group.name()),
convert<std::string>(group.after_groups()),
convert(group.description()));
}
loot::File convert(const loot::rust::File& file) {
return loot::File(convert(file.filename().as_str()),
convert(file.display_name()),
convert(file.condition()),
convert<loot::MessageContent>(file.detail()),
convert(file.constraint()));
}
loot::MessageType convert(loot::rust::MessageType messageType) {
switch (messageType) {
case loot::rust::MessageType::Say:
return loot::MessageType::say;
case loot::rust::MessageType::Warn:
return loot::MessageType::warn;
case loot::rust::MessageType::Error:
return loot::MessageType::error;
default:
throw std::logic_error("Unsupported MessageType value");
}
}
loot::MessageContent convert(const loot::rust::MessageContent& content) {
return loot::MessageContent(convert(content.text()),
convert(content.language()));
}
loot::Message convert(const loot::rust::Message& message) {
return loot::Message(convert(message.message_type()),
convert<loot::MessageContent>(message.content()),
convert(message.condition()));
}
loot::Tag convert(const loot::rust::Tag& tag) {
return loot::Tag(
convert(tag.name()), tag.is_addition(), convert(tag.condition()));
}
loot::PluginCleaningData convert(const loot::rust::PluginCleaningData& data) {
return loot::PluginCleaningData(data.crc(),
convert(data.cleaning_utility()),
convert<loot::MessageContent>(data.detail()),
data.itm_count(),
data.deleted_reference_count(),
data.deleted_navmesh_count());
}
loot::Location convert(const loot::rust::Location& location) {
return loot::Location(convert(location.url()), convert(location.name()));
}
loot::PluginMetadata convert(const loot::rust::PluginMetadata& metadata) {
auto output = loot::PluginMetadata(convert(metadata.name()));
if (!metadata.group().empty()) {
output.SetGroup(convert(metadata.group()));
}
output.SetLoadAfterFiles(convert<loot::File>(metadata.load_after_files()));
output.SetRequirements(convert<loot::File>(metadata.requirements()));
output.SetIncompatibilities(
convert<loot::File>(metadata.incompatibilities()));
output.SetMessages(convert<loot::Message>(metadata.messages()));
output.SetTags(convert<loot::Tag>(metadata.tags()));
output.SetDirtyInfo(convert<loot::PluginCleaningData>(metadata.dirty_info()));
output.SetCleanInfo(convert<loot::PluginCleaningData>(metadata.clean_info()));
output.SetLocations(convert<loot::Location>(metadata.locations()));
return output;
}
std::optional<loot::EdgeType> convert(uint8_t edgeType) {
switch (edgeType) {
case static_cast<uint8_t>(loot::rust::EdgeType::Hardcoded):
return loot::EdgeType::hardcoded;
case static_cast<uint8_t>(loot::rust::EdgeType::MasterFlag):
return loot::EdgeType::masterFlag;
case static_cast<uint8_t>(loot::rust::EdgeType::Master):
return loot::EdgeType::master;
case static_cast<uint8_t>(loot::rust::EdgeType::MasterlistRequirement):
return loot::EdgeType::masterlistRequirement;
case static_cast<uint8_t>(loot::rust::EdgeType::UserRequirement):
return loot::EdgeType::userRequirement;
case static_cast<uint8_t>(loot::rust::EdgeType::MasterlistLoadAfter):
return loot::EdgeType::masterlistLoadAfter;
case static_cast<uint8_t>(loot::rust::EdgeType::UserLoadAfter):
return loot::EdgeType::userLoadAfter;
case static_cast<uint8_t>(loot::rust::EdgeType::MasterlistGroup):
return loot::EdgeType::masterlistGroup;
case static_cast<uint8_t>(loot::rust::EdgeType::UserGroup):
return loot::EdgeType::userGroup;
case static_cast<uint8_t>(loot::rust::EdgeType::RecordOverlap):
return loot::EdgeType::recordOverlap;
case static_cast<uint8_t>(loot::rust::EdgeType::AssetOverlap):
return loot::EdgeType::assetOverlap;
case static_cast<uint8_t>(loot::rust::EdgeType::TieBreak):
return loot::EdgeType::tieBreak;
case static_cast<uint8_t>(loot::rust::EdgeType::BlueprintMaster):
return loot::EdgeType::blueprintMaster;
default:
return std::nullopt;
}
}
loot::Vertex convert(const loot::rust::Vertex& vertex) {
try {
const auto outEdgeType = convert(vertex.out_edge_type());
if (outEdgeType.has_value()) {
return loot::Vertex(convert(vertex.name()), outEdgeType.value());
} else {
return loot::Vertex(convert(vertex.name()));
}
} catch (const ::rust::Error& e) {
std::rethrow_exception(mapError(e));
}
}
// From public types
///////////////////////
::rust::Str convert(std::string_view view) {
return ::rust::Str(view.data(), view.length());
}
::rust::Box<loot::rust::Group> convert(const loot::Group& group) {
return loot::rust::new_group(
group.GetName(), group.GetDescription(), convert(group.GetAfterGroups()));
}
::rust::Box<loot::rust::File> convert(const loot::File& file) {
try {
return loot::rust::new_file(
std::string(file.GetName()),
file.GetDisplayName(),
file.GetCondition(),
::rust::Slice(convert<loot::rust::MessageContent>(file.GetDetail())),
file.GetConstraint());
} catch (const ::rust::Error& e) {
std::rethrow_exception(mapError(e));
}
}
loot::rust::MessageType convert(loot::MessageType messageType) {
switch (messageType) {
case loot::MessageType::say:
return loot::rust::MessageType::Say;
case loot::MessageType::warn:
return loot::rust::MessageType::Warn;
case loot::MessageType::error:
return loot::rust::MessageType::Error;
default:
throw std::logic_error("Unsupported MessageType value");
}
}
::rust::Box<loot::rust::MessageContent> convert(
const loot::MessageContent& content) {
return loot::rust::new_message_content(content.GetText(),
content.GetLanguage());
}
::rust::Box<loot::rust::Message> convert(const loot::Message& message) {
try {
return loot::rust::multilingual_message(
convert(message.GetType()),
::rust::Slice(
convert<loot::rust::MessageContent>(message.GetContent())),
message.GetCondition());
} catch (const ::rust::Error& e) {
std::rethrow_exception(mapError(e));
}
}
::rust::Box<loot::rust::Tag> convert(const loot::Tag& tag) {
try {
const auto suggestion = tag.IsAddition()
? loot::rust::TagSuggestion::Addition
: loot::rust::TagSuggestion::Removal;
return loot::rust::new_tag(tag.GetName(), suggestion, tag.GetCondition());
} catch (const ::rust::Error& e) {
std::rethrow_exception(mapError(e));
}
}
::rust::Box<loot::rust::PluginCleaningData> convert(
const loot::PluginCleaningData& data) {
try {
return loot::rust::new_plugin_cleaning_data(
data.GetCRC(),
data.GetCleaningUtility(),
::rust::Slice(convert<loot::rust::MessageContent>(data.GetDetail())),
data.GetITMCount(),
data.GetDeletedReferenceCount(),
data.GetDeletedNavmeshCount());
} catch (const ::rust::Error& e) {
std::rethrow_exception(mapError(e));
}
}
::rust::Box<loot::rust::Location> convert(const loot::Location& location) {
return loot::rust::new_location(location.GetURL(), location.GetName());
}
::rust::Box<loot::rust::PluginMetadata> convert(
const loot::PluginMetadata& metadata) {
try {
auto output = loot::rust::new_plugin_metadata(metadata.GetName());
if (metadata.GetGroup().has_value()) {
output->set_group(metadata.GetGroup().value());
}
output->set_load_after_files(
::rust::Slice(convert<loot::rust::File>(metadata.GetLoadAfterFiles())));
output->set_requirements(
::rust::Slice(convert<loot::rust::File>(metadata.GetRequirements())));
output->set_incompatibilities(::rust::Slice(
convert<loot::rust::File>(metadata.GetIncompatibilities())));
output->set_messages(
::rust::Slice(convert<loot::rust::Message>(metadata.GetMessages())));
output->set_tags(
::rust::Slice(convert<loot::rust::Tag>(metadata.GetTags())));
output->set_dirty_info(::rust::Slice(
convert<loot::rust::PluginCleaningData>(metadata.GetDirtyInfo())));
output->set_clean_info(::rust::Slice(
convert<loot::rust::PluginCleaningData>(metadata.GetCleanInfo())));
output->set_locations(
::rust::Slice(convert<loot::rust::Location>(metadata.GetLocations())));
return output;
} catch (const ::rust::Error& e) {
std::rethrow_exception(mapError(e));
}
}
// Between containers
////////////////////////
::rust::Vec<::rust::String> convert(const std::vector<std::string>& vector) {
::rust::Vec<::rust::String> strings;
for (const auto& str : vector) {
strings.push_back(str);
}
return strings;
}
}
+92
View File
@@ -0,0 +1,92 @@
#ifndef LOOT_API_CONVERT
#define LOOT_API_CONVERT
#include "libloot-cpp/src/lib.rs.h"
#include "loot/metadata/group.h"
#include "loot/metadata/plugin_metadata.h"
#include "loot/vertex.h"
namespace loot {
// To public types
/////////////////////
std::string convert(const ::rust::String& string);
std::string_view convert(::rust::Str string);
loot::Group convert(const loot::rust::Group& group);
loot::File convert(const loot::rust::File& file);
loot::MessageType convert(loot::rust::MessageType messageType);
loot::MessageContent convert(const loot::rust::MessageContent& content);
loot::Message convert(const loot::rust::Message& message);
loot::Tag convert(const loot::rust::Tag& tag);
loot::PluginCleaningData convert(const loot::rust::PluginCleaningData& data);
loot::Location convert(const loot::rust::Location& location);
loot::PluginMetadata convert(const loot::rust::PluginMetadata& metadata);
std::optional<loot::EdgeType> convert(uint8_t edgeType);
loot::Vertex convert(const loot::rust::Vertex& vertex);
// From public types
///////////////////////
::rust::Str convert(std::string_view view);
::rust::Box<loot::rust::Group> convert(const loot::Group& group);
::rust::Box<loot::rust::File> convert(const loot::File& file);
loot::rust::MessageType convert(loot::MessageType messageType);
::rust::Box<loot::rust::MessageContent> convert(
const loot::MessageContent& content);
::rust::Box<loot::rust::Message> convert(const loot::Message& message);
::rust::Box<loot::rust::Tag> convert(const loot::Tag& tag);
::rust::Box<loot::rust::PluginCleaningData> convert(
const loot::PluginCleaningData& data);
::rust::Box<loot::rust::Location> convert(const loot::Location& location);
::rust::Box<loot::rust::PluginMetadata> convert(
const loot::PluginMetadata& metadata);
// Between containers
////////////////////////
::rust::Vec<::rust::String> convert(const std::vector<std::string>& vector);
template<typename T, typename U>
std::vector<T> convert(const ::rust::Slice<const U>& slice) {
std::vector<T> output;
for (const auto& element : slice) {
output.push_back(convert(element));
}
return output;
}
template<typename T, typename U>
std::vector<T> convert(const ::rust::Vec<U>& vec) {
return convert<T, U>(::rust::Slice(vec));
}
template<typename T, typename U>
const std::vector<::rust::Box<T>> convert(const std::vector<U>& vec) {
return convert<::rust::Box<T>, U>(::rust::Slice(vec));
}
}
#endif
+173
View File
@@ -0,0 +1,173 @@
#include "api/database.h"
#include "api/convert.h"
#include "api/exception.h"
namespace loot {
Database::Database(::rust::Box<loot::rust::Database>&& database) :
database_(std::move(database)) {}
void Database::LoadMasterlist(const std::filesystem::path& masterlistPath) {
try {
database_->load_masterlist(masterlistPath.u8string());
} catch (const ::rust::Error& e) {
std::rethrow_exception(mapError(e));
}
}
void Database::LoadMasterlistWithPrelude(
const std::filesystem::path& masterlistPath,
const std::filesystem::path& masterlistPreludePath) {
try {
database_->load_masterlist_with_prelude(masterlistPath.u8string(),
masterlistPreludePath.u8string());
} catch (const ::rust::Error& e) {
std::rethrow_exception(mapError(e));
}
}
void Database::LoadUserlist(const std::filesystem::path& userlistPath) {
try {
database_->load_userlist(userlistPath.u8string());
} catch (const ::rust::Error& e) {
std::rethrow_exception(mapError(e));
}
}
void Database::WriteUserMetadata(const std::filesystem::path& outputFile,
const bool overwrite) const {
try {
database_->write_user_metadata(outputFile.u8string(), overwrite);
} catch (const ::rust::Error& e) {
std::rethrow_exception(mapError(e));
}
}
bool Database::Evaluate(const std::string& condition) const {
try {
return database_->evaluate(condition);
} catch (const ::rust::Error& e) {
std::rethrow_exception(mapError(e));
}
}
std::vector<std::string> Database::GetKnownBashTags() const {
try {
return convert<std::string>(database_->known_bash_tags());
} catch (const ::rust::Error& e) {
std::rethrow_exception(mapError(e));
}
}
std::vector<Message> Database::GetGeneralMessages(
bool evaluateConditions) const {
try {
return convert<Message>(database_->general_messages(evaluateConditions));
} catch (const ::rust::Error& e) {
std::rethrow_exception(mapError(e));
}
}
std::vector<Group> Database::GetGroups(bool includeUserMetadata) const {
try {
return convert<Group>(database_->groups(includeUserMetadata));
} catch (const ::rust::Error& e) {
std::rethrow_exception(mapError(e));
}
}
std::vector<Group> Database::GetUserGroups() const {
try {
return convert<Group>(database_->user_groups());
} catch (const ::rust::Error& e) {
std::rethrow_exception(mapError(e));
}
}
void Database::SetUserGroups(const std::vector<Group>& groups) {
try {
database_->set_user_groups(
::rust::Slice(convert<loot::rust::Group>(groups)));
} catch (const ::rust::Error& e) {
std::rethrow_exception(mapError(e));
}
}
std::vector<Vertex> Database::GetGroupsPath(
std::string_view fromGroupName,
std::string_view toGroupName) const {
try {
return convert<Vertex>(
database_->groups_path(convert(fromGroupName), convert(toGroupName)));
} catch (const ::rust::Error& e) {
std::rethrow_exception(mapError(e));
}
}
std::optional<PluginMetadata> Database::GetPluginMetadata(
std::string_view plugin,
bool includeUserMetadata,
bool evaluateConditions) const {
try {
const auto metadata = database_->plugin_metadata(
convert(plugin), includeUserMetadata, evaluateConditions);
if (metadata->is_some()) {
return convert(metadata->as_ref());
} else {
return std::nullopt;
}
} catch (const ::rust::Error& e) {
std::rethrow_exception(mapError(e));
}
}
std::optional<PluginMetadata> Database::GetPluginUserMetadata(
std::string_view plugin,
bool evaluateConditions) const {
try {
const auto metadata =
database_->plugin_user_metadata(convert(plugin), evaluateConditions);
if (metadata->is_some()) {
return convert(metadata->as_ref());
} else {
return std::nullopt;
}
} catch (const ::rust::Error& e) {
std::rethrow_exception(mapError(e));
}
}
void Database::SetPluginUserMetadata(const PluginMetadata& pluginMetadata) {
try {
database_->set_plugin_user_metadata(convert(pluginMetadata));
} catch (const ::rust::Error& e) {
std::rethrow_exception(mapError(e));
}
}
void Database::DiscardPluginUserMetadata(std::string_view plugin) {
try {
database_->discard_plugin_user_metadata(convert(plugin));
} catch (const ::rust::Error& e) {
std::rethrow_exception(mapError(e));
}
}
void Database::DiscardAllUserMetadata() {
try {
database_->discard_all_user_metadata();
} catch (const ::rust::Error& e) {
std::rethrow_exception(mapError(e));
}
}
void Database::WriteMinimalList(const std::filesystem::path& outputFile,
const bool overwrite) const {
try {
database_->write_minimal_list(outputFile.u8string(), overwrite);
} catch (const ::rust::Error& e) {
std::rethrow_exception(mapError(e));
}
}
}
+61
View File
@@ -0,0 +1,61 @@
#ifndef LOOT_API_DATABASE
#define LOOT_API_DATABASE
#include "libloot-cpp/src/lib.rs.h"
#include "loot/database_interface.h"
#include "rust/cxx.h"
namespace loot {
class Database final : public DatabaseInterface {
public:
explicit Database(::rust::Box<loot::rust::Database>&& database);
void LoadMasterlist(const std::filesystem::path& masterlist_path) override;
void LoadMasterlistWithPrelude(
const std::filesystem::path& masterlist_path,
const std::filesystem::path& masterlist_prelude_path) override;
void LoadUserlist(const std::filesystem::path& userlist_path) override;
void WriteUserMetadata(const std::filesystem::path& outputFile,
const bool overwrite) const override;
void WriteMinimalList(const std::filesystem::path& outputFile,
const bool overwrite) const override;
bool Evaluate(const std::string& condition) const override;
std::vector<std::string> GetKnownBashTags() const override;
std::vector<Message> GetGeneralMessages(
bool evaluateConditions = false) const override;
std::vector<Group> GetGroups(bool includeUserMetadata = true) const override;
std::vector<Group> GetUserGroups() const override;
void SetUserGroups(const std::vector<Group>& groups) override;
std::vector<Vertex> GetGroupsPath(
std::string_view fromGroupName,
std::string_view toGroupName) const override;
std::optional<PluginMetadata> GetPluginMetadata(
std::string_view plugin,
bool includeUserMetadata = true,
bool evaluateConditions = false) const override;
std::optional<PluginMetadata> GetPluginUserMetadata(
std::string_view plugin,
bool evaluateConditions = false) const override;
void SetPluginUserMetadata(const PluginMetadata& pluginMetadata) override;
void DiscardPluginUserMetadata(std::string_view plugin) override;
void DiscardAllUserMetadata() override;
private:
::rust::Box<loot::rust::Database> database_;
};
}
#endif
+80
View File
@@ -0,0 +1,80 @@
/* 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/>.
*/
#include "loot/exception/error_categories.h"
#include <string>
namespace loot {
namespace detail {
class esplugin_category : public std::error_category {
const char* name() const noexcept override { return "esplugin"; }
std::string message(int) const override { return "esplugin error"; }
bool equivalent(const std::error_code& code, int) const noexcept override {
return code.category().name() == name();
}
};
class libloadorder_category : public std::error_category {
const char* name() const noexcept override { return "libloadorder"; }
std::string message(int) const override { return "Libloadorder error"; }
bool equivalent(const std::error_code& code, int) const noexcept override {
return code.category().name() == name();
}
};
class loot_condition_interpreter_category : public std::error_category {
const char* name() const noexcept override {
return "loot condition interpreter";
}
std::string message(int) const override {
return "loot condition interpreter error";
}
bool equivalent(const std::error_code& code, int) const noexcept override {
return code.category().name() == name();
}
};
}
LOOT_API const std::error_category& esplugin_category() {
static detail::esplugin_category instance;
return instance;
}
LOOT_API const std::error_category& libloadorder_category() {
static detail::libloadorder_category instance;
return instance;
}
LOOT_API const std::error_category& loot_condition_interpreter_category() {
static detail::loot_condition_interpreter_category instance;
return instance;
}
}
+177
View File
@@ -0,0 +1,177 @@
#include "api/exception.h"
#include <charconv>
#include "loot/exception/condition_syntax_error.h"
#include "loot/exception/cyclic_interaction_error.h"
#include "loot/exception/error_categories.h"
#include "loot/exception/file_access_error.h"
#include "loot/exception/undefined_group_error.h"
#include "loot/vertex.h"
namespace {
using std::string_view_literals::operator""sv;
using loot::EdgeType;
using loot::Vertex;
constexpr std::string_view CYCLIC_ERROR_PREFIX = "CyclicInteractionError: "sv;
constexpr std::string_view UNDEFINED_GROUP_ERROR_PREFIX =
"UndefinedGroupError: "sv;
constexpr std::string_view ESPLUGIN_ERROR_PREFIX = "EspluginError: "sv;
constexpr std::string_view LIBLOADORDER_ERROR_PREFIX = "LibloadorderError: "sv;
constexpr std::string_view LCI_ERROR_PREFIX = "LciError: "sv;
constexpr std::string_view FILE_ACCESS_ERROR_PREFIX = "FileAccessError: "sv;
constexpr std::string_view INVALID_ARGUMENT_PREFIX = "InvalidArgument: "sv;
bool startsWith(std::string_view str, std::string_view prefix) {
if (str.size() < prefix.size()) {
return false;
}
return str.substr(0, prefix.size()) == prefix;
}
std::string replace(std::string_view str,
std::string_view from,
std::string_view to) {
std::string out;
out.reserve(str.size());
size_t i = 0;
while (i < str.size()) {
if (i + from.size() <= str.size() && str.substr(i, from.size()) == from) {
out.append(to);
i += from.size();
} else {
out.push_back(str[i]);
i += 1;
}
}
return out;
}
EdgeType toEdgeType(std::string_view edgeTypeDisplay) {
if (edgeTypeDisplay == "Hardcoded") {
return EdgeType::hardcoded;
} else if (edgeTypeDisplay == "Master Flag") {
return EdgeType::masterFlag;
} else if (edgeTypeDisplay == "Master") {
return EdgeType::master;
} else if (edgeTypeDisplay == "Masterlist Requirement") {
return EdgeType::masterlistRequirement;
} else if (edgeTypeDisplay == "User Requirement") {
return EdgeType::userRequirement;
} else if (edgeTypeDisplay == "Masterlist Load After") {
return EdgeType::masterlistLoadAfter;
} else if (edgeTypeDisplay == "User Load After") {
return EdgeType::userLoadAfter;
} else if (edgeTypeDisplay == "Masterlist Group") {
return EdgeType::masterlistGroup;
} else if (edgeTypeDisplay == "User Group") {
return EdgeType::userGroup;
} else if (edgeTypeDisplay == "Record Overlap") {
return EdgeType::recordOverlap;
} else if (edgeTypeDisplay == "Asset Overlap") {
return EdgeType::assetOverlap;
} else if (edgeTypeDisplay == "Tie Break") {
return EdgeType::tieBreak;
} else if (edgeTypeDisplay == "Blueprint Master") {
return EdgeType::blueprintMaster;
} else {
std::string what("Unrecognised edge type: ");
what += edgeTypeDisplay;
throw std::logic_error(what);
}
}
std::vector<Vertex> parseCyclicError(std::string_view what) {
const auto suffix = what.substr(0, CYCLIC_ERROR_PREFIX.size());
std::vector<Vertex> vertices;
size_t pos = 0;
while (pos < suffix.size()) {
const auto sepPos = suffix.find("--", pos);
const auto escapedName = suffix.substr(pos, sepPos);
const auto name = replace(replace(escapedName, "\\-", "-"), "\\\\", "\\");
if (sepPos != std::string::npos) {
const auto secondSepPos = suffix.find("--", sepPos + 2);
const auto escapedEdgeName =
suffix.substr(sepPos + 2, secondSepPos - (sepPos + 2));
vertices.push_back(Vertex(name, toEdgeType(escapedEdgeName)));
pos = secondSepPos + 2;
} else {
vertices.push_back(Vertex(name));
pos = suffix.size();
}
}
return vertices;
}
std::string getErrorSuffix(std::string_view what) {
const auto sepPos = what.find(": ");
return std::string(what.substr(sepPos + 2));
}
std::pair<int, std::string> parseSystemError(std::string_view whatSuffix) {
const auto sepPos = whatSuffix.find(": ");
int code;
const auto result =
std::from_chars(whatSuffix.data(), whatSuffix.data() + sepPos, code);
if (result.ec != std::errc{}) {
std::string err = "Could not parse error code from string: ";
err += whatSuffix;
throw std::runtime_error(err);
}
return std::make_pair(code, std::string(whatSuffix.substr(sepPos + 2)));
}
}
namespace loot {
std::exception_ptr mapError(const ::rust::Error& error) {
if (startsWith(error.what(), CYCLIC_ERROR_PREFIX)) {
return std::make_exception_ptr(
CyclicInteractionError(parseCyclicError(error.what())));
} else if (startsWith(error.what(), UNDEFINED_GROUP_ERROR_PREFIX)) {
return std::make_exception_ptr(
UndefinedGroupError(getErrorSuffix(error.what())));
} else if (startsWith(error.what(), ESPLUGIN_ERROR_PREFIX)) {
const auto [code, details] = parseSystemError(
std::string_view(error.what()).substr(ESPLUGIN_ERROR_PREFIX.size()));
return std::make_exception_ptr(
std::system_error(code, esplugin_category(), details));
} else if (startsWith(error.what(), LIBLOADORDER_ERROR_PREFIX)) {
const auto [code, details] =
parseSystemError(std::string_view(error.what())
.substr(LIBLOADORDER_ERROR_PREFIX.size()));
return std::make_exception_ptr(
std::system_error(code, libloadorder_category(), details));
} else if (startsWith(error.what(), LCI_ERROR_PREFIX)) {
const auto [code, details] = parseSystemError(
std::string_view(error.what()).substr(LCI_ERROR_PREFIX.size()));
return std::make_exception_ptr(ConditionSyntaxError(
code, loot_condition_interpreter_category(), details));
} else if (startsWith(error.what(), FILE_ACCESS_ERROR_PREFIX)) {
return std::make_exception_ptr(
FileAccessError(getErrorSuffix(error.what())));
} else if (startsWith(error.what(), FILE_ACCESS_ERROR_PREFIX)) {
return std::make_exception_ptr(
FileAccessError(getErrorSuffix(error.what())));
} else if (startsWith(error.what(), INVALID_ARGUMENT_PREFIX)) {
return std::make_exception_ptr(
std::invalid_argument(getErrorSuffix(error.what())));
} else {
return std::make_exception_ptr(std::runtime_error(error.what()));
}
}
}
+10
View File
@@ -0,0 +1,10 @@
#ifndef LOOT_API_EXCEPTION
#define LOOT_API_EXCEPTION
#include "rust/cxx.h"
namespace loot {
std::exception_ptr mapError(const ::rust::Error& error);
}
#endif
+248
View File
@@ -0,0 +1,248 @@
#include "api/game.h"
#include "api/convert.h"
#include "api/exception.h"
namespace {
loot::GameType convert(loot::rust::GameType gameType) {
switch (gameType) {
case loot::rust::GameType::Morrowind:
return loot::GameType::tes3;
case loot::rust::GameType::Oblivion:
return loot::GameType::tes4;
case loot::rust::GameType::Skyrim:
return loot::GameType::tes5;
case loot::rust::GameType::SkyrimSE:
return loot::GameType::tes5se;
case loot::rust::GameType::SkyrimVR:
return loot::GameType::tes5vr;
case loot::rust::GameType::Fallout3:
return loot::GameType::fo3;
case loot::rust::GameType::FalloutNV:
return loot::GameType::fonv;
case loot::rust::GameType::Fallout4:
return loot::GameType::fo4;
case loot::rust::GameType::Fallout4VR:
return loot::GameType::fo4vr;
case loot::rust::GameType::Starfield:
return loot::GameType::starfield;
case loot::rust::GameType::OpenMW:
return loot::GameType::openmw;
default:
throw std::logic_error("Unsupported GameType value");
}
}
loot::rust::GameType convert(loot::GameType gameType) {
switch (gameType) {
case loot::GameType::tes3:
return loot::rust::GameType::Morrowind;
case loot::GameType::tes4:
return loot::rust::GameType::Oblivion;
case loot::GameType::tes5:
return loot::rust::GameType::Skyrim;
case loot::GameType::tes5se:
return loot::rust::GameType::SkyrimSE;
case loot::GameType::tes5vr:
return loot::rust::GameType::SkyrimVR;
case loot::GameType::fo3:
return loot::rust::GameType::Fallout3;
case loot::GameType::fonv:
return loot::rust::GameType::FalloutNV;
case loot::GameType::fo4:
return loot::rust::GameType::Fallout4;
case loot::GameType::fo4vr:
return loot::rust::GameType::Fallout4VR;
case loot::GameType::starfield:
return loot::rust::GameType::Starfield;
case loot::GameType::openmw:
return loot::rust::GameType::OpenMW;
default:
throw std::logic_error("Unsupported GameType value");
}
}
rust::Box<loot::rust::Game> constructGame(
const loot::GameType gameType,
const std::filesystem::path& gamePath,
const std::filesystem::path& localDataPath) {
try {
if (localDataPath.empty()) {
return loot::rust::new_game(convert(gameType), gamePath.u8string());
} else {
return loot::rust::new_game_with_local_path(
convert(gameType), gamePath.u8string(), localDataPath.u8string());
}
} catch (const ::rust::Error& e) {
std::rethrow_exception(loot::mapError(e));
}
}
std::vector<::rust::Str> as_str_refs(const std::vector<std::string>& vector) {
std::vector<::rust::Str> strings;
for (const auto& str : vector) {
strings.push_back(str);
}
return strings;
}
}
namespace loot {
Game::Game(const GameType gameType,
const std::filesystem::path& gamePath,
const std::filesystem::path& localDataPath) :
game_(constructGame(gameType, gamePath, localDataPath)),
database_(game_->database()) {}
GameType Game::GetType() const {
try {
return ::convert(game_->game_type());
} catch (const ::rust::Error& e) {
std::rethrow_exception(mapError(e));
}
}
const DatabaseInterface& Game::GetDatabase() const { return database_; }
DatabaseInterface& Game::GetDatabase() { return database_; }
std::vector<std::filesystem::path> Game::GetAdditionalDataPaths() const {
try {
std::vector<std::filesystem::path> paths;
for (const auto& path_str : game_->additional_data_paths()) {
paths.push_back(
std::filesystem::u8path(path_str.begin(), path_str.end()));
}
return paths;
} catch (const ::rust::Error& e) {
std::rethrow_exception(mapError(e));
}
}
void Game::SetAdditionalDataPaths(
const std::vector<std::filesystem::path>& additionalDataPaths) {
std::vector<::rust::String> path_strings;
std::vector<::rust::Str> path_strs;
for (const auto& path : additionalDataPaths) {
path_strings.push_back(path.u8string());
path_strs.push_back(path_strings.back());
}
try {
game_->set_additional_data_paths(
::rust::Slice<const ::rust::Str>(path_strs));
} catch (const ::rust::Error& e) {
std::rethrow_exception(mapError(e));
}
}
bool Game::IsValidPlugin(const std::filesystem::path& pluginPath) const {
return game_->is_valid_plugin(pluginPath.u8string());
}
void Game::LoadPlugins(const std::vector<std::filesystem::path>& pluginPaths,
bool loadHeadersOnly) {
std::vector<::rust::String> path_strings;
std::vector<::rust::Str> path_strs;
for (const auto& path : pluginPaths) {
path_strings.push_back(path.u8string());
path_strs.push_back(path_strings.back());
}
try {
if (loadHeadersOnly) {
game_->load_plugin_headers(::rust::Slice<const ::rust::Str>(path_strs));
} else {
game_->load_plugins(::rust::Slice<const ::rust::Str>(path_strs));
}
} catch (const ::rust::Error& e) {
std::rethrow_exception(mapError(e));
}
}
void Game::ClearLoadedPlugins() { game_->clear_loaded_plugins(); }
std::shared_ptr<const PluginInterface> Game::GetPlugin(
std::string_view pluginName) const {
const auto pluginOpt = game_->plugin(convert(pluginName));
if (!pluginOpt->is_some()) {
return nullptr;
}
try {
return std::make_shared<Plugin>(
std::move(pluginOpt->as_ref().boxed_clone()));
} catch (const ::rust::Error& e) {
std::rethrow_exception(mapError(e));
}
}
std::vector<std::shared_ptr<const PluginInterface>> Game::GetLoadedPlugins()
const {
std::vector<std::shared_ptr<const PluginInterface>> plugins;
for (const auto& pluginRef : game_->loaded_plugins()) {
plugins.push_back(
std::make_shared<Plugin>(std::move(pluginRef.boxed_clone())));
}
return plugins;
}
std::vector<std::string> Game::SortPlugins(
const std::vector<std::string>& pluginFilenames) {
const auto strs = as_str_refs(pluginFilenames);
try {
const auto results = game_->sort_plugins(::rust::Slice(strs));
return convert<std::string>(results);
} catch (const ::rust::Error& e) {
std::rethrow_exception(mapError(e));
}
}
void Game::LoadCurrentLoadOrderState() {
try {
game_->load_current_load_order_state();
} catch (const ::rust::Error& e) {
std::rethrow_exception(mapError(e));
}
}
bool Game::IsLoadOrderAmbiguous() const {
try {
return game_->is_load_order_ambiguous();
} catch (const ::rust::Error& e) {
std::rethrow_exception(mapError(e));
}
}
std::filesystem::path Game::GetActivePluginsFilePath() const {
try {
const auto path_string = game_->active_plugins_file_path();
return std::filesystem::u8path(path_string.begin(), path_string.end());
} catch (const ::rust::Error& e) {
std::rethrow_exception(mapError(e));
}
}
bool Game::IsPluginActive(const std::string& pluginName) const {
return game_->is_plugin_active(pluginName);
}
std::vector<std::string> Game::GetLoadOrder() const {
return convert<std::string>(game_->load_order());
}
void Game::SetLoadOrder(const std::vector<std::string>& loadOrder) {
const auto strs = as_str_refs(loadOrder);
try {
game_->set_load_order(::rust::Slice(strs));
} catch (const ::rust::Error& e) {
std::rethrow_exception(mapError(e));
}
}
}
+67
View File
@@ -0,0 +1,67 @@
#ifndef LOOT_API_GAME
#define LOOT_API_GAME
#include <map>
#include "api/database.h"
#include "api/plugin.h"
#include "libloot-cpp/src/lib.rs.h"
#include "loot/game_interface.h"
#include "loot/metadata/filename.h"
#include "rust/cxx.h"
namespace loot {
class Game final : public GameInterface {
public:
explicit Game(const GameType gameType,
const std::filesystem::path& gamePath,
const std::filesystem::path& gameLocalDataPath = "");
// Game Interface Methods //
////////////////////////////
GameType GetType() const override;
std::vector<std::filesystem::path> GetAdditionalDataPaths() const;
void SetAdditionalDataPaths(
const std::vector<std::filesystem::path>& additionalDataPaths) override;
DatabaseInterface& GetDatabase() override;
const DatabaseInterface& GetDatabase() const override;
bool IsValidPlugin(const std::filesystem::path& pluginPath) const override;
void LoadPlugins(const std::vector<std::filesystem::path>& pluginPaths,
bool loadHeadersOnly) override;
void ClearLoadedPlugins() override;
std::shared_ptr<const PluginInterface> GetPlugin(
std::string_view pluginName) const override;
std::vector<std::shared_ptr<const PluginInterface>> GetLoadedPlugins()
const override;
std::vector<std::string> SortPlugins(
const std::vector<std::string>& pluginFilenames) override;
void LoadCurrentLoadOrderState() override;
bool IsLoadOrderAmbiguous() const override;
std::filesystem::path GetActivePluginsFilePath() const override;
bool IsPluginActive(const std::string& pluginName) const override;
std::vector<std::string> GetLoadOrder() const override;
void SetLoadOrder(const std::vector<std::string>& loadOrder) override;
private:
::rust::Box<loot::rust::Game> game_;
Database database_;
};
}
#endif
@@ -0,0 +1,34 @@
/* 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/>.
*/
#include "loot/metadata/conditional_metadata.h"
namespace loot {
ConditionalMetadata::ConditionalMetadata(std::string_view condition) :
condition_(condition) {}
bool ConditionalMetadata::IsConditional() const { return !condition_.empty(); }
std::string ConditionalMetadata::GetCondition() const { return condition_; }
}
+97
View File
@@ -0,0 +1,97 @@
/* 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/>.
*/
#include "loot/metadata/file.h"
namespace loot {
File::File(std::string_view name,
std::string_view display,
std::string_view condition,
const std::vector<MessageContent>& detail,
std::string_view constraint) :
ConditionalMetadata(condition),
name_(Filename(name)),
display_(display),
detail_(detail),
constraint_(constraint) {}
Filename File::GetName() const { return name_; }
std::string File::GetDisplayName() const { return display_; }
std::vector<MessageContent> File::GetDetail() const { return detail_; }
std::string File::GetConstraint() const { return constraint_; }
bool operator==(const File& lhs, const File& rhs) {
return lhs.GetDisplayName() == rhs.GetDisplayName() &&
lhs.GetCondition() == rhs.GetCondition() &&
lhs.GetConstraint() == rhs.GetConstraint() &&
lhs.GetName() == rhs.GetName() && lhs.GetDetail() == rhs.GetDetail();
}
bool operator!=(const File& lhs, const File& rhs) { return !(lhs == rhs); }
bool operator<(const File& lhs, const File& rhs) {
if (lhs.GetDisplayName() < rhs.GetDisplayName()) {
return true;
}
if (rhs.GetDisplayName() < lhs.GetDisplayName()) {
return false;
}
if (lhs.GetCondition() < rhs.GetCondition()) {
return true;
}
if (rhs.GetCondition() < lhs.GetCondition()) {
return false;
}
if (lhs.GetConstraint() < rhs.GetConstraint()) {
return true;
}
if (rhs.GetConstraint() < lhs.GetConstraint()) {
return false;
}
if (lhs.GetName() < rhs.GetName()) {
return true;
}
if (rhs.GetName() < lhs.GetName()) {
return false;
}
return lhs.GetDetail() < rhs.GetDetail();
}
bool operator>(const File& lhs, const File& rhs) { return rhs < lhs; }
bool operator<=(const File& lhs, const File& rhs) { return !(lhs > rhs); }
bool operator>=(const File& lhs, const File& rhs) { return !(lhs < rhs); }
}
+58
View File
@@ -0,0 +1,58 @@
/* 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/>.
*/
#include "loot/metadata/filename.h"
#include <stdexcept>
// #include "api/helpers/text.h"
#include "libloot-cpp/src/lib.rs.h"
namespace loot {
Filename::Filename(std::string_view filename) : filename_(filename) {}
Filename::operator std::string() const { return filename_; }
bool operator==(const Filename& lhs, const Filename& rhs) {
return loot::rust::compare_filenames(std::string(lhs), std::string(rhs)) == 0;
}
bool operator!=(const Filename& lhs, const Filename& rhs) {
return !(lhs == rhs);
}
bool operator<(const Filename& lhs, const Filename& rhs) {
return loot::rust::compare_filenames(std::string(lhs), std::string(rhs)) < 0;
}
bool operator>(const Filename& lhs, const Filename& rhs) { return rhs < lhs; }
bool operator<=(const Filename& lhs, const Filename& rhs) {
return !(lhs > rhs);
}
bool operator>=(const Filename& lhs, const Filename& rhs) {
return !(lhs < rhs);
}
}
+72
View File
@@ -0,0 +1,72 @@
/* LOOT
A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and
Fallout: New Vegas.
Copyright (C) 2018 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/>.
*/
#include "loot/metadata/group.h"
namespace loot {
Group::Group(std::string_view name,
const std::vector<std::string>& afterGroups,
std::string_view description) :
name_(name), description_(description), afterGroups_(afterGroups) {}
std::string Group::GetName() const { return name_; }
std::string Group::GetDescription() const { return description_; }
std::vector<std::string> Group::GetAfterGroups() const { return afterGroups_; }
bool operator==(const Group& lhs, const Group& rhs) {
return lhs.GetName() == rhs.GetName() &&
lhs.GetDescription() == rhs.GetDescription() &&
lhs.GetAfterGroups() == rhs.GetAfterGroups();
}
bool operator!=(const Group& lhs, const Group& rhs) { return !(lhs == rhs); }
bool operator<(const Group& lhs, const Group& rhs) {
if (lhs.GetName() < rhs.GetName()) {
return true;
}
if (rhs.GetName() < lhs.GetName()) {
return false;
}
if (lhs.GetDescription() < rhs.GetDescription()) {
return true;
}
if (rhs.GetDescription() < lhs.GetDescription()) {
return false;
}
return lhs.GetAfterGroups() < rhs.GetAfterGroups();
}
bool operator>(const Group& lhs, const Group& rhs) { return rhs < lhs; }
bool operator<=(const Group& lhs, const Group& rhs) { return !(lhs > rhs); }
bool operator>=(const Group& lhs, const Group& rhs) { return !(lhs < rhs); }
}
+64
View File
@@ -0,0 +1,64 @@
/* 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/>.
*/
#include "loot/metadata/location.h"
namespace loot {
Location::Location(std::string_view url, std::string_view name) :
url_(url), name_(name) {}
std::string Location::GetURL() const { return url_; }
std::string Location::GetName() const { return name_; }
bool operator==(const Location& lhs, const Location& rhs) {
return lhs.GetURL() == rhs.GetURL() && lhs.GetName() == rhs.GetName();
}
bool operator!=(const Location& lhs, const Location& rhs) {
return !(lhs == rhs);
}
bool operator<(const Location& lhs, const Location& rhs) {
if (lhs.GetURL() < rhs.GetURL()) {
return true;
}
if (rhs.GetURL() < lhs.GetURL()) {
return false;
}
return lhs.GetName() < rhs.GetName();
}
bool operator>(const Location& lhs, const Location& rhs) { return rhs < lhs; }
bool operator<=(const Location& lhs, const Location& rhs) {
return !(lhs > rhs);
}
bool operator>=(const Location& lhs, const Location& rhs) {
return !(lhs < rhs);
}
}
+93
View File
@@ -0,0 +1,93 @@
/* 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/>.
*/
#include "loot/metadata/message.h"
#include <stdexcept>
namespace loot {
Message::Message(const MessageType type,
std::string_view content,
std::string_view condition) :
ConditionalMetadata(condition),
type_(type),
content_({MessageContent(content)}) {}
Message::Message(const MessageType type,
const std::vector<MessageContent>& content,
std::string_view condition) :
ConditionalMetadata(condition), type_(type), content_(content) {
if (content.size() > 1) {
bool englishStringExists = false;
for (const auto& mc : content) {
if (mc.GetLanguage() == MessageContent::DEFAULT_LANGUAGE)
englishStringExists = true;
}
if (!englishStringExists)
throw std::invalid_argument(
"bad conversion: multilingual messages must contain an English "
"content string");
}
}
MessageType Message::GetType() const { return type_; }
std::vector<MessageContent> Message::GetContent() const { return content_; }
bool operator==(const Message& lhs, const Message& rhs) {
return lhs.GetType() == rhs.GetType() &&
lhs.GetCondition() == rhs.GetCondition() &&
lhs.GetContent() == rhs.GetContent();
}
bool operator!=(const Message& lhs, const Message& rhs) {
return !(lhs == rhs);
}
bool operator<(const Message& lhs, const Message& rhs) {
if (lhs.GetType() < rhs.GetType()) {
return true;
}
if (rhs.GetType() < lhs.GetType()) {
return false;
}
if (lhs.GetCondition() < rhs.GetCondition()) {
return true;
}
if (rhs.GetCondition() < lhs.GetCondition()) {
return false;
}
return lhs.GetContent() < rhs.GetContent();
}
bool operator>(const Message& lhs, const Message& rhs) { return rhs < lhs; }
bool operator<=(const Message& lhs, const Message& rhs) { return !(lhs > rhs); }
bool operator>=(const Message& lhs, const Message& rhs) { return !(lhs < rhs); }
}
+117
View File
@@ -0,0 +1,117 @@
/* 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/>.
*/
#include "loot/metadata/message_content.h"
namespace loot {
MessageContent::MessageContent(std::string_view text,
std::string_view language) :
text_(text), language_(language) {}
std::string MessageContent::GetText() const { return text_; }
std::string MessageContent::GetLanguage() const { return language_; }
bool operator==(const MessageContent& lhs, const MessageContent& rhs) {
return lhs.GetText() == rhs.GetText() &&
lhs.GetLanguage() == rhs.GetLanguage();
}
bool operator!=(const MessageContent& lhs, const MessageContent& rhs) {
return !(lhs == rhs);
}
bool operator<(const MessageContent& lhs, const MessageContent& rhs) {
if (lhs.GetText() < rhs.GetText()) {
return true;
}
if (rhs.GetText() < lhs.GetText()) {
return false;
}
return lhs.GetLanguage() < rhs.GetLanguage();
}
bool operator>(const MessageContent& lhs, const MessageContent& rhs) {
return rhs < lhs;
}
bool operator<=(const MessageContent& lhs, const MessageContent& rhs) {
return !(lhs > rhs);
}
bool operator>=(const MessageContent& lhs, const MessageContent& rhs) {
return !(lhs < rhs);
}
std::optional<MessageContent> SelectMessageContent(
const std::vector<MessageContent> content,
std::string_view language) {
if (content.empty())
return std::nullopt;
else if (content.size() == 1)
return content.at(0);
else {
auto languageCode = language.substr(0, language.find("_"));
const auto isCountryCodeGiven = languageCode.length() != language.length();
std::optional<MessageContent> matchedLanguage;
std::optional<MessageContent> english;
for (const auto& mc : content) {
auto contentLanguage = mc.GetLanguage();
if (contentLanguage == language) {
return mc;
} else if (!matchedLanguage.has_value()) {
if (isCountryCodeGiven && contentLanguage == languageCode) {
matchedLanguage = mc;
} else if (!isCountryCodeGiven) {
const auto underscorePos = contentLanguage.find("_");
if (underscorePos != std::string::npos) {
auto contentLanguageCode = contentLanguage.substr(0, underscorePos);
if (contentLanguageCode == language) {
matchedLanguage = mc;
}
}
}
if (contentLanguage == MessageContent::DEFAULT_LANGUAGE) {
english = mc;
}
}
}
if (matchedLanguage.has_value()) {
return matchedLanguage;
}
if (english.has_value()) {
return english;
}
return std::nullopt;
}
}
}
@@ -0,0 +1,129 @@
/* 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/>.
*/
#include "loot/metadata/plugin_cleaning_data.h"
namespace loot {
PluginCleaningData::PluginCleaningData(uint32_t crc, std::string_view utility) :
crc_(crc), utility_(utility) {}
PluginCleaningData::PluginCleaningData(
uint32_t crc,
std::string_view utility,
const std::vector<MessageContent>& detail,
unsigned int itm,
unsigned int ref,
unsigned int nav) :
crc_(crc),
itm_(itm),
ref_(ref),
nav_(nav),
utility_(utility),
detail_(detail) {}
uint32_t PluginCleaningData::GetCRC() const { return crc_; }
unsigned int PluginCleaningData::GetITMCount() const { return itm_; }
unsigned int PluginCleaningData::GetDeletedReferenceCount() const {
return ref_;
}
unsigned int PluginCleaningData::GetDeletedNavmeshCount() const { return nav_; }
std::string PluginCleaningData::GetCleaningUtility() const { return utility_; }
std::vector<MessageContent> PluginCleaningData::GetDetail() const {
return detail_;
}
bool operator==(const PluginCleaningData& lhs, const PluginCleaningData& rhs) {
return lhs.GetCRC() == rhs.GetCRC() &&
lhs.GetITMCount() == rhs.GetITMCount() &&
lhs.GetDeletedReferenceCount() == rhs.GetDeletedReferenceCount() &&
lhs.GetDeletedNavmeshCount() == rhs.GetDeletedNavmeshCount() &&
lhs.GetCleaningUtility() == rhs.GetCleaningUtility() &&
lhs.GetDetail() == rhs.GetDetail();
}
bool operator!=(const PluginCleaningData& lhs, const PluginCleaningData& rhs) {
return !(lhs == rhs);
}
bool operator<(const PluginCleaningData& lhs, const PluginCleaningData& rhs) {
if (lhs.GetCRC() < rhs.GetCRC()) {
return true;
}
if (rhs.GetCRC() < lhs.GetCRC()) {
return false;
}
if (lhs.GetCleaningUtility() < rhs.GetCleaningUtility()) {
return true;
}
if (rhs.GetCleaningUtility() < lhs.GetCleaningUtility()) {
return false;
}
if (lhs.GetITMCount() < rhs.GetITMCount()) {
return true;
}
if (rhs.GetITMCount() < lhs.GetITMCount()) {
return false;
}
if (lhs.GetDeletedReferenceCount() < rhs.GetDeletedReferenceCount()) {
return true;
}
if (rhs.GetDeletedReferenceCount() < lhs.GetDeletedReferenceCount()) {
return false;
}
if (lhs.GetDeletedNavmeshCount() < rhs.GetDeletedNavmeshCount()) {
return true;
}
if (rhs.GetDeletedNavmeshCount() < lhs.GetDeletedNavmeshCount()) {
return false;
}
return lhs.GetDetail() < rhs.GetDetail();
}
bool operator>(const PluginCleaningData& lhs, const PluginCleaningData& rhs) {
return rhs < lhs;
}
bool operator<=(const PluginCleaningData& lhs, const PluginCleaningData& rhs) {
return !(lhs > rhs);
}
bool operator>=(const PluginCleaningData& lhs, const PluginCleaningData& rhs) {
return !(lhs < rhs);
}
}
+212
View File
@@ -0,0 +1,212 @@
/* 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/>.
*/
#include "loot/metadata/plugin_metadata.h"
#include <cstring>
#include <regex>
#include <stdexcept>
#include "api/convert.h"
#include "libloot-cpp/src/lib.rs.h"
namespace {
// Append second to first, skipping any elements that are already present in
// first. Although this is O(U * M), both input vectors are expected to be
// small (with tens of elements being an unusually large number).
template<typename T>
std::vector<T> mergeVectors(std::vector<T> first,
const std::vector<T>& second) {
const auto initialSizeOfFirst = first.size();
for (const auto& element : second) {
const auto end = first.cbegin() + initialSizeOfFirst;
if (std::find(first.cbegin(), end, element) == end) {
first.push_back(element);
}
}
return first;
}
std::string TrimDotGhostExtension(std::string&& filename) {
using std::string_view_literals::operator""sv;
// If the name passed ends in '.ghost', that should be trimmed.
constexpr std::string_view GHOST_FILE_EXTENSION = ".ghost"sv;
if (filename.length() < GHOST_FILE_EXTENSION.length()) {
return filename;
}
auto view = std::string_view(filename);
view.remove_prefix(filename.length() - GHOST_FILE_EXTENSION.length());
bool areEqual = std::equal(
view.begin(),
view.end(),
GHOST_FILE_EXTENSION.begin(),
[](unsigned char a, unsigned char b) { return std::tolower(a) == b; });
if (areEqual) {
return filename.substr(0,
filename.length() - GHOST_FILE_EXTENSION.length());
}
return filename;
}
}
namespace loot {
// If the name passed ends in '.ghost', that should be trimmed.
PluginMetadata::PluginMetadata(std::string_view n) :
name_(TrimDotGhostExtension(std::string(n))) {
if (IsRegexPlugin()) {
nameRegex_ = std::regex(name_, std::regex::ECMAScript | std::regex::icase);
}
}
void PluginMetadata::MergeMetadata(const PluginMetadata& plugin) {
if (plugin.HasNameOnly())
return;
if (!group_.has_value() && plugin.GetGroup()) {
group_ = plugin.GetGroup();
}
loadAfter_ = mergeVectors(loadAfter_, plugin.loadAfter_);
requirements_ = mergeVectors(requirements_, plugin.requirements_);
incompatibilities_ =
mergeVectors(incompatibilities_, plugin.incompatibilities_);
tags_ = mergeVectors(tags_, plugin.tags_);
// Messages are in an ordered list, and should be fully merged.
messages_.insert(
end(messages_), begin(plugin.messages_), end(plugin.messages_));
dirtyInfo_ = mergeVectors(dirtyInfo_, plugin.dirtyInfo_);
cleanInfo_ = mergeVectors(cleanInfo_, plugin.cleanInfo_);
locations_ = mergeVectors(locations_, plugin.locations_);
return;
}
std::string PluginMetadata::GetName() const { return name_; }
std::optional<std::string> PluginMetadata::GetGroup() const { return group_; }
std::vector<File> PluginMetadata::GetLoadAfterFiles() const {
return loadAfter_;
}
std::vector<File> PluginMetadata::GetRequirements() const {
return requirements_;
}
std::vector<File> PluginMetadata::GetIncompatibilities() const {
return incompatibilities_;
}
std::vector<Message> PluginMetadata::GetMessages() const { return messages_; }
std::vector<Tag> PluginMetadata::GetTags() const { return tags_; }
std::vector<PluginCleaningData> PluginMetadata::GetDirtyInfo() const {
return dirtyInfo_;
}
std::vector<PluginCleaningData> PluginMetadata::GetCleanInfo() const {
return cleanInfo_;
}
std::vector<Location> PluginMetadata::GetLocations() const {
return locations_;
}
void PluginMetadata::SetGroup(std::string_view group) { group_ = group; }
void PluginMetadata::UnsetGroup() { group_ = std::nullopt; }
void PluginMetadata::SetLoadAfterFiles(const std::vector<File>& l) {
loadAfter_ = l;
}
void PluginMetadata::SetRequirements(const std::vector<File>& r) {
requirements_ = r;
}
void PluginMetadata::SetIncompatibilities(const std::vector<File>& i) {
incompatibilities_ = i;
}
void PluginMetadata::SetMessages(const std::vector<Message>& m) {
messages_ = m;
}
void PluginMetadata::SetTags(const std::vector<Tag>& t) { tags_ = t; }
void PluginMetadata::SetDirtyInfo(
const std::vector<PluginCleaningData>& dirtyInfo) {
dirtyInfo_ = dirtyInfo;
}
void PluginMetadata::SetCleanInfo(const std::vector<PluginCleaningData>& info) {
cleanInfo_ = info;
}
void PluginMetadata::SetLocations(const std::vector<Location>& locations) {
locations_ = locations;
}
bool PluginMetadata::HasNameOnly() const {
return !group_.has_value() && loadAfter_.empty() && requirements_.empty() &&
incompatibilities_.empty() && messages_.empty() && tags_.empty() &&
dirtyInfo_.empty() && cleanInfo_.empty() && locations_.empty();
}
bool PluginMetadata::IsRegexPlugin() const {
// Treat as regex if the plugin filename contains any of ":\*?|" as
// they are not valid Windows filename characters, but have meaning
// in regexes.
return strpbrk(name_.c_str(), ":\\*?|") != nullptr;
}
bool PluginMetadata::NameMatches(std::string_view pluginName) const {
if (IsRegexPlugin()) {
if (!nameRegex_.has_value()) {
throw std::runtime_error("Regex plugin does not have regex object");
}
return std::regex_match(
pluginName.begin(), pluginName.end(), nameRegex_.value());
}
return loot::rust::compare_filenames(name_, convert(pluginName)) == 0;
}
std::string PluginMetadata::AsYaml() const {
const auto metadata = convert(*this);
return std::string(metadata->as_yaml());
}
}
+66
View File
@@ -0,0 +1,66 @@
/* 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/>.
*/
#include "loot/metadata/tag.h"
namespace loot {
Tag::Tag(std::string_view tag,
const bool isAddition,
std::string_view condition) :
ConditionalMetadata(condition), name_(tag), addTag_(isAddition) {}
bool Tag::IsAddition() const { return addTag_; }
std::string Tag::GetName() const { return name_; }
bool operator==(const Tag& lhs, const Tag& rhs) {
return lhs.IsAddition() == rhs.IsAddition() &&
lhs.GetName() == rhs.GetName() &&
lhs.GetCondition() == rhs.GetCondition();
}
bool operator!=(const Tag& lhs, const Tag& rhs) { return !(lhs == rhs); }
bool operator<(const Tag& lhs, const Tag& rhs) {
if (lhs.IsAddition() != rhs.IsAddition()) {
return lhs.IsAddition() && !rhs.IsAddition();
}
if (lhs.GetName() < rhs.GetName()) {
return true;
}
if (rhs.GetName() < lhs.GetName()) {
return false;
}
return lhs.GetCondition() < rhs.GetCondition();
}
bool operator>(const Tag& lhs, const Tag& rhs) { return rhs < lhs; }
bool operator<=(const Tag& lhs, const Tag& rhs) { return !(lhs > rhs); }
bool operator>=(const Tag& lhs, const Tag& rhs) { return !(lhs < rhs); }
}

Some files were not shown because too many files have changed in this diff Show More