Allow loaded plugins to be retrieved using a .ghost extension

libloot trims any .ghost extension from plugins it loads, so be consistent with that behaviour when getting or sorting plugins.
This commit is contained in:
Oliver Hamlet
2026-05-14 18:58:26 +01:00
parent 4410265e10
commit 7942083cd0
2 changed files with 45 additions and 5 deletions
@@ -580,6 +580,23 @@ TEST_P(GameInterfaceTest,
EXPECT_NE(pointer1, pointer2);
}
TEST_P(GameInterfaceTest, getPluginShouldStripGhostExtension) {
if (GetParam() == GameType::openmw) {
return;
}
const auto ghostedName = std::string(BLANK_ESP) + ".ghost";
copyPlugin(BLANK_ESP, ghostedName);
handle_->LoadPlugins({BLANK_ESP}, true);
EXPECT_EQ(1, handle_->GetLoadedPlugins().size());
const auto plugin = handle_->GetPlugin(ghostedName);
ASSERT_NE(nullptr, plugin);
EXPECT_EQ(BLANK_ESP, plugin->GetName());
}
TEST_P(GameInterfaceTest,
getLoadedPluginsShouldReturnAnEmptySetIfNoneHaveBeenLoaded) {
EXPECT_TRUE(handle_->GetLoadedPlugins().empty());
@@ -666,14 +683,19 @@ TEST_P(GameInterfaceTest, sortPluginsShouldSupportGhostedPlugins) {
return;
}
const auto pluginName =
GetParam() == GameType::starfield ? BLANK_FULL_ESM : BLANK_ESM;
copyPlugin(pluginName);
copyPlugin(BLANK_ESP, "Blank.esp.ghost");
handle_->LoadPlugins({BLANK_ESP}, false);
handle_->LoadPlugins({pluginName, BLANK_ESP}, false);
const auto sortedOrder = handle_->SortPlugins(
{std::string(BLANK_ESP)});
{std::string(pluginName) + ".ghost", std::string(BLANK_ESP)});
std::vector<std::string> expectedOrder{std::string(BLANK_ESP)};
std::vector<std::string> expectedOrder{std::string(pluginName),
std::string(BLANK_ESP)};
EXPECT_EQ(expectedOrder, sortedOrder);
}
+20 -2
View File
@@ -19,7 +19,7 @@ use crate::{
logging::{self, format_details, is_log_enabled},
metadata::{
Filename,
plugin_metadata::{GHOST_FILE_EXTENSION, iends_with_ascii},
plugin_metadata::{GHOST_FILE_EXTENSION, iends_with_ascii, trim_dot_ghost},
},
plugin::{
LoadScope, Plugin,
@@ -821,7 +821,8 @@ impl GameCache {
}
fn plugin(&self, plugin_name: &str) -> Option<&Arc<Plugin>> {
self.plugins.get(&Filename::new(plugin_name.to_owned()))
self.plugins
.get(&Filename::new(trim_dot_ghost(plugin_name).to_owned()))
}
pub(crate) fn archives_iter(&self) -> impl Iterator<Item = &PathBuf> {
@@ -2274,6 +2275,23 @@ mod tests {
assert_eq!("Blank.esm", cache.plugin("blank.esm").unwrap().name());
}
#[test]
fn should_trim_dot_ghost_extension() {
let mut cache = GameCache::default();
cache.insert_plugins(vec![
Plugin::new(
GameType::Oblivion,
&cache,
&source_plugins_path(GameType::Oblivion).join(BLANK_ESM),
LoadScope::HeaderOnly,
)
.unwrap(),
]);
assert_eq!("Blank.esm", cache.plugin("blank.esm.ghost").unwrap().name());
}
#[test]
fn should_return_none_if_the_plugin_is_not_cached() {
let cache = GameCache::default();