aboutsummaryrefslogtreecommitdiff
path: root/libs/installer_omod/src/implementations
diff options
context:
space:
mode:
authorSulfurNitride <SulfurNitride@users.noreply.github.com>2026-02-11 02:37:39 -0600
committerSulfurNitride <SulfurNitride@users.noreply.github.com>2026-02-11 02:37:39 -0600
commit7ee008e150bc5bcf76082d726f719ee0fdfda982 (patch)
tree27fb39be241fdb5ac2734c574de678977d1856d0 /libs/installer_omod/src/implementations
Fluorine Manager: full Linux port of Mod Organizer 2
Complete native Linux port with FUSE-based virtual filesystem, Proton/umu-run integration, and Flatpak packaging. Key features: - FUSE VFS replacing Windows USVFS (in-process + standalone helper for Flatpak) - Proton/GE-Proton/umu-run launcher with env var forwarding - Flatpak support (sandbox-aware VFS, NXM handler, umu-run) - Wine prefix management UI - Case-insensitive path resolution for Linux filesystems - QSettings-safe INI handling (avoids Bethesda INI corruption) - Portable instance support with auto-generated launcher scripts Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'libs/installer_omod/src/implementations')
-rw-r--r--libs/installer_omod/src/implementations/CodeProgress.cpp85
-rw-r--r--libs/installer_omod/src/implementations/CodeProgress.h59
-rw-r--r--libs/installer_omod/src/implementations/Logger.cpp45
-rw-r--r--libs/installer_omod/src/implementations/Logger.h17
-rw-r--r--libs/installer_omod/src/implementations/ScriptFunctions.cpp320
-rw-r--r--libs/installer_omod/src/implementations/ScriptFunctions.h122
6 files changed, 648 insertions, 0 deletions
diff --git a/libs/installer_omod/src/implementations/CodeProgress.cpp b/libs/installer_omod/src/implementations/CodeProgress.cpp
new file mode 100644
index 0000000..7256592
--- /dev/null
+++ b/libs/installer_omod/src/implementations/CodeProgress.cpp
@@ -0,0 +1,85 @@
+#include <QApplication>
+
+#include "CodeProgress.h"
+
+CodeProgressHelper::CodeProgressHelper(QWidget* parentWidget) : mParentWidget{ parentWidget }, mProgressDialog(make_nullptr<QProgressDialog>()) {
+ moveToThread(QApplication::instance()->thread());
+ connect(this, &CodeProgressHelper::ShowProgressDialogSignal, this, &CodeProgressHelper::ShowProgressDialogSlot, Qt::QueuedConnection);
+ connect(this, &CodeProgressHelper::UpdateProgressValueSignal, this, &CodeProgressHelper::UpdateProgressValueSlot, Qt::BlockingQueuedConnection);
+ connect(this, &CodeProgressHelper::HideProgressDialogSignal, this, &CodeProgressHelper::HideProgressDialogSlot, Qt::QueuedConnection);
+
+}
+
+void CodeProgressHelper::ShowProgressDialog() {
+ emit ShowProgressDialogSignal();
+}
+
+void CodeProgressHelper::UpdateProgressValue(int percentage) {
+ emit UpdateProgressValueSignal(percentage);
+}
+
+void CodeProgressHelper::HideProgressDialog() {
+ emit HideProgressDialogSignal();
+}
+
+void CodeProgressHelper::ShowProgressDialogSlot() {
+ mProgressDialog.reset(new QProgressDialog(mParentWidget));
+ mProgressDialog->setWindowFlags(mProgressDialog->windowFlags() & ~Qt::WindowContextHelpButtonHint & ~Qt::WindowCloseButtonHint);
+ mProgressDialog->setWindowModality(Qt::WindowModal);
+ mProgressDialog->setCancelButton(nullptr);
+ mProgressDialog->setMinimum(0);
+ mProgressDialog->setMaximum(100);
+ mProgressDialog->setAutoReset(false);
+ mProgressDialog->setAutoClose(false);
+ mProgressDialog->show();
+}
+
+void CodeProgressHelper::UpdateProgressValueSlot(int percentage) {
+ mProgressDialog->setValue(percentage);
+}
+
+void CodeProgressHelper::HideProgressDialogSlot() {
+ mProgressDialog->hide();
+ mProgressDialog.reset();
+ }
+
+void CodeProgress::Init(__int64 totalSize, bool compressing)
+{
+ System::GC::ReRegisterForFinalize(this);
+
+ mTotalSize = totalSize;
+ mCompressing = compressing;
+ mPercentage = 0;
+
+ mHelper = new CodeProgressHelper(mParentWidget);
+ mHelper->ShowProgressDialog();
+}
+
+void CodeProgress::SetProgress(__int64 inSize, __int64 outSize)
+{
+ int newPercentage = (int)(100 * inSize / (double)mTotalSize);
+ if (newPercentage != mPercentage)
+ mHelper->UpdateProgressValue(newPercentage);
+ mPercentage = newPercentage;
+}
+
+CodeProgress::~CodeProgress()
+{
+ if (!mHelper)
+ return;
+
+ mHelper->HideProgressDialog();
+ this->!CodeProgress();
+}
+
+CodeProgress::!CodeProgress()
+{
+ if (mHelper)
+ mHelper->deleteLater();
+ mHelper = nullptr;
+}
+
+void CodeProgress::setParentWidget(QWidget* parentWidget)
+{
+ mParentWidget = parentWidget;
+}
diff --git a/libs/installer_omod/src/implementations/CodeProgress.h b/libs/installer_omod/src/implementations/CodeProgress.h
new file mode 100644
index 0000000..902fccd
--- /dev/null
+++ b/libs/installer_omod/src/implementations/CodeProgress.h
@@ -0,0 +1,59 @@
+#pragma once
+
+#include <QObject>
+#include <QWidget>
+#include <QProgressDialog>
+
+#include "../QObject_unique_ptr.h"
+
+using namespace cli;
+
+class CodeProgressHelper : public QObject {
+ Q_OBJECT
+public:
+
+ CodeProgressHelper(QWidget* parentWidget);
+
+ void ShowProgressDialog();
+ void UpdateProgressValue(int percentage);
+ void HideProgressDialog();
+
+public slots:
+ void ShowProgressDialogSlot();
+ void UpdateProgressValueSlot(int percentage);
+ void HideProgressDialogSlot();
+
+signals:
+
+ void ShowProgressDialogSignal();
+ void UpdateProgressValueSignal(int percentage);
+ void HideProgressDialogSignal();
+
+private:
+ QWidget* mParentWidget;
+ QObject_unique_ptr<QProgressDialog> mProgressDialog;
+};
+
+ref class CodeProgress : OMODFramework::ICodeProgress
+{
+public:
+
+ CodeProgress(QWidget* parentWidget) : mParentWidget(parentWidget) { }
+
+ virtual void Init(__int64 totalSize, bool compressing);
+
+ virtual void SetProgress(__int64 inSize, __int64 outSize);
+
+ ~CodeProgress();
+
+ !CodeProgress();
+
+ void setParentWidget(QWidget* parentWidget);
+
+private:
+ QWidget* mParentWidget;
+ CodeProgressHelper* mHelper;
+ __int64 mTotalSize;
+ int mPercentage;
+ bool mCompressing;
+};
diff --git a/libs/installer_omod/src/implementations/Logger.cpp b/libs/installer_omod/src/implementations/Logger.cpp
new file mode 100644
index 0000000..2b0a0c1
--- /dev/null
+++ b/libs/installer_omod/src/implementations/Logger.cpp
@@ -0,0 +1,45 @@
+#include "Logger.h"
+
+#include "../interop/StdDotNetConverters.h"
+
+OMODFramework::LoggingLevel Logger::OMODLoggingLevel(MOBase::log::Levels level)
+{
+ switch (level)
+ {
+ default:
+ case MOBase::log::Debug:
+ return OMODFramework::LoggingLevel::DEBUG;
+ case MOBase::log::Info:
+ return OMODFramework::LoggingLevel::INFO;
+ case MOBase::log::Warning:
+ return OMODFramework::LoggingLevel::WARNING;
+ case MOBase::log::Error:
+ return OMODFramework::LoggingLevel::ERROR;
+ }
+}
+
+MOBase::log::Levels Logger::MOLoggingLevel(OMODFramework::LoggingLevel level)
+{
+ switch (level)
+ {
+ default:
+ case OMODFramework::LoggingLevel::DEBUG:
+ return MOBase::log::Debug;
+ case OMODFramework::LoggingLevel::INFO:
+ return MOBase::log::Info;
+ case OMODFramework::LoggingLevel::WARNING:
+ return MOBase::log::Warning;
+ case OMODFramework::LoggingLevel::ERROR:
+ return MOBase::log::Error;
+ }
+}
+
+void Logger::Init()
+{
+ //no op
+}
+
+void Logger::Log(OMODFramework::LoggingLevel level, System::String^ message, System::DateTime time)
+{
+ MOBase::log::getDefault().log(MOLoggingLevel(level), "{}", toUTF8String(message));
+}
diff --git a/libs/installer_omod/src/implementations/Logger.h b/libs/installer_omod/src/implementations/Logger.h
new file mode 100644
index 0000000..e3d1e7e
--- /dev/null
+++ b/libs/installer_omod/src/implementations/Logger.h
@@ -0,0 +1,17 @@
+#pragma once
+
+using namespace cli;
+
+#include <uibase/log.h>
+
+ref class Logger : OMODFramework::ILogger
+{
+public:
+ static OMODFramework::LoggingLevel OMODLoggingLevel(MOBase::log::Levels level);
+
+ static MOBase::log::Levels MOLoggingLevel(OMODFramework::LoggingLevel level);
+
+ virtual void Init();
+
+ virtual void Log(OMODFramework::LoggingLevel level, System::String^ message, System::DateTime time);
+};
diff --git a/libs/installer_omod/src/implementations/ScriptFunctions.cpp b/libs/installer_omod/src/implementations/ScriptFunctions.cpp
new file mode 100644
index 0000000..be4deb3
--- /dev/null
+++ b/libs/installer_omod/src/implementations/ScriptFunctions.cpp
@@ -0,0 +1,320 @@
+#include "ScriptFunctions.h"
+
+#include <QApplication>
+#include <QDir>
+#include <QGridLayout>
+#include <QInputDialog>
+#include <QImageReader>
+#include <QLabel>
+#include <QMessageBox>
+#include <QScreen>
+
+#include <uibase/iplugingame.h>
+#include <uibase/ipluginlist.h>
+#include <uibase/log.h>
+
+#include "../interop/QtDotNetConverters.h"
+#include "../newstuff/rtfPopup.h"
+#include "../oldstuff/DialogSelect.h"
+
+ScriptFunctionsHelper::ScriptFunctionsHelper() : mMessageBoxHelper(make_unique<MessageBoxHelper>())
+{
+ moveToThread(QApplication::instance()->thread());
+
+ connect(this, &ScriptFunctionsHelper::DialogSelectSignal, this, &ScriptFunctionsHelper::DialogSelectSlot, Qt::BlockingQueuedConnection);
+ connect(this, &ScriptFunctionsHelper::InputStringSignal, this, &ScriptFunctionsHelper::InputStringSlot, Qt::BlockingQueuedConnection);
+ connect(this, &ScriptFunctionsHelper::DisplayImageSignal, this, &ScriptFunctionsHelper::DisplayImageSlot, Qt::BlockingQueuedConnection);
+ connect(this, &ScriptFunctionsHelper::DisplayTextSignal, this, &ScriptFunctionsHelper::DisplayTextSlot, Qt::BlockingQueuedConnection);
+}
+
+std::optional<QVector<int>> ScriptFunctionsHelper::DialogSelect(QWidget* parent, const QString& title, const QVector<QString>& items, const QVector<QString>& descriptions, const QVector<QString>& pixmaps, bool multiSelect)
+{
+ std::optional<QVector<int>> result;
+ emit DialogSelectSignal(result, parent, title, items, descriptions, pixmaps, multiSelect);
+ return result;
+}
+
+QString ScriptFunctionsHelper::InputString(QWidget* parentWidget, const QString& title, const QString& initialText)
+{
+ QString text;
+ emit InputStringSignal(text, parentWidget, title, initialText);
+ return text;
+}
+
+void ScriptFunctionsHelper::DisplayImage(QWidget* parentWidget, const QString& path, const QString& title)
+{
+ emit DisplayImageSignal(parentWidget, path, title);
+}
+
+void ScriptFunctionsHelper::DisplayText(QWidget* parentWidget, const QString& path, const QString& title)
+{
+ emit DisplayTextSignal(parentWidget, path, title);
+}
+
+void ScriptFunctionsHelper::InputStringSlot(QString& textOut, QWidget* parentWidget, const QString& title, const QString& initialText)
+{
+ textOut = QInputDialog::getText(parentWidget, title, title, QLineEdit::Normal, initialText);
+}
+
+void ScriptFunctionsHelper::DisplayImageSlot(QWidget* parentWidget, const QString& path, const QString& title)
+{
+ QImageReader reader(path);
+ QImage image = reader.read();
+ if (!image.isNull())
+ {
+ QPixmap pixmap = QPixmap::fromImage(image);
+ MOBase::log::debug("image size {}, pixmap size {}", image.size(), pixmap.size());
+ QDialog popup;
+ QLayout* layout = new QGridLayout(&popup);
+ popup.setLayout(layout);
+ FixedAspectRatioImageLabel* label = new FixedAspectRatioImageLabel(&popup);
+ label->setUnscaledPixmap(pixmap);
+
+ QSize screenSize = parentWidget->screen()->availableSize();
+ int maxHeight = static_cast<int>(screenSize.height() * 0.8f);
+ if (pixmap.size().height() > maxHeight)
+ // This is approximate due to borders, label can sort out details.
+ popup.resize(label->widthForHeight(maxHeight), maxHeight);
+
+ layout->addWidget(label);
+ popup.setWindowTitle(title);
+ popup.exec();
+ }
+ else
+ MOBase::log::error("Unable to display {}. Error was {}: {}", path, reader.error(), reader.errorString());
+}
+
+void ScriptFunctionsHelper::DisplayTextSlot(QWidget* parentWidget, const QString& path, const QString& title)
+{
+ RtfPopup popup(toDotNetString(path), parentWidget);
+ popup.setWindowTitle(title);
+ // the size readmes are becoming automatically
+ popup.resize(492, 366);
+ popup.exec();
+}
+
+void ScriptFunctionsHelper::DialogSelectSlot(std::optional<QVector<int>>& resultOut, QWidget* parent, const QString& title, const QVector<QString>& items,
+ const QVector<QString>& descriptions, const QVector<QString>& pixmaps, bool multiSelect)
+{
+ resultOut = ::DialogSelect(parent, title, items, descriptions, pixmaps, multiSelect);
+}
+
+ScriptFunctions::ScriptFunctions(QWidget* parentWidget, MOBase::IOrganizer* moInfo) : mParentWidget(parentWidget), mMoInfo(moInfo), mHelper(new ScriptFunctionsHelper) {}
+
+ScriptFunctions::~ScriptFunctions()
+{
+ if (!mHelper)
+ return;
+ this->!ScriptFunctions();
+}
+
+ScriptFunctions::!ScriptFunctions()
+{
+ mHelper->deleteLater();
+ mHelper = nullptr;
+}
+
+void ScriptFunctions::Warn(System::String^ msg)
+{
+ mHelper->warning(mParentWidget, "Warning", toQString(msg));
+}
+
+void ScriptFunctions::Message(System::String^ msg)
+{
+ mHelper->information(mParentWidget, "Message", toQString(msg));
+}
+
+void ScriptFunctions::Message(System::String^ msg, System::String^ title)
+{
+ mHelper->information(mParentWidget, toQString(title), toQString(msg));
+}
+
+System::Collections::Generic::List<int>^ ScriptFunctions::Select(System::Collections::Generic::List<System::String^>^ items,
+ System::String^ title,
+ bool isMultiSelect,
+ System::Collections::Generic::List<System::String^>^ previews,
+ System::Collections::Generic::List<System::String^>^ descriptions)
+{
+ QVector<QString> qItems;
+ qItems.reserve(items ? items->Count : 0);
+ QVector<QString> qPreviews;
+ qPreviews.reserve(previews ? previews->Count : 0);
+ QVector<QString> qDescriptions;
+ qDescriptions.reserve(descriptions ? descriptions->Count : 0);
+
+ // Expect red squiggles. No one told intellisense about this syntax, but it's the least ugly.
+ if (items)
+ {
+ for each (System::String ^ item in items)
+ qItems.push_back(toQString(item));
+ }
+
+ if (previews)
+ {
+ for each (System::String ^ preview in previews)
+ qPreviews.push_back(toQString(preview));
+ }
+
+ if (descriptions)
+ {
+ for each (System::String ^ description in descriptions)
+ qDescriptions.push_back(toQString(description));
+ }
+
+ std::optional<QVector<int>> qResponse = mHelper->DialogSelect(mParentWidget, toQString(title), qItems, qDescriptions, qPreviews, isMultiSelect);
+ if (!qResponse.has_value())
+ return nullptr;
+
+ System::Collections::Generic::List<int>^ response = gcnew System::Collections::Generic::List<int>(qResponse.value().length());
+ for (const auto selection : qResponse.value())
+ response->Add(selection);
+ return response;
+}
+
+System::String^ ScriptFunctions::InputString(System::String^ title, System::String^ initialText)
+{
+ return toDotNetString(mHelper->InputString(mParentWidget, toQString(title), initialText ? toQString(initialText) : ""));
+}
+
+int ScriptFunctions::DialogYesNo(System::String^ message)
+{
+ return mHelper->question(mParentWidget, "", toQString(message)) == QMessageBox::StandardButton::Yes;
+}
+
+int ScriptFunctions::DialogYesNo(System::String^ message, System::String^ title)
+{
+ return mHelper->question(mParentWidget, toQString(title), toQString(message)) == QMessageBox::StandardButton::Yes;
+}
+
+void ScriptFunctions::DisplayImage(System::String^ path, System::String^ title)
+{
+ mHelper->DisplayImage(mParentWidget, toQString(path), toQString(title));
+}
+
+void ScriptFunctions::DisplayText(System::String^ text, System::String^ title)
+{
+ mHelper->DisplayText(mParentWidget, toQString(text), toQString(title));
+}
+
+void ScriptFunctions::Patch(System::String^ from, System::String^ to)
+{
+ throw gcnew System::NotImplementedException();
+}
+
+System::String^ ScriptFunctions::ReadOblivionINI(System::String^ section, System::String^ name)
+{
+ throw gcnew System::NotImplementedException();
+ // TODO: implement this if a user ever reports the exception. OMODFramework should be handling this for us.
+}
+
+System::String^ ScriptFunctions::ReadRendererInfo(System::String^ name)
+{
+ throw gcnew System::NotImplementedException();
+ // TODO: implement this if a user ever reports the exception. OMODFramework should be handling this for us.
+}
+
+bool ScriptFunctions::DataFileExists(System::String^ path)
+{
+ return mMoInfo->resolvePath(toQString(path)) != "";
+}
+
+bool ScriptFunctions::HasScriptExtender()
+{
+ for (const auto& forcedLoad : mMoInfo->managedGame()->executableForcedLoads())
+ {
+ if (forcedLoad.library().toLower().startsWith("obse"))
+ {
+ if (mMoInfo->managedGame()->gameDirectory().exists(forcedLoad.library()))
+ return true;
+ }
+ }
+
+ return mMoInfo->managedGame()->gameDirectory().exists("obse_loader.exe");
+}
+
+bool ScriptFunctions::HasGraphicsExtender()
+{
+ return DataFileExists("obse\\plugins\\obge.dll");
+}
+
+System::Version^ ScriptFunctions::ScriptExtenderVersion()
+{
+ QString obsePath;
+ for (const auto& forcedLoad : mMoInfo->managedGame()->executableForcedLoads())
+ {
+ if (forcedLoad.library().toLower().startsWith("obse"))
+ {
+ if (mMoInfo->managedGame()->gameDirectory().exists(forcedLoad.library()))
+ {
+ obsePath = mMoInfo->managedGame()->gameDirectory().filePath(forcedLoad.library());
+ break;
+ }
+ }
+ }
+
+ if (obsePath.isEmpty())
+ obsePath = mMoInfo->managedGame()->gameDirectory().filePath("obse_loader.exe");
+
+ System::Diagnostics::FileVersionInfo^ info = System::Diagnostics::FileVersionInfo::GetVersionInfo(toDotNetString(obsePath));
+ return gcnew System::Version(info->FileMajorPart, info->FileMinorPart, info->FileBuildPart, info->FilePrivatePart);
+}
+
+System::Version^ ScriptFunctions::GraphicsExtenderVersion()
+{
+ return gcnew System::Version(System::Diagnostics::FileVersionInfo::GetVersionInfo(toDotNetString(mMoInfo->resolvePath("obse\\plugins\\obge.dll")))->FileVersion);
+}
+
+System::Version^ ScriptFunctions::OblivionVersion()
+{
+ return gcnew System::Version(System::Diagnostics::FileVersionInfo::GetVersionInfo(toDotNetString(mMoInfo->managedGame()->gameDirectory().filePath("oblivion.exe")))->FileVersion);
+}
+
+System::Version^ ScriptFunctions::OBSEPluginVersion(System::String^ path)
+{
+ QString pluginPath = mMoInfo->resolvePath(toQString(System::IO::Path::Combine("obse", "plugins", System::IO::Path::ChangeExtension(path, ".dll"))));
+ if (pluginPath.isEmpty())
+ return nullptr;
+ return gcnew System::Version(System::Diagnostics::FileVersionInfo::GetVersionInfo(toDotNetString(pluginPath))->FileVersion);
+}
+
+System::Collections::Generic::IEnumerable<OMODFramework::Scripting::ScriptESP>^ ScriptFunctions::GetESPs()
+{
+ QStringList plugins = mMoInfo->pluginList()->pluginNames();
+ System::Collections::Generic::List<OMODFramework::Scripting::ScriptESP>^ pluginList = gcnew System::Collections::Generic::List<OMODFramework::Scripting::ScriptESP>(plugins.count());
+ for (const auto& pluginName : plugins)
+ {
+ auto state = mMoInfo->pluginList()->state(pluginName);
+ if (state != MOBase::IPluginList::PluginState::STATE_MISSING)
+ {
+ OMODFramework::Scripting::ScriptESP plugin;
+ plugin.Name = toDotNetString(pluginName);
+ plugin.Active = state == MOBase::IPluginList::PluginState::STATE_ACTIVE;
+ pluginList->Add(plugin);
+ }
+ }
+ return pluginList;
+}
+
+System::Collections::Generic::IEnumerable<System::String^>^ ScriptFunctions::GetActiveOMODNames()
+{
+ throw gcnew System::NotImplementedException();
+ // TODO: implement this if a user ever reports the exception. No known OMODs seem to actually use this (which is irritating as this is one of OBMM's most powerful features).
+}
+
+cli::array<unsigned char, 1>^ ScriptFunctions::ReadExistingDataFile(System::String^ file)
+{
+ throw gcnew System::NotImplementedException();
+ // TODO: implement this if a user ever reports the exception. OMODFramework should be handling this for us.
+}
+
+cli::array<unsigned char, 1>^ ScriptFunctions::GetDataFileFromBSA(System::String^ file)
+{
+ throw gcnew System::NotImplementedException();
+ // TODO: implement this if a user ever reports the exception. OMODFramework should be handling this for us.
+}
+
+cli::array<unsigned char, 1>^ ScriptFunctions::GetDataFileFromBSA(System::String^ bsa, System::String^ file)
+{
+ throw gcnew System::NotImplementedException();
+ // TODO: implement this if a user ever reports the exception. OMODFramework should be handling this for us.
+}
diff --git a/libs/installer_omod/src/implementations/ScriptFunctions.h b/libs/installer_omod/src/implementations/ScriptFunctions.h
new file mode 100644
index 0000000..28f9dee
--- /dev/null
+++ b/libs/installer_omod/src/implementations/ScriptFunctions.h
@@ -0,0 +1,122 @@
+#pragma once
+
+using namespace cli;
+
+#include <optional>
+
+#include <QWidget>
+
+#include <uibase/imoinfo.h>
+
+#include "../MessageBoxHelper.h"
+#include "../QObject_unique_ptr.h"
+
+class ScriptFunctionsHelper : public QObject
+{
+ Q_OBJECT
+
+public:
+ ScriptFunctionsHelper();
+
+ // don't bother with std::forward and decltype(auto) as we know everything is fine being copied
+ // in fact, as some stuff lives on the managed heap, we can't take a reference anyway
+ template<typename... Args> auto critical(Args... args) { return mMessageBoxHelper->critical(args...); }
+ template<typename... Args> auto information(Args... args) { return mMessageBoxHelper->information(args...); }
+ template<typename... Args> auto question(Args... args) { return mMessageBoxHelper->question(args...); }
+ template<typename... Args> auto warning(Args... args) { return mMessageBoxHelper->warning(args...); }
+
+ std::optional<QVector<int>> DialogSelect(QWidget* parent, const QString& title, const QVector<QString>& items,
+ const QVector<QString>& descriptions, const QVector<QString>& pixmaps,
+ bool multiSelect);
+
+ QString InputString(QWidget* parentWidget, const QString& title, const QString& initialText);
+
+ void DisplayImage(QWidget* parentWidget, const QString& path, const QString& title);
+
+ void DisplayText(QWidget* parentWidget, const QString& path, const QString& title);
+
+signals:
+ void DialogSelectSignal(std::optional<QVector<int>>& resultOut, QWidget* parent, const QString& title, const QVector<QString>& items,
+ const QVector<QString>& descriptions, const QVector<QString>& pixmaps, bool multiSelect);
+
+ void InputStringSignal(QString& textOut, QWidget* parentWidget, const QString& title, const QString& initialText);
+
+ void DisplayImageSignal(QWidget* parentWidget, const QString& path, const QString& title);
+
+ void DisplayTextSignal(QWidget* parentWidget, const QString& path, const QString& title);
+
+public slots:
+ void DialogSelectSlot(std::optional<QVector<int>>& resultOut, QWidget* parent, const QString& title, const QVector<QString>& items,
+ const QVector<QString>& descriptions, const QVector<QString>& pixmaps, bool multiSelect);
+
+ void InputStringSlot(QString& textOut, QWidget* parentWidget, const QString& title, const QString& initialText);
+
+ void DisplayImageSlot(QWidget* parentWidget, const QString& path, const QString& title);
+
+ void DisplayTextSlot(QWidget* parentWidget, const QString& path, const QString& title);
+
+private:
+ QObject_unique_ptr<MessageBoxHelper> mMessageBoxHelper;
+};
+
+ref class ScriptFunctions : OMODFramework::Scripting::IScriptFunctions
+{
+public:
+ ScriptFunctions(QWidget* parentWidget, MOBase::IOrganizer* moInfo);
+ ~ScriptFunctions();
+ !ScriptFunctions();
+
+ // note: C++/CLI wants virtual for interface implementations, not override
+ virtual void Warn(System::String^ msg);
+
+ virtual void Message(System::String^ msg);
+
+ virtual void Message(System::String^ msg, System::String^ title);
+
+ virtual System::Collections::Generic::List<int>^ Select(System::Collections::Generic::List<System::String^>^ items, System::String^ title, bool isMultiSelect, System::Collections::Generic::List<System::String^>^ previews, System::Collections::Generic::List<System::String^>^ descriptions);
+
+ virtual System::String^ InputString(System::String^ title, System::String^ initialText);
+
+ virtual int DialogYesNo(System::String^ message);
+
+ virtual int DialogYesNo(System::String^ message, System::String^ title);
+
+ virtual void DisplayImage(System::String^ path, System::String^ title);
+
+ virtual void DisplayText(System::String^ text, System::String^ title);
+
+ virtual void Patch(System::String^ from, System::String^ to);
+
+ virtual System::String^ ReadOblivionINI(System::String^ section, System::String^ name);
+
+ virtual System::String^ ReadRendererInfo(System::String^ name);
+
+ virtual bool DataFileExists(System::String^ path);
+
+ virtual bool HasScriptExtender();
+
+ virtual bool HasGraphicsExtender();
+
+ virtual System::Version^ ScriptExtenderVersion();
+
+ virtual System::Version^ GraphicsExtenderVersion();
+
+ virtual System::Version^ OblivionVersion();
+
+ virtual System::Version^ OBSEPluginVersion(System::String^ path);
+
+ virtual System::Collections::Generic::IEnumerable<OMODFramework::Scripting::ScriptESP>^ GetESPs();
+
+ virtual System::Collections::Generic::IEnumerable<System::String^>^ GetActiveOMODNames();
+
+ virtual cli::array<unsigned char, 1>^ ReadExistingDataFile(System::String^ file);
+
+ virtual cli::array<unsigned char, 1>^ GetDataFileFromBSA(System::String^ file);
+
+ virtual cli::array<unsigned char, 1>^ GetDataFileFromBSA(System::String^ bsa, System::String^ file);
+
+private:
+ QWidget* mParentWidget;
+ MOBase::IOrganizer* mMoInfo;
+ ScriptFunctionsHelper* mHelper;
+};