aboutsummaryrefslogtreecommitdiff
path: root/libs/installer_fomod_plus/scanner/FomodPlusScanner.cpp
diff options
context:
space:
mode:
authorSulfurNitride <SulfurNitride@users.noreply.github.com>2026-02-14 02:45:12 -0600
committerSulfurNitride <SulfurNitride@users.noreply.github.com>2026-02-14 02:45:25 -0600
commit817e8f5cd26739c69d930d21cd9dc4c0b6e4984e (patch)
tree52aed11d6751bb7d20b06acf197d56fac113203d /libs/installer_fomod_plus/scanner/FomodPlusScanner.cpp
parent51a9f8f197727f00896e5de44569b098923527dd (diff)
Add FUSE external mapping support, BG3/Oblivion Remastered fixes, fomod-plus and NaK integration
FUSE VFS now deploys non-data-dir mod mappings (Paks, OBSE, UE4SS, etc.) via real symlinks and injects file-level data-dir mappings (plugins.txt, loadorder.txt) into the VFS tree. Fixes game launches for Oblivion Remastered (Root Builder path resolution, script extender support) and BG3 (Wine prefix documents directory, file mapper symlinks on Linux). Vendors mo2-fomod-plus plugin and NaK crate for FOMOD installer and game finder/runtime support. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'libs/installer_fomod_plus/scanner/FomodPlusScanner.cpp')
-rw-r--r--libs/installer_fomod_plus/scanner/FomodPlusScanner.cpp128
1 files changed, 128 insertions, 0 deletions
diff --git a/libs/installer_fomod_plus/scanner/FomodPlusScanner.cpp b/libs/installer_fomod_plus/scanner/FomodPlusScanner.cpp
new file mode 100644
index 0000000..4770178
--- /dev/null
+++ b/libs/installer_fomod_plus/scanner/FomodPlusScanner.cpp
@@ -0,0 +1,128 @@
+#include "FomodPlusScanner.h"
+
+#include "archiveparser.h"
+#include "FomodDBEntry.h"
+
+#include <QDialog>
+#include <QPushButton>
+#include <QProgressBar>
+#include <QVBoxLayout>
+#include <QMessageBox>
+#include <archive.h>
+
+#include <QLabel>
+#include <QMovie>
+#include <iostream>
+
+#include "stringutil.h"
+
+using ScanCallbackFn = std::function<bool(IModInterface*, ScanResult result)>;
+
+bool FomodPlusScanner::init(IOrganizer* organizer)
+{
+ mOrganizer = organizer;
+
+ mDialog = new QDialog();
+ mDialog->setWindowTitle(tr("FOMOD Scanner"));
+
+ const auto layout = new QVBoxLayout(mDialog);
+
+ const QString description = tr("Greetings, traveler.\n"
+ "This tool will scan your load order for mods installed via FOMOD.\n"
+ "It will also fix up any erroneous FOMOD flags from previous versions of FOMOD Plus :) \n\n"
+ "Safe travels, and may your load order be free of conflicts.");
+
+ const auto descriptionLabel = new QLabel(description, mDialog);
+ descriptionLabel->setWordWrap(true); // Enable word wrap for large text
+ descriptionLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ descriptionLabel->setAlignment(Qt::AlignLeft | Qt::AlignTop); // Align text to the top-left corner
+
+ const auto gifLabel = new QLabel(mDialog);
+ const auto movie = new QMovie(":/fomod/wizard");
+ gifLabel->setMovie(movie);
+ movie->start();
+
+ mProgressBar = new QProgressBar(mDialog);
+ mProgressBar->setRange(0, mOrganizer->modList()->allMods().size());
+ mProgressBar->setVisible(false);
+
+ const auto scanButton = new QPushButton(tr("Scan"), mDialog);
+ const auto cancelButton = new QPushButton(tr("Cancel"), mDialog);
+
+ layout->addWidget(descriptionLabel, 1);
+ layout->addWidget(gifLabel, 1);
+ layout->addWidget(mProgressBar, 1);
+ layout->addWidget(scanButton, 1);
+ layout->addWidget(cancelButton, 1);
+
+ connect(cancelButton, &QPushButton::clicked, mDialog, &QDialog::reject);
+ connect(scanButton, &QPushButton::clicked, this, &FomodPlusScanner::onScanClicked);
+ connect(mDialog, &QDialog::finished, this, &FomodPlusScanner::cleanup);
+
+ mDialog->setLayout(layout);
+ mDialog->setMinimumSize(400, 300);
+ mDialog->adjustSize();
+ descriptionLabel->adjustSize();
+ return true;
+}
+
+void FomodPlusScanner::onScanClicked() const
+{
+ mProgressBar->setVisible(true);
+ const int added = scanLoadOrder(setFomodInfoForMod);
+ mDialog->accept();
+ QMessageBox::information(mDialog, tr("Scan Complete"),
+ tr("The load order scan is complete. Updated filter info for ") + QString::number(added) + tr(" mods."));
+ mOrganizer->refresh();
+}
+
+void FomodPlusScanner::cleanup() const
+{
+ mProgressBar->reset();
+ mProgressBar->setVisible(false);
+}
+
+void FomodPlusScanner::display() const
+{
+ mDialog->exec();
+}
+
+int FomodPlusScanner::scanLoadOrder(const ScanCallbackFn& callback) const
+{
+ int progress = 0;
+ int modified = 0;
+ for (const auto& modName : mOrganizer->modList()->allMods()) {
+ const auto mod = mOrganizer->modList()->getMod(modName);
+ if (const ScanResult result = openInstallationArchive(mod); callback(mod, result)) {
+ modified++;
+ }
+ mProgressBar->setValue(++progress);
+ }
+ return modified;
+}
+
+ScanResult FomodPlusScanner::openInstallationArchive(const IModInterface* mod) const
+{
+ const auto downloadsDir = mOrganizer->downloadsPath();
+ const auto installationFilePath = mod->installationFile();
+ return ArchiveParser::scanForFomodFiles(downloadsDir, installationFilePath, mod->name());
+}
+
+bool FomodPlusScanner::setFomodInfoForMod(IModInterface* mod, const ScanResult result)
+{
+ const auto pluginName = "FOMOD Plus";
+ const auto setting = mod->pluginSetting(pluginName, "fomod", 0);
+ if (setting == 0 && ScanResult::HAS_FOMOD == result) {
+ return mod->setPluginSetting(pluginName, "fomod", "{}");
+ }
+ if (setting != 0 && ScanResult::NO_FOMOD == result) {
+ return mod->setPluginSetting(pluginName, "fomod", 0);
+ }
+ return false;
+}
+
+bool FomodPlusScanner::removeFomodInfoFromMod(IModInterface* mod, ScanResult)
+{
+ const auto pluginName = QString::fromStdString("FOMOD Plus");
+ return mod->setPluginSetting(pluginName, "fomod", 0);
+}