summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/mainwindow.cpp4
-rw-r--r--src/organizercore.cpp192
-rw-r--r--src/organizercore.h9
-rw-r--r--src/plugincontainer.cpp1
-rw-r--r--src/spawn.cpp131
-rw-r--r--src/spawn.h34
6 files changed, 230 insertions, 141 deletions
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index 12ed40b3..b5b9ab0d 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -411,7 +411,7 @@ MainWindow::MainWindow(Settings &settings
m_Tutorial.expose("modList", m_OrganizerCore.modList());
m_Tutorial.expose("espList", m_OrganizerCore.pluginList());
- m_OrganizerCore.setUserInterface(this, this);
+ m_OrganizerCore.setUserInterface(this);
for (const QString &fileName : m_PluginContainer.pluginFileNames()) {
installTranslator(QFileInfo(fileName).baseName());
}
@@ -595,7 +595,7 @@ MainWindow::~MainWindow()
cleanup();
m_PluginContainer.setUserInterface(nullptr, nullptr);
- m_OrganizerCore.setUserInterface(nullptr, nullptr);
+ m_OrganizerCore.setUserInterface(nullptr);
m_IntegratedBrowser.close();
delete ui;
} catch (std::exception &e) {
diff --git a/src/organizercore.cpp b/src/organizercore.cpp
index 5613e8ce..3c986004 100644
--- a/src/organizercore.cpp
+++ b/src/organizercore.cpp
@@ -1,5 +1,5 @@
#include "organizercore.h"
-
+#include "mainwindow.h"
#include "delayedfilewriter.h"
#include "guessedvalue.h"
#include "imodinterface.h"
@@ -127,7 +127,7 @@ QStringList toStringList(InputIterator current, InputIterator end)
OrganizerCore::OrganizerCore(Settings &settings)
- : m_UserInterface(nullptr)
+ : m_MainWindow(nullptr)
, m_PluginContainer(nullptr)
, m_GameName()
, m_CurrentProfile(nullptr)
@@ -249,44 +249,43 @@ void OrganizerCore::updateExecutablesList()
m_PluginContainer, m_Settings.interface().displayForeign(), managedGame());
}
-void OrganizerCore::setUserInterface(IUserInterface *userInterface,
- QWidget *widget)
+void OrganizerCore::setUserInterface(MainWindow* mainWindow)
{
storeSettings();
- m_UserInterface = userInterface;
+ m_MainWindow = mainWindow;
- if (widget != nullptr) {
- connect(&m_ModList, SIGNAL(modlistChanged(QModelIndex, int)), widget,
+ if (m_MainWindow != nullptr) {
+ connect(&m_ModList, SIGNAL(modlistChanged(QModelIndex, int)), m_MainWindow,
SLOT(modlistChanged(QModelIndex, int)));
- connect(&m_ModList, SIGNAL(modlistChanged(QModelIndexList, int)), widget,
+ connect(&m_ModList, SIGNAL(modlistChanged(QModelIndexList, int)), m_MainWindow,
SLOT(modlistChanged(QModelIndexList, int)));
- connect(&m_ModList, SIGNAL(showMessage(QString)), widget,
+ connect(&m_ModList, SIGNAL(showMessage(QString)), m_MainWindow,
SLOT(showMessage(QString)));
- connect(&m_ModList, SIGNAL(modRenamed(QString, QString)), widget,
+ connect(&m_ModList, SIGNAL(modRenamed(QString, QString)), m_MainWindow,
SLOT(modRenamed(QString, QString)));
- connect(&m_ModList, SIGNAL(modUninstalled(QString)), widget,
+ connect(&m_ModList, SIGNAL(modUninstalled(QString)), m_MainWindow,
SLOT(modRemoved(QString)));
- connect(&m_ModList, SIGNAL(removeSelectedMods()), widget,
+ connect(&m_ModList, SIGNAL(removeSelectedMods()), m_MainWindow,
SLOT(removeMod_clicked()));
- connect(&m_ModList, SIGNAL(clearOverwrite()), widget,
+ connect(&m_ModList, SIGNAL(clearOverwrite()), m_MainWindow,
SLOT(clearOverwrite()));
- connect(&m_ModList, SIGNAL(requestColumnSelect(QPoint)), widget,
+ connect(&m_ModList, SIGNAL(requestColumnSelect(QPoint)), m_MainWindow,
SLOT(displayColumnSelection(QPoint)));
- connect(&m_ModList, SIGNAL(fileMoved(QString, QString, QString)), widget,
+ connect(&m_ModList, SIGNAL(fileMoved(QString, QString, QString)), m_MainWindow,
SLOT(fileMoved(QString, QString, QString)));
- connect(&m_ModList, SIGNAL(modorder_changed()), widget,
+ connect(&m_ModList, SIGNAL(modorder_changed()), m_MainWindow,
SLOT(modorder_changed()));
- connect(&m_PluginList, SIGNAL(writePluginsList()), widget,
+ connect(&m_PluginList, SIGNAL(writePluginsList()), m_MainWindow,
SLOT(esplist_changed()));
- connect(&m_PluginList, SIGNAL(esplist_changed()), widget,
+ connect(&m_PluginList, SIGNAL(esplist_changed()), m_MainWindow,
SLOT(esplist_changed()));
- connect(&m_DownloadManager, SIGNAL(showMessage(QString)), widget,
+ connect(&m_DownloadManager, SIGNAL(showMessage(QString)), m_MainWindow,
SLOT(showMessage(QString)));
}
- m_InstallationManager.setParentWidget(widget);
- m_Updater.setUserInterface(widget);
+ m_InstallationManager.setParentWidget(m_MainWindow);
+ m_Updater.setUserInterface(m_MainWindow);
checkForUpdates();
}
@@ -295,7 +294,7 @@ void OrganizerCore::checkForUpdates()
{
// this currently wouldn't work reliably if the ui isn't initialized yet to
// display the result
- if (m_UserInterface != nullptr) {
+ if (m_MainWindow != nullptr) {
m_Updater.testForUpdate(m_Settings);
}
}
@@ -755,13 +754,13 @@ MOBase::IModInterface *OrganizerCore::installMod(const QString &fileName,
int modIndex = ModInfo::getIndex(modName);
if (modIndex != UINT_MAX) {
ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex);
- if (hasIniTweaks && (m_UserInterface != nullptr)
+ if (hasIniTweaks && (m_MainWindow != nullptr)
&& (QMessageBox::question(qApp->activeWindow(), tr("Configure Mod"),
tr("This mod contains ini tweaks. Do you "
"want to configure them now?"),
QMessageBox::Yes | QMessageBox::No)
== QMessageBox::Yes)) {
- m_UserInterface->displayModInformation(
+ m_MainWindow->displayModInformation(
modInfo, modIndex, ModInfoTabIDs::IniFiles);
}
m_ModInstalled(modName);
@@ -822,13 +821,13 @@ void OrganizerCore::installDownload(int index)
ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex);
modInfo->addInstalledFile(modID, fileID);
- if (hasIniTweaks && m_UserInterface != nullptr
+ if (hasIniTweaks && m_MainWindow != nullptr
&& (QMessageBox::question(qApp->activeWindow(), tr("Configure Mod"),
tr("This mod contains ini tweaks. Do you "
"want to configure them now?"),
QMessageBox::Yes | QMessageBox::No)
== QMessageBox::Yes)) {
- m_UserInterface->displayModInformation(
+ m_MainWindow->displayModInformation(
modInfo, modIndex, ModInfoTabIDs::IniFiles);
}
@@ -1250,11 +1249,11 @@ HANDLE OrganizerCore::spawnBinaryDirect(const QFileInfo &binary,
std::unique_ptr<LockedDialog> dlg;
ILockedWaitingForProcess* uilock = nullptr;
- if (m_UserInterface != nullptr) {
- uilock = m_UserInterface->lock();
+ if (m_MainWindow != nullptr) {
+ uilock = m_MainWindow->lock();
}
else {
- // i.e. when running command line shortcuts there is no m_UserInterface
+ // i.e. when running command line shortcuts there is no user interface
dlg.reset(new LockedDialog);
dlg->show();
dlg->setEnabled(true);
@@ -1262,8 +1261,8 @@ HANDLE OrganizerCore::spawnBinaryDirect(const QFileInfo &binary,
}
ON_BLOCK_EXIT([&]() {
- if (m_UserInterface != nullptr) {
- m_UserInterface->unlock();
+ if (m_MainWindow != nullptr) {
+ m_MainWindow->unlock();
} });
DWORD ignoreExitCode;
@@ -1275,35 +1274,21 @@ HANDLE OrganizerCore::spawnBinaryDirect(const QFileInfo &binary,
}
-HANDLE OrganizerCore::spawnBinaryProcess(const QFileInfo &binary,
- const QString &arguments,
- const QString &profileName,
- const QDir &currentDirectory,
- const QString &steamAppID,
- const QString &customOverwrite,
- const QList<MOBase::ExecutableForcedLoadSetting> &forcedLibraries)
+HANDLE OrganizerCore::spawnBinaryProcess(
+ const QFileInfo &binary, const QString &arguments, const QString &profileName,
+ const QDir &currentDirectory, const QString &steamAppID,
+ const QString &customOverwrite,
+ const QList<MOBase::ExecutableForcedLoadSetting> &forcedLibraries)
{
spawn::SpawnParameters sp;
sp.binary = binary;
sp.arguments = arguments;
sp.currentDirectory = currentDirectory;
+ sp.steamAppID = steamAppID;
sp.hooked = true;
prepareStart();
- QWidget *window = qApp->activeWindow();
- if ((window != nullptr) && (!window->isVisible())) {
- window = nullptr;
- }
-
- if (!spawn::checkBinary(window, sp)) {
- return INVALID_HANDLE_VALUE;
- }
-
- if (!spawn::checkSteam(window, sp, managedGame()->gameDirectory(), steamAppID, m_Settings)) {
- return INVALID_HANDLE_VALUE;
- }
-
while (m_DirectoryUpdate) {
::Sleep(100);
QCoreApplication::processEvents();
@@ -1315,72 +1300,25 @@ HANDLE OrganizerCore::spawnBinaryProcess(const QFileInfo &binary,
}
// TODO: should also pass arguments
- if (m_AboutToRun(binary.absoluteFilePath())) {
- try {
- m_USVFS.updateMapping(fileMapping(profileName, customOverwrite));
- m_USVFS.updateForcedLibraries(forcedLibraries);
-
- } catch (const UsvfsConnectorException &e) {
- log::debug(e.what());
- return INVALID_HANDLE_VALUE;
- } catch (const std::exception &e) {
- QMessageBox::warning(window, tr("Error"), e.what());
- return INVALID_HANDLE_VALUE;
- }
-
- if (!spawn::checkEnvironment(window, sp)) {
- return INVALID_HANDLE_VALUE;
- }
-
- if (!spawn::checkBlacklist(window, sp, m_Settings)) {
- return INVALID_HANDLE_VALUE;
- }
-
- QString modsPath = settings().paths().mods();
-
- // Check if this a request with either an executable or a working directory under our mods folder
- // then will start the process in a virtualized "environment" with the appropriate paths fixed:
- // (i.e. mods\FNIS\path\exe => game\data\path\exe)
- QString cwdPath = currentDirectory.absolutePath();
- bool virtualizedCwd = cwdPath.startsWith(modsPath, Qt::CaseInsensitive);
- QString binPath = binary.absoluteFilePath();
- bool virtualizedBin = binPath.startsWith(modsPath, Qt::CaseInsensitive);
- if (virtualizedCwd || virtualizedBin) {
- if (virtualizedCwd) {
- int cwdOffset = cwdPath.indexOf('/', modsPath.length() + 1);
- QString adjustedCwd = cwdPath.mid(cwdOffset, -1);
- cwdPath = m_GamePlugin->dataDirectory().absolutePath();
- if (cwdOffset >= 0)
- cwdPath += adjustedCwd;
-
- }
-
- if (virtualizedBin) {
- int binOffset = binPath.indexOf('/', modsPath.length() + 1);
- QString adjustedBin = binPath.mid(binOffset, -1);
- binPath = m_GamePlugin->dataDirectory().absolutePath();
- if (binOffset >= 0)
- binPath += adjustedBin;
- }
-
- QString cmdline
- = QString("launch \"%1\" \"%2\" %3")
- .arg(QDir::toNativeSeparators(cwdPath),
- QDir::toNativeSeparators(binPath), arguments);
+ if (!m_AboutToRun(binary.absoluteFilePath())) {
+ log::debug("start of \"{}\" canceled by plugin", binary.absoluteFilePath());
+ return INVALID_HANDLE_VALUE;
+ }
- sp.binary = QFileInfo(QCoreApplication::applicationFilePath());
- sp.arguments = cmdline;
- sp.currentDirectory.setPath(QCoreApplication::applicationDirPath());
+ try {
+ m_USVFS.updateMapping(fileMapping(profileName, customOverwrite));
+ m_USVFS.updateForcedLibraries(forcedLibraries);
- return spawn::startBinary(window, sp);
- } else {
- log::debug("Spawning direct process <{}, {}, {}>", binPath, arguments, cwdPath);
- return spawn::startBinary(window, sp);
- }
- } else {
- log::debug("start of \"{}\" canceled by plugin", binary.absoluteFilePath());
+ } catch (const UsvfsConnectorException &e) {
+ log::debug(e.what());
+ return INVALID_HANDLE_VALUE;
+ } catch (const std::exception &e) {
+ QMessageBox::warning(m_MainWindow, tr("Error"), e.what());
return INVALID_HANDLE_VALUE;
}
+
+ auto process = spawn::Spawner().spawn(m_MainWindow, m_GamePlugin, sp, m_Settings);
+ return process.releaseHandle();
}
HANDLE OrganizerCore::runShortcut(const MOShortcut& shortcut)
@@ -1498,13 +1436,13 @@ bool OrganizerCore::waitForApplication(HANDLE handle, LPDWORD exitCode)
return true;
ILockedWaitingForProcess* uilock = nullptr;
- if (m_UserInterface != nullptr) {
- uilock = m_UserInterface->lock();
+ if (m_MainWindow != nullptr) {
+ uilock = m_MainWindow->lock();
}
ON_BLOCK_EXIT([&] () {
- if (m_UserInterface != nullptr) {
- m_UserInterface->unlock();
+ if (m_MainWindow != nullptr) {
+ m_MainWindow->unlock();
} });
return waitForProcessCompletion(handle, exitCode, uilock);
}
@@ -1762,8 +1700,8 @@ void OrganizerCore::refreshBSAList()
m_ActiveArchives = m_DefaultArchives;
}
- if (m_UserInterface != nullptr) {
- m_UserInterface->updateBSAList(m_DefaultArchives, m_ActiveArchives);
+ if (m_MainWindow != nullptr) {
+ m_MainWindow->updateBSAList(m_DefaultArchives, m_ActiveArchives);
}
m_ArchivesInit = true;
@@ -1879,8 +1817,8 @@ void OrganizerCore::updateModsInDirectoryStructure(QMap<unsigned int, ModInfo::P
// now we need to refresh the bsa list and save it so there is no confusion
// about what archives are available and active
refreshBSAList();
- if (m_UserInterface != nullptr) {
- m_UserInterface->archivesWriter().writeImmediately(false);
+ if (m_MainWindow != nullptr) {
+ m_MainWindow->archivesWriter().writeImmediately(false);
}
std::vector<QString> archives = enabledArchives();
@@ -2064,8 +2002,8 @@ void OrganizerCore::modStatusChanged(unsigned int index)
= m_DirectoryStructure->getOriginByName(ToWString(modInfo->name()));
origin.enable(false);
}
- if (m_UserInterface != nullptr) {
- m_UserInterface->archivesWriter().write();
+ if (m_MainWindow != nullptr) {
+ m_MainWindow->archivesWriter().write();
}
}
modInfo->clearCaches();
@@ -2114,8 +2052,8 @@ void OrganizerCore::modStatusChanged(QList<unsigned int> index) {
origin.enable(false);
}
}
- if (m_UserInterface != nullptr) {
- m_UserInterface->archivesWriter().write();
+ if (m_MainWindow != nullptr) {
+ m_MainWindow->archivesWriter().write();
}
}
@@ -2290,8 +2228,8 @@ bool OrganizerCore::saveCurrentLists()
try {
savePluginList();
- if (m_UserInterface != nullptr) {
- m_UserInterface->archivesWriter().write();
+ if (m_MainWindow != nullptr) {
+ m_MainWindow->archivesWriter().write();
}
} catch (const std::exception &e) {
reportError(tr("failed to save load order: %1").arg(e.what()));
diff --git a/src/organizercore.h b/src/organizercore.h
index 0d0a092c..04c96ba6 100644
--- a/src/organizercore.h
+++ b/src/organizercore.h
@@ -3,7 +3,7 @@
#include "selfupdater.h"
-#include "iuserinterface.h" //should be class IUserInterface;
+#include "ilockedwaitingforprocess.h"
#include "settings.h"
#include "modlist.h"
#include "modinfo.h"
@@ -26,6 +26,8 @@
class ModListSortProxy;
class PluginListSortProxy;
class Profile;
+class MainWindow;
+
namespace MOBase {
template <typename T> class GuessedValue;
class IModInterface;
@@ -101,7 +103,7 @@ public:
~OrganizerCore();
- void setUserInterface(IUserInterface *userInterface, QWidget *widget);
+ void setUserInterface(MainWindow* mainWindow);
void connectPlugins(PluginContainer *container);
void disconnectPlugins();
@@ -328,8 +330,7 @@ private:
static const unsigned int PROBLEM_MO1SCRIPTEXTENDERWORKAROUND = 1;
private:
-
- IUserInterface *m_UserInterface;
+ MainWindow* m_MainWindow;
PluginContainer *m_PluginContainer;
QString m_GameName;
MOBase::IPluginGame *m_GamePlugin;
diff --git a/src/plugincontainer.cpp b/src/plugincontainer.cpp
index c0706ba8..767d3eb8 100644
--- a/src/plugincontainer.cpp
+++ b/src/plugincontainer.cpp
@@ -3,6 +3,7 @@
#include "organizerproxy.h"
#include "report.h"
#include <ipluginproxy.h>
+#include <iuserinterface.h>
#include <idownloadmanager.h>
#include <appconfig.h>
#include <QAction>
diff --git a/src/spawn.cpp b/src/spawn.cpp
index a34230b2..3c7d64ce 100644
--- a/src/spawn.cpp
+++ b/src/spawn.cpp
@@ -27,6 +27,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "envmodule.h"
#include "settings.h"
#include "settingsdialogworkarounds.h"
+#include <iplugingame.h>
#include <errorcodes.h>
#include <report.h>
#include <log.h>
@@ -440,7 +441,7 @@ QMessageBox::StandardButton confirmBlacklisted(
namespace spawn
{
-DWORD spawn(const SpawnParameters& sp, HANDLE& processHandle, HANDLE& threadHandle)
+DWORD spawn(const SpawnParameters& sp, HANDLE& processHandle)
{
BOOL inheritHandles = FALSE;
@@ -494,7 +495,7 @@ DWORD spawn(const SpawnParameters& sp, HANDLE& processHandle, HANDLE& threadHand
}
processHandle = pi.hProcess;
- threadHandle = pi.hThread;
+ ::CloseHandle(pi.hThread);
return ERROR_SUCCESS;
}
@@ -618,8 +619,7 @@ bool startSteam(QWidget* parent)
(password.isEmpty() ? "no" : "yes"));
HANDLE ph = INVALID_HANDLE_VALUE;
- HANDLE th = INVALID_HANDLE_VALUE;
- const auto e = spawn(sp, ph, th);
+ const auto e = spawn(sp, ph);
if (e != ERROR_SUCCESS) {
// make sure username and passwords are not shown
@@ -772,17 +772,59 @@ bool checkBlacklist(
}
+void adjustForVirtualized(
+ const IPluginGame* game, SpawnParameters& sp, const Settings& settings)
+{
+ const QString modsPath = settings.paths().mods();
+
+ // Check if this a request with either an executable or a working directory
+ // under our mods folder then will start the process in a virtualized
+ // "environment" with the appropriate paths fixed:
+ // (i.e. mods\FNIS\path\exe => game\data\path\exe)
+ QString cwdPath = sp.currentDirectory.absolutePath();
+ bool virtualizedCwd = cwdPath.startsWith(modsPath, Qt::CaseInsensitive);
+ QString binPath = sp.binary.absoluteFilePath();
+ bool virtualizedBin = binPath.startsWith(modsPath, Qt::CaseInsensitive);
+ if (virtualizedCwd || virtualizedBin) {
+ if (virtualizedCwd) {
+ int cwdOffset = cwdPath.indexOf('/', modsPath.length() + 1);
+ QString adjustedCwd = cwdPath.mid(cwdOffset, -1);
+ cwdPath = game->dataDirectory().absolutePath();
+ if (cwdOffset >= 0)
+ cwdPath += adjustedCwd;
+
+ }
+
+ if (virtualizedBin) {
+ int binOffset = binPath.indexOf('/', modsPath.length() + 1);
+ QString adjustedBin = binPath.mid(binOffset, -1);
+ binPath = game->dataDirectory().absolutePath();
+ if (binOffset >= 0)
+ binPath += adjustedBin;
+ }
+
+ QString cmdline
+ = QString("launch \"%1\" \"%2\" %3")
+ .arg(QDir::toNativeSeparators(cwdPath),
+ QDir::toNativeSeparators(binPath), sp.arguments);
+
+ sp.binary = QFileInfo(QCoreApplication::applicationFilePath());
+ sp.arguments = cmdline;
+ sp.currentDirectory.setPath(QCoreApplication::applicationDirPath());
+ }
+}
+
+
HANDLE startBinary(QWidget* parent, const SpawnParameters& sp)
{
- HANDLE processHandle, threadHandle;
- const auto e = spawn(sp, processHandle, threadHandle);
+ HANDLE handle = INVALID_HANDLE_VALUE;
+ const auto e = spawn::spawn(sp, handle);
switch (e)
{
case ERROR_SUCCESS:
{
- ::CloseHandle(threadHandle);
- return processHandle;
+ return handle;
}
case ERROR_ELEVATION_REQUIRED:
@@ -799,6 +841,79 @@ HANDLE startBinary(QWidget* parent, const SpawnParameters& sp)
}
}
+
+
+SpawnedProcess::SpawnedProcess(HANDLE handle, SpawnParameters sp)
+ : m_handle(handle), m_parameters(std::move(sp))
+{
+}
+
+SpawnedProcess::SpawnedProcess(SpawnedProcess&& other)
+ : m_handle(other.m_handle), m_parameters(std::move(other.m_parameters))
+{
+ other.m_handle = INVALID_HANDLE_VALUE;
+}
+
+SpawnedProcess& SpawnedProcess::operator=(SpawnedProcess&& other)
+{
+ if (this != &other) {
+ destroy();
+
+ m_handle = other.m_handle;
+ other.m_handle = INVALID_HANDLE_VALUE;
+
+ m_parameters = std::move(other.m_parameters);
+ }
+
+ return *this;
+}
+
+SpawnedProcess::~SpawnedProcess()
+{
+ destroy();
+}
+
+HANDLE SpawnedProcess::releaseHandle()
+{
+ const auto h = m_handle;
+ m_handle = INVALID_HANDLE_VALUE;
+ return h;
+}
+
+void SpawnedProcess::destroy()
+{
+ if (m_handle != INVALID_HANDLE_VALUE) {
+ ::CloseHandle(m_handle);
+ m_handle = INVALID_HANDLE_VALUE;
+ }
+}
+
+
+SpawnedProcess Spawner::spawn(
+ QWidget* parent, const IPluginGame* game,
+ SpawnParameters sp, Settings& settings)
+{
+ if (!checkBinary(parent, sp)) {
+ return {INVALID_HANDLE_VALUE, sp};
+ }
+
+ if (!checkSteam(parent, sp, game->gameDirectory(), sp.steamAppID, settings)) {
+ return {INVALID_HANDLE_VALUE, sp};
+ }
+
+ if (!spawn::checkEnvironment(parent, sp)) {
+ return {INVALID_HANDLE_VALUE, sp};
+ }
+
+ if (!spawn::checkBlacklist(parent, sp, settings)) {
+ return {INVALID_HANDLE_VALUE, sp};
+ }
+
+ adjustForVirtualized(game, sp, settings);
+
+ return {startBinary(parent, sp), sp};
+}
+
} // namespace
diff --git a/src/spawn.h b/src/spawn.h
index 31b44739..d2853cd5 100644
--- a/src/spawn.h
+++ b/src/spawn.h
@@ -27,6 +27,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <QDir>
class Settings;
+namespace MOBase { class IPluginGame; }
namespace spawn
{
@@ -46,6 +47,7 @@ struct SpawnParameters
QFileInfo binary;
QString arguments;
QDir currentDirectory;
+ QString steamAppID;
bool hooked = false;
HANDLE stdOut = INVALID_HANDLE_VALUE;
HANDLE stdErr = INVALID_HANDLE_VALUE;
@@ -69,6 +71,38 @@ bool checkBlacklist(
**/
HANDLE startBinary(QWidget* parent, const SpawnParameters& sp);
+
+class SpawnedProcess
+{
+public:
+ SpawnedProcess(HANDLE handle, SpawnParameters sp);
+
+ SpawnedProcess(const SpawnedProcess&) = delete;
+ SpawnedProcess& operator=(const SpawnedProcess&) = delete;
+ SpawnedProcess(SpawnedProcess&& other);
+ SpawnedProcess& operator=(SpawnedProcess&& other);
+ ~SpawnedProcess();
+
+ HANDLE releaseHandle();
+
+private:
+ HANDLE m_handle;
+ SpawnParameters m_parameters;
+
+ void destroy();
+};
+
+
+class Spawner
+{
+public:
+ SpawnedProcess spawn(
+ QWidget* parent, const MOBase::IPluginGame* game,
+ SpawnParameters sp, Settings& settings);
+
+private:
+};
+
} // namespace