From ec8d4913afe90c58e1b311960c32755c34454470 Mon Sep 17 00:00:00 2001 From: Max Roncace Date: Wed, 29 Jul 2026 23:18:45 -0400 Subject: [PATCH] gamebryo-plugin-management: Fix plugin IDs for LOOT on case-sensitive FSes --- .../src/autosort.ts | 4 +++- .../src/util/toLootPluginName.test.ts | 19 +++++++++++++++++++ .../src/util/toLootPluginName.ts | 15 +++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 extensions/gamebryo-plugin-management/src/util/toLootPluginName.test.ts create mode 100644 extensions/gamebryo-plugin-management/src/util/toLootPluginName.ts diff --git a/extensions/gamebryo-plugin-management/src/autosort.ts b/extensions/gamebryo-plugin-management/src/autosort.ts index 8a5a0d28f..0a20722a2 100644 --- a/extensions/gamebryo-plugin-management/src/autosort.ts +++ b/extensions/gamebryo-plugin-management/src/autosort.ts @@ -18,6 +18,7 @@ import { gameDataPath, gameSupported, nativePlugins, pluginPath } from "./util/g import { missingGroupFixes } from "./util/groups"; import { invalidPluginsFromError } from "./util/invalidPlugins"; import { downloadMasterlist, downloadPrelude } from "./util/masterlist"; +import toLootPluginName from "./util/toLootPluginName"; import toPluginId from "./util/toPluginId"; const MAX_RESTARTS = 3; @@ -578,7 +579,7 @@ class LootInterface { } try { await loot.loadPluginsAsync( - deployed.filter((id) => !invalid.has(id)).map((name) => toPluginId(name)), + deployed.filter((id) => !invalid.has(id)).map((id) => toLootPluginName(id, pluginList)), false, ); pluginsLoaded = true; @@ -908,6 +909,7 @@ class LootInterface { expectSuccess: true, env: { ELECTRON_RUN_AS_NODE: "1", + LD_LIBRARY_PATH: path.join(__dirname, ".."), }, }) .catch((err: Error) => { diff --git a/extensions/gamebryo-plugin-management/src/util/toLootPluginName.test.ts b/extensions/gamebryo-plugin-management/src/util/toLootPluginName.test.ts new file mode 100644 index 000000000..483eb0ed4 --- /dev/null +++ b/extensions/gamebryo-plugin-management/src/util/toLootPluginName.test.ts @@ -0,0 +1,19 @@ +import { describe, expect, it } from "vitest"; + +import toLootPluginName from "./toLootPluginName"; + +describe("toLootPluginName", () => { + it("uses on-disk filename casing when file path is known", () => { + const pluginList = { + "myplugin.esp": { + filePath: "C:\\Games\\Data\\MyPlugin.esp", + }, + } as any; + + expect(toLootPluginName("myplugin.esp", pluginList)).toBe("MyPlugin.esp"); + }); + + it("falls back to normalized plugin id when file path is missing", () => { + expect(toLootPluginName("MyPlugin.esp", {} as any)).toBe("myplugin.esp"); + }); +}); diff --git a/extensions/gamebryo-plugin-management/src/util/toLootPluginName.ts b/extensions/gamebryo-plugin-management/src/util/toLootPluginName.ts new file mode 100644 index 000000000..5118ad85c --- /dev/null +++ b/extensions/gamebryo-plugin-management/src/util/toLootPluginName.ts @@ -0,0 +1,15 @@ +import * as path from "path"; + +import { IPlugins } from "../types/IPlugins"; +import toPluginId from "./toPluginId"; + +function toLootPluginName(pluginId: string, pluginList: IPlugins): string { + const filePath = pluginList[pluginId]?.filePath; + if (filePath === undefined) { + return toPluginId(pluginId); + } + const base = path.basename(filePath); + return base === filePath ? path.win32.basename(filePath) : base; +} + +export default toLootPluginName;