mirror of
https://github.com/loot/libloot.git
synced 2026-07-27 14:16:01 -07:00
Add support for ESL plugins
The heavy lifting is done by esplugin and libloadorder.
This commit is contained in:
+2
-2
@@ -74,7 +74,7 @@ set (GTEST_LIBRARIES "${BINARY_DIR}/googlemock/gtest/${CMAKE_CFG_INTDIR}/${CMAKE
|
||||
|
||||
ExternalProject_Add(esplugin
|
||||
PREFIX "external"
|
||||
URL "https://github.com/WrinklyNinja/esplugin/archive/1.0.3.tar.gz"
|
||||
URL "https://github.com/WrinklyNinja/esplugin/archive/1.0.5.tar.gz"
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_IN_SOURCE 1
|
||||
BUILD_COMMAND cargo build --release --all --all-features --target ${RUST_TARGET}
|
||||
@@ -100,7 +100,7 @@ set(LIBGIT2_LIBRARIES "${BINARY_DIR}/${CMAKE_CFG_INTDIR}/${CMAKE_STATIC_LIBRARY_
|
||||
|
||||
ExternalProject_Add(libloadorder
|
||||
PREFIX "external"
|
||||
URL "https://github.com/WrinklyNinja/libloadorder/archive/rust-rewrite.tar.gz"
|
||||
URL "https://github.com/WrinklyNinja/libloadorder/archive/master.tar.gz"
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_IN_SOURCE 1
|
||||
BUILD_COMMAND cargo build --release --all --all-features --target ${RUST_TARGET}
|
||||
|
||||
@@ -85,6 +85,12 @@ public:
|
||||
*/
|
||||
virtual bool IsMaster() const = 0;
|
||||
|
||||
/**
|
||||
* Check if the plugin is a light master.
|
||||
* @return True if plugin is a light master, false otherwise.
|
||||
*/
|
||||
virtual bool IsLightMaster() const = 0;
|
||||
|
||||
/**
|
||||
* Check if the plugin contains any records other than its TES4 header.
|
||||
* @return True if the plugin only contains a TES4 header, false otherwise.
|
||||
|
||||
@@ -174,7 +174,7 @@ bool ConditionEvaluator::fileExists(const std::string& filePath) const {
|
||||
return true;
|
||||
} catch (...) {
|
||||
// Not a loaded plugin, check the filesystem.
|
||||
if (boost::iends_with(filePath, ".esp") || boost::iends_with(filePath, ".esm"))
|
||||
if (hasPluginFileExtension(filePath, gameType_))
|
||||
return boost::filesystem::exists(dataPath_ / filePath)
|
||||
|| boost::filesystem::exists(dataPath_ / (filePath + ".ghost"));
|
||||
else
|
||||
@@ -257,7 +257,7 @@ bool ConditionEvaluator::checksumMatches(const std::string& filePath, const uint
|
||||
if (realChecksum == 0) {
|
||||
if (boost::filesystem::exists(dataPath_ / filePath))
|
||||
realChecksum = GetCrc32(dataPath_ / filePath);
|
||||
else if ((boost::iends_with(filePath, ".esp") || boost::iends_with(filePath, ".esm")) && boost::filesystem::exists(dataPath_ / (filePath + ".ghost")))
|
||||
else if (hasPluginFileExtension(filePath, gameType_) && boost::filesystem::exists(dataPath_ / (filePath + ".ghost")))
|
||||
realChecksum = GetCrc32(dataPath_ / (filePath + ".ghost"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,6 +155,16 @@ bool Plugin::IsMaster() const {
|
||||
return isMaster;
|
||||
}
|
||||
|
||||
bool Plugin::IsLightMaster() const {
|
||||
bool isLightMaster;
|
||||
auto ret = esp_plugin_is_light_master(esPlugin.get(), &isLightMaster);
|
||||
if (ret != ESP_OK) {
|
||||
throw FileAccessError(name_ + " : Libespm error code: " + std::to_string(ret));
|
||||
}
|
||||
|
||||
return isLightMaster;
|
||||
}
|
||||
|
||||
bool Plugin::IsEmpty() const {
|
||||
return isEmpty_;
|
||||
}
|
||||
@@ -196,7 +206,7 @@ bool Plugin::IsValid(const std::string& filename, const GameType gameType, const
|
||||
name = filename;
|
||||
|
||||
// Check that the file has a valid extension.
|
||||
if (!boost::iends_with(name, ".esm") && !boost::iends_with(name, ".esp"))
|
||||
if (!hasPluginFileExtension(name, gameType))
|
||||
return false;
|
||||
|
||||
bool isValid;
|
||||
@@ -286,15 +296,27 @@ bool Plugin::LoadsArchive(const std::string& pluginName, const GameType gameType
|
||||
}
|
||||
|
||||
unsigned int Plugin::GetEspluginGameId(GameType gameType) {
|
||||
if (gameType == GameType::tes4)
|
||||
switch (gameType) {
|
||||
case GameType::tes4:
|
||||
return ESP_GAME_OBLIVION;
|
||||
else if (gameType == GameType::tes5 || gameType == GameType::tes5se)
|
||||
case GameType::tes5:
|
||||
return ESP_GAME_SKYRIM;
|
||||
else if (gameType == GameType::fo3)
|
||||
case GameType::tes5se:
|
||||
return ESP_GAME_SKYRIMSE;
|
||||
case GameType::fo3:
|
||||
return ESP_GAME_FALLOUT3;
|
||||
else if (gameType == GameType::fonv)
|
||||
case GameType::fonv:
|
||||
return ESP_GAME_FALLOUTNV;
|
||||
else
|
||||
return ESP_GAME_SKYRIM;
|
||||
default:
|
||||
return ESP_GAME_FALLOUT4;
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
&& boost::iends_with(filename, ".esl");
|
||||
|
||||
return espOrEsm || lightMaster;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include <boost/locale.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <esplugin.hpp>
|
||||
|
||||
#include "api/game/load_order_handler.h"
|
||||
@@ -56,6 +57,7 @@ public:
|
||||
uint32_t GetCRC() const;
|
||||
|
||||
bool IsMaster() const;
|
||||
bool IsLightMaster() const;
|
||||
bool IsEmpty() const;
|
||||
bool LoadsArchive() const;
|
||||
bool DoFormIDsOverlap(const PluginInterface& plugin) const;
|
||||
@@ -91,6 +93,8 @@ private:
|
||||
|
||||
std::shared_ptr<std::remove_pointer<::Plugin>::type> esPlugin;
|
||||
};
|
||||
|
||||
bool hasPluginFileExtension(const std::string& filename, GameType gameType);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -51,7 +51,7 @@ std::string PluginSortingData::GetName() const {
|
||||
}
|
||||
|
||||
bool PluginSortingData::IsMaster() const {
|
||||
return plugin_.IsMaster();
|
||||
return plugin_.IsMaster() || plugin_.IsLightMaster();
|
||||
}
|
||||
|
||||
bool PluginSortingData::LoadsArchive() const {
|
||||
|
||||
@@ -37,6 +37,7 @@ protected:
|
||||
PluginTest() :
|
||||
emptyFile("EmptyFile.esm"),
|
||||
lowercaseBlankEsp("blank.esp"),
|
||||
blankEsl("blank.esl"),
|
||||
game_(GetParam(), dataPath.parent_path(), localPath),
|
||||
blankArchive("Blank" + GetArchiveFileExtension(game_.Type())),
|
||||
blankSuffixArchive("Blank - Different - suffix" + GetArchiveFileExtension(game_.Type())) {}
|
||||
@@ -55,6 +56,8 @@ protected:
|
||||
ASSERT_NO_THROW(boost::filesystem::copy(dataPath / blankEsp, dataPath / lowercaseBlankEsp));
|
||||
#endif
|
||||
|
||||
ASSERT_NO_THROW(boost::filesystem::copy(dataPath / blankEsp, dataPath / blankEsl));
|
||||
|
||||
// Create dummy archive files.
|
||||
out.open(dataPath / blankArchive);
|
||||
out.close();
|
||||
@@ -69,6 +72,7 @@ protected:
|
||||
#ifndef _WIN32
|
||||
boost::filesystem::remove(dataPath / lowercaseBlankEsp);
|
||||
#endif
|
||||
boost::filesystem::remove(dataPath / blankEsl);
|
||||
boost::filesystem::remove(dataPath / blankArchive);
|
||||
boost::filesystem::remove(dataPath / blankSuffixArchive);
|
||||
}
|
||||
@@ -77,6 +81,7 @@ protected:
|
||||
|
||||
const std::string emptyFile;
|
||||
const std::string lowercaseBlankEsp;
|
||||
const std::string blankEsl;
|
||||
const std::string blankArchive;
|
||||
const std::string blankSuffixArchive;
|
||||
private:
|
||||
@@ -98,6 +103,7 @@ public:
|
||||
uint32_t GetCRC() const { return 0; }
|
||||
|
||||
bool IsMaster() const { return false; }
|
||||
bool IsLightMaster() const { return false; }
|
||||
bool IsEmpty() const { return false; }
|
||||
bool LoadsArchive() const { return false; }
|
||||
bool DoFormIDsOverlap(const PluginInterface& plugin) const { return true; }
|
||||
@@ -159,6 +165,16 @@ TEST_P(PluginTest, loadingANonMasterPluginShouldReadTheMasterFlagAsFalse) {
|
||||
EXPECT_FALSE(plugin.IsMaster());
|
||||
}
|
||||
|
||||
TEST_P(PluginTest, isLightMasterShouldBeTrueForAPluginWithEslFileExtensionForFallout4AndSkyrimSeAndFalseOtherwise) {
|
||||
Plugin plugin1(game_.Type(), game_.DataPath(), game_.GetLoadOrderHandler(), blankEsm, true);
|
||||
Plugin plugin2(game_.Type(), game_.DataPath(), game_.GetLoadOrderHandler(), blankMasterDependentEsp, true);
|
||||
Plugin plugin3(game_.Type(), game_.DataPath(), game_.GetLoadOrderHandler(), blankEsl, true);
|
||||
|
||||
EXPECT_FALSE(plugin1.IsLightMaster());
|
||||
EXPECT_FALSE(plugin2.IsLightMaster());
|
||||
EXPECT_EQ(GetParam() == GameType::fo4 || GetParam() == GameType::tes5se, plugin3.IsLightMaster());
|
||||
}
|
||||
|
||||
TEST_P(PluginTest, loadingAPluginWithMastersShouldReadThemCorrectly) {
|
||||
Plugin plugin(game_.Type(), game_.DataPath(), game_.GetLoadOrderHandler(), blankMasterDependentEsp, true);
|
||||
|
||||
@@ -271,6 +287,18 @@ TEST_P(PluginTest, doFormIDsOverlapShouldReturnTrueIfOnePluginOverridesTheOthers
|
||||
EXPECT_TRUE(plugin1.DoFormIDsOverlap(plugin2));
|
||||
EXPECT_TRUE(plugin2.DoFormIDsOverlap(plugin1));
|
||||
}
|
||||
|
||||
TEST_P(PluginTest, hasPluginFileExtensionShouldBeTrueIfFileEndsInDotEspOrDotEsm) {
|
||||
EXPECT_TRUE(hasPluginFileExtension("file.esp", GetParam()));
|
||||
EXPECT_TRUE(hasPluginFileExtension("file.esm", GetParam()));
|
||||
EXPECT_FALSE(hasPluginFileExtension("file.bsa", GetParam()));
|
||||
}
|
||||
|
||||
TEST_P(PluginTest, hasPluginFileExtensionShouldBeTrueIfFileEndsInDotEslOnlyForFallout4AndSkyrimSE) {
|
||||
bool result = hasPluginFileExtension("file.esl", GetParam());
|
||||
|
||||
EXPECT_EQ(GetParam() == GameType::fo4 || GetParam() == GameType::tes5se, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user