diff options
| author | SulfurNitride <SulfurNitride@users.noreply.github.com> | 2026-03-11 04:10:59 -0500 |
|---|---|---|
| committer | SulfurNitride <SulfurNitride@users.noreply.github.com> | 2026-03-11 04:10:59 -0500 |
| commit | bf553c6568146ac2d104c06135e27f140b5782b8 (patch) | |
| tree | 36ed51f6187ddef6a96745fadb4a2b0a8c6c4bbb /src | |
| parent | f8d99c4f4d03348eb477f812c1769e670a19eb42 (diff) | |
Fix LOOT "ambiguous load order" by merging base ESMs into plugins.txt
MO2's plugins.txt omits implicitly-active base game ESMs — LOOT needs
every plugin listed to determine unambiguous load order. Now merges
entries from loadorder.txt that are missing from plugins.txt before
passing the files to LOOT.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'src')
| -rw-r--r-- | src/src/loot.cpp | 57 |
1 files changed, 53 insertions, 4 deletions
diff --git a/src/src/loot.cpp b/src/src/loot.cpp index 794cc9b..75ecbbc 100644 --- a/src/src/loot.cpp +++ b/src/src/loot.cpp @@ -1350,7 +1350,10 @@ static bool launchLootGui(QWidget* parent, OrganizerCore& core) ensureLootSettings(lootDataPath, mo2GameName, folderName, gamePath, localPath);
// Copy the profile's load order files to the local path so LOOT sees
- // the current load order.
+ // the current load order. MO2's plugins.txt omits implicitly-active
+ // plugins (base game ESMs) — LOOT needs every plugin listed or it warns
+ // about an "ambiguous load order". Merge loadorder.txt entries into
+ // plugins.txt so LOOT sees the complete picture.
QDir().mkpath(localPath);
QString profilePlugins = core.profilePath() + "/plugins.txt";
QString profileLoadOrder = core.profilePath() + "/loadorder.txt";
@@ -1359,12 +1362,58 @@ static bool launchLootGui(QWidget* parent, OrganizerCore& core) QFile::remove(lootPlugins);
QFile::remove(lootLoadOrder);
- if (QFile::exists(profilePlugins)) {
- QFile::copy(profilePlugins, lootPlugins);
- }
if (QFile::exists(profileLoadOrder)) {
QFile::copy(profileLoadOrder, lootLoadOrder);
}
+ if (QFile::exists(profilePlugins) && QFile::exists(profileLoadOrder)) {
+ // Build a set of plugins already in plugins.txt (case-insensitive).
+ QFile pluginsFile(profilePlugins);
+ QSet<QString> pluginsListed;
+ QStringList pluginsLines;
+ if (pluginsFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ QTextStream in(&pluginsFile);
+ while (!in.atEnd()) {
+ QString line = in.readLine();
+ pluginsLines.append(line);
+ QString name = line.trimmed();
+ if (name.startsWith('*'))
+ name = name.mid(1);
+ if (!name.isEmpty() && !name.startsWith('#'))
+ pluginsListed.insert(name.toLower());
+ }
+ pluginsFile.close();
+ }
+
+ // Read loadorder.txt to find plugins not in plugins.txt (base ESMs).
+ QFile loFile(profileLoadOrder);
+ QStringList missingPlugins;
+ if (loFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ QTextStream in(&loFile);
+ while (!in.atEnd()) {
+ QString line = in.readLine().trimmed();
+ if (line.isEmpty() || line.startsWith('#'))
+ continue;
+ if (!pluginsListed.contains(line.toLower())) {
+ // Base game ESMs are always active — mark with *.
+ missingPlugins.append("*" + line);
+ }
+ }
+ loFile.close();
+ }
+
+ // Write merged plugins.txt: missing base ESMs first, then original.
+ QFile outFile(lootPlugins);
+ if (outFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
+ QTextStream out(&outFile);
+ for (const auto& line : missingPlugins)
+ out << line << "\n";
+ for (const auto& line : pluginsLines)
+ out << line << "\n";
+ outFile.close();
+ }
+ } else if (QFile::exists(profilePlugins)) {
+ QFile::copy(profilePlugins, lootPlugins);
+ }
// Mount the FUSE VFS so LOOT sees the merged mod files in the Data directory.
log::info("Mounting VFS for LOOT...");
|
