From 5e5c9c07291f6b09623d31c92b1fb61c4ede576e Mon Sep 17 00:00:00 2001 From: Sandro Jäckel Date: Thu, 22 Feb 2018 16:54:34 +0100 Subject: Applied clang-format on source --- src/organizercore.cpp | 3491 ++++++++++++++++++++++--------------------------- 1 file changed, 1598 insertions(+), 1893 deletions(-) (limited to 'src/organizercore.cpp') diff --git a/src/organizercore.cpp b/src/organizercore.cpp index b84488da..4b6567c5 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -1,39 +1,39 @@ #include "organizercore.h" +#include "appconfig.h" +#include "credentialsdialog.h" #include "delayedfilewriter.h" +#include "filedialogmemory.h" #include "guessedvalue.h" #include "imodinterface.h" #include "imoinfo.h" +#include "instancemanager.h" #include "iplugingame.h" #include "iuserinterface.h" #include "loadmechanism.h" +#include "lockeddialog.h" +#include "logbuffer.h" #include "messagedialog.h" +#include "modinfodialog.h" #include "modlistsortproxy.h" #include "modrepositoryfileinfo.h" #include "nexusinterface.h" +#include "nxmaccessmanager.h" #include "plugincontainer.h" #include "pluginlistsortproxy.h" #include "profile.h" -#include "logbuffer.h" -#include "credentialsdialog.h" -#include "filedialogmemory.h" -#include "modinfodialog.h" #include "spawn.h" #include "syncoverwritedialog.h" -#include "nxmaccessmanager.h" -#include #include -#include #include -#include -#include -#include -#include "appconfig.h" -#include +#include +#include #include -#include "lockeddialog.h" -#include "instancemanager.h" +#include +#include #include +#include +#include #include #include @@ -51,2129 +51,1834 @@ #include #include -#include #include // for _tcsicmp +#include #include #include #include // for memset, wcsrchr +#include #include #include -#include #include #include #include //for wstring #include #include - using namespace MOShared; using namespace MOBase; -//static +// static CrashDumpsType OrganizerCore::m_globalCrashDumpsType = CrashDumpsType::None; -static bool isOnline() -{ - QList interfaces = QNetworkInterface::allInterfaces(); - - bool connected = false; - for (auto iter = interfaces.begin(); iter != interfaces.end() && !connected; - ++iter) { - if ((iter->flags() & QNetworkInterface::IsUp) - && (iter->flags() & QNetworkInterface::IsRunning) - && !(iter->flags() & QNetworkInterface::IsLoopBack)) { - auto addresses = iter->addressEntries(); - if (addresses.count() == 0) { - continue; - } - qDebug("interface %s seems to be up (address: %s)", - qPrintable(iter->humanReadableName()), - qPrintable(addresses[0].ip().toString())); - connected = true; +static bool isOnline() { + QList interfaces = QNetworkInterface::allInterfaces(); + + bool connected = false; + for (auto iter = interfaces.begin(); iter != interfaces.end() && !connected; ++iter) { + if ((iter->flags() & QNetworkInterface::IsUp) && (iter->flags() & QNetworkInterface::IsRunning) && + !(iter->flags() & QNetworkInterface::IsLoopBack)) { + auto addresses = iter->addressEntries(); + if (addresses.count() == 0) { + continue; + } + qDebug("interface %s seems to be up (address: %s)", qPrintable(iter->humanReadableName()), + qPrintable(addresses[0].ip().toString())); + connected = true; + } } - } - return connected; + return connected; } -static bool renameFile(const QString &oldName, const QString &newName, - bool overwrite = true) -{ - if (overwrite && QFile::exists(newName)) { - QFile::remove(newName); - } - return QFile::rename(oldName, newName); +static bool renameFile(const QString& oldName, const QString& newName, bool overwrite = true) { + if (overwrite && QFile::exists(newName)) { + QFile::remove(newName); + } + return QFile::rename(oldName, newName); } -static std::wstring getProcessName(HANDLE process) -{ - wchar_t buffer[MAX_PATH]; - wchar_t *fileName = L"unknown"; +static std::wstring getProcessName(HANDLE process) { + wchar_t buffer[MAX_PATH]; + wchar_t* fileName = L"unknown"; - if (process == nullptr) return fileName; + if (process == nullptr) + return fileName; - if (::GetProcessImageFileNameW(process, buffer, MAX_PATH) != 0) { - fileName = wcsrchr(buffer, L'\\'); - if (fileName == nullptr) { - fileName = buffer; - } - else { - fileName += 1; - } - } + if (::GetProcessImageFileNameW(process, buffer, MAX_PATH) != 0) { + fileName = wcsrchr(buffer, L'\\'); + if (fileName == nullptr) { + fileName = buffer; + } else { + fileName += 1; + } + } - return fileName; + return fileName; } // Get parent PID for the given process, return 0 on failure -static DWORD getProcessParentID(DWORD pid) -{ - HANDLE th = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); - PROCESSENTRY32 pe = { 0 }; - pe.dwSize = sizeof(PROCESSENTRY32); - - DWORD res = 0; - if (Process32First(th, &pe)) - do { - if (pe.th32ProcessID == pid) { - res = pe.th32ParentProcessID; - break; - } - } while (Process32Next(th, &pe)); - - CloseHandle(th); - - return res; -} - -static void startSteam(QWidget *widget) -{ - QSettings steamSettings("HKEY_CURRENT_USER\\Software\\Valve\\Steam", - QSettings::NativeFormat); - QString exe = steamSettings.value("SteamExe", "").toString(); - if (!exe.isEmpty()) { - exe = QString("\"%1\"").arg(exe); - // See if username and password supplied. If so, pass them into steam. - QStringList args; - QString username; - QString password; - if (Settings::instance().getSteamLogin(username, password)) { - args << "-login"; - args << username; - if (password != "") { - args << password; - } - } - if (!QProcess::startDetached(exe, args)) { - reportError(QObject::tr("Failed to start \"%1\"").arg(exe)); - } else { - QMessageBox::information( - widget, QObject::tr("Waiting"), - QObject::tr("Please press OK once you're logged into steam.")); +static DWORD getProcessParentID(DWORD pid) { + HANDLE th = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + PROCESSENTRY32 pe = {0}; + pe.dwSize = sizeof(PROCESSENTRY32); + + DWORD res = 0; + if (Process32First(th, &pe)) + do { + if (pe.th32ProcessID == pid) { + res = pe.th32ParentProcessID; + break; + } + } while (Process32Next(th, &pe)); + + CloseHandle(th); + + return res; +} + +static void startSteam(QWidget* widget) { + QSettings steamSettings("HKEY_CURRENT_USER\\Software\\Valve\\Steam", QSettings::NativeFormat); + QString exe = steamSettings.value("SteamExe", "").toString(); + if (!exe.isEmpty()) { + exe = QString("\"%1\"").arg(exe); + // See if username and password supplied. If so, pass them into steam. + QStringList args; + QString username; + QString password; + if (Settings::instance().getSteamLogin(username, password)) { + args << "-login"; + args << username; + if (password != "") { + args << password; + } + } + if (!QProcess::startDetached(exe, args)) { + reportError(QObject::tr("Failed to start \"%1\"").arg(exe)); + } else { + QMessageBox::information(widget, QObject::tr("Waiting"), + QObject::tr("Please press OK once you're logged into steam.")); + } } - } } template -QStringList toStringList(InputIterator current, InputIterator end) -{ - QStringList result; - for (; current != end; ++current) { - result.append(*current); - } - return result; -} - -OrganizerCore::OrganizerCore(const QSettings &initSettings) - : m_UserInterface(nullptr) - , m_PluginContainer(nullptr) - , m_GameName() - , m_CurrentProfile(nullptr) - , m_Settings(initSettings) - , m_Updater(NexusInterface::instance()) - , m_AboutToRun() - , m_FinishedRun() - , m_ModInstalled() - , m_ModList(this) - , m_PluginList(this) - , m_DirectoryRefresher() - , m_DirectoryStructure(new DirectoryEntry(L"data", nullptr, 0)) - , m_DownloadManager(NexusInterface::instance(), this) - , m_InstallationManager() - , m_RefresherThread() - , m_AskForNexusPW(false) - , m_DirectoryUpdate(false) - , m_ArchivesInit(false) - , m_PluginListsWriter(std::bind(&OrganizerCore::savePluginList, this)) -{ - m_DownloadManager.setOutputDirectory(m_Settings.getDownloadDirectory()); - m_DownloadManager.setPreferredServers(m_Settings.getPreferredServers()); - - NexusInterface::instance()->setCacheDirectory(m_Settings.getCacheDirectory()); - NexusInterface::instance()->setNMMVersion(m_Settings.getNMMVersion()); - - MOBase::QuestionBoxMemory::init(initSettings.fileName()); - - m_InstallationManager.setModsDirectory(m_Settings.getModDirectory()); - m_InstallationManager.setDownloadDirectory(m_Settings.getDownloadDirectory()); - - connect(&m_DownloadManager, SIGNAL(downloadSpeed(QString, int)), this, - SLOT(downloadSpeed(QString, int))); - connect(&m_DirectoryRefresher, SIGNAL(refreshed()), this, - SLOT(directory_refreshed())); - - connect(&m_ModList, SIGNAL(removeOrigin(QString)), this, - SLOT(removeOrigin(QString))); - - connect(NexusInterface::instance()->getAccessManager(), - SIGNAL(loginSuccessful(bool)), this, SLOT(loginSuccessful(bool))); - connect(NexusInterface::instance()->getAccessManager(), - SIGNAL(loginFailed(QString)), this, SLOT(loginFailed(QString))); - - // This seems awfully imperative - connect(this, SIGNAL(managedGameChanged(MOBase::IPluginGame const *)), - &m_Settings, SLOT(managedGameChanged(MOBase::IPluginGame const *))); - connect(this, SIGNAL(managedGameChanged(MOBase::IPluginGame const *)), - &m_DownloadManager, - SLOT(managedGameChanged(MOBase::IPluginGame const *))); - connect(this, SIGNAL(managedGameChanged(MOBase::IPluginGame const *)), - &m_PluginList, SLOT(managedGameChanged(MOBase::IPluginGame const *))); - connect(this, SIGNAL(managedGameChanged(MOBase::IPluginGame const *)), - NexusInterface::instance(), - SLOT(managedGameChanged(MOBase::IPluginGame const *))); - - connect(&m_PluginList, &PluginList::writePluginsList, &m_PluginListsWriter, - &DelayedFileWriterBase::write); - - // make directory refresher run in a separate thread - m_RefresherThread.start(); - m_DirectoryRefresher.moveToThread(&m_RefresherThread); - - m_AskForNexusPW = initSettings.value("ask_for_nexuspw", true).toBool(); -} - -OrganizerCore::~OrganizerCore() -{ - m_RefresherThread.exit(); - m_RefresherThread.wait(); - - prepareStart(); - - // profile has to be cleaned up before the modinfo-buffer is cleared - delete m_CurrentProfile; - m_CurrentProfile = nullptr; - - ModInfo::clear(); - LogBuffer::cleanQuit(); - m_ModList.setProfile(nullptr); - // NexusInterface::instance()->cleanup(); - - delete m_DirectoryStructure; -} - -QString OrganizerCore::commitSettings(const QString &iniFile) -{ - if (!shellRename(iniFile + ".new", iniFile, true, qApp->activeWindow())) { - DWORD err = ::GetLastError(); - // make a second attempt using qt functions but if that fails print the - // error from the first attempt - if (!renameFile(iniFile + ".new", iniFile)) { - return windowsErrorString(err); - } - } - return QString(); -} - -QSettings::Status OrganizerCore::storeSettings(const QString &fileName) -{ - QSettings settings(fileName, QSettings::IniFormat); - if (m_UserInterface != nullptr) { - m_UserInterface->storeSettings(settings); - } - if (m_CurrentProfile != nullptr) { - settings.setValue("selected_profile", - m_CurrentProfile->name().toUtf8().constData()); - } - settings.setValue("ask_for_nexuspw", m_AskForNexusPW); - - settings.remove("customExecutables"); - settings.beginWriteArray("customExecutables"); - std::vector::const_iterator current, end; - m_ExecutablesList.getExecutables(current, end); - int count = 0; - for (; current != end; ++current) { - const Executable &item = *current; - settings.setArrayIndex(count++); - settings.setValue("title", item.m_Title); - settings.setValue("custom", item.isCustom()); - settings.setValue("toolbar", item.isShownOnToolbar()); - settings.setValue("ownicon", item.usesOwnIcon()); - if (item.isCustom()) { - settings.setValue("binary", item.m_BinaryInfo.absoluteFilePath()); - settings.setValue("arguments", item.m_Arguments); - settings.setValue("workingDirectory", item.m_WorkingDirectory); - settings.setValue("steamAppID", item.m_SteamAppID); - } - } - settings.endArray(); - - FileDialogMemory::save(settings); - - settings.sync(); - return settings.status(); -} - -void OrganizerCore::storeSettings() -{ - QString iniFile = qApp->property("dataPath").toString() + "/" - + QString::fromStdWString(AppConfig::iniFileName()); - if (QFileInfo(iniFile).exists()) { - if (!shellCopy(iniFile, iniFile + ".new", true, qApp->activeWindow())) { - QMessageBox::critical( - qApp->activeWindow(), tr("Failed to write settings"), - tr("An error occured trying to update MO settings to %1: %2") - .arg(iniFile, windowsErrorString(::GetLastError()))); - return; - } - } - - QString writeTarget = iniFile + ".new"; - - QSettings::Status result = storeSettings(writeTarget); - - if (result == QSettings::NoError) { - QString errMsg = commitSettings(iniFile); - if (!errMsg.isEmpty()) { - qWarning("settings file not writable, may be locked by another " - "application, trying direct write"); - writeTarget = iniFile; - result = storeSettings(iniFile); - } - } - if (result != QSettings::NoError) { - QString reason = result == QSettings::AccessError - ? tr("File is write protected") - : result == QSettings::FormatError - ? tr("Invalid file format (probably a bug)") - : tr("Unknown error %1").arg(result); - QMessageBox::critical( - qApp->activeWindow(), tr("Failed to write settings"), - tr("An error occured trying to write back MO settings to %1: %2") - .arg(writeTarget, reason)); - } -} - -bool OrganizerCore::testForSteam() -{ - size_t currentSize = 1024; - std::unique_ptr processIDs; - DWORD bytesReturned; - bool success = false; - while (!success) { - processIDs.reset(new DWORD[currentSize]); - if (!::EnumProcesses(processIDs.get(), - static_cast(currentSize) * sizeof(DWORD), - &bytesReturned)) { - qWarning("failed to determine if steam is running"); - return true; - } - if (bytesReturned == (currentSize * sizeof(DWORD))) { - // maximum size used, list probably truncated - currentSize *= 2; - } else { - success = true; - } - } - TCHAR processName[MAX_PATH]; - for (unsigned int i = 0; i < bytesReturned / sizeof(DWORD); ++i) { - memset(processName, '\0', sizeof(TCHAR) * MAX_PATH); - if (processIDs[i] != 0) { - HANDLE process = ::OpenProcess( - PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processIDs[i]); - - if (process != nullptr) { - - ON_BLOCK_EXIT([&]() { - if (process != INVALID_HANDLE_VALUE) - ::CloseHandle(process); - }); - - HMODULE module; - DWORD ignore; - - // first module in a process is always the binary - if (EnumProcessModules(process, &module, sizeof(HMODULE) * 1, - &ignore)) { - ::GetModuleBaseName(process, module, processName, MAX_PATH); - if ((_tcsicmp(processName, TEXT("steam.exe")) == 0) - || (_tcsicmp(processName, TEXT("steamservice.exe")) == 0)) { +QStringList toStringList(InputIterator current, InputIterator end) { + QStringList result; + for (; current != end; ++current) { + result.append(*current); + } + return result; +} + +OrganizerCore::OrganizerCore(const QSettings& initSettings) + : m_UserInterface(nullptr), m_PluginContainer(nullptr), m_GameName(), m_CurrentProfile(nullptr), + m_Settings(initSettings), m_Updater(NexusInterface::instance()), m_AboutToRun(), m_FinishedRun(), + m_ModInstalled(), m_ModList(this), m_PluginList(this), m_DirectoryRefresher(), + m_DirectoryStructure(new DirectoryEntry(L"data", nullptr, 0)), + m_DownloadManager(NexusInterface::instance(), this), m_InstallationManager(), m_RefresherThread(), + m_AskForNexusPW(false), m_DirectoryUpdate(false), m_ArchivesInit(false), + m_PluginListsWriter(std::bind(&OrganizerCore::savePluginList, this)) { + m_DownloadManager.setOutputDirectory(m_Settings.getDownloadDirectory()); + m_DownloadManager.setPreferredServers(m_Settings.getPreferredServers()); + + NexusInterface::instance()->setCacheDirectory(m_Settings.getCacheDirectory()); + NexusInterface::instance()->setNMMVersion(m_Settings.getNMMVersion()); + + MOBase::QuestionBoxMemory::init(initSettings.fileName()); + + m_InstallationManager.setModsDirectory(m_Settings.getModDirectory()); + m_InstallationManager.setDownloadDirectory(m_Settings.getDownloadDirectory()); + + connect(&m_DownloadManager, SIGNAL(downloadSpeed(QString, int)), this, SLOT(downloadSpeed(QString, int))); + connect(&m_DirectoryRefresher, SIGNAL(refreshed()), this, SLOT(directory_refreshed())); + + connect(&m_ModList, SIGNAL(removeOrigin(QString)), this, SLOT(removeOrigin(QString))); + + connect(NexusInterface::instance()->getAccessManager(), SIGNAL(loginSuccessful(bool)), this, + SLOT(loginSuccessful(bool))); + connect(NexusInterface::instance()->getAccessManager(), SIGNAL(loginFailed(QString)), this, + SLOT(loginFailed(QString))); + + // This seems awfully imperative + connect(this, SIGNAL(managedGameChanged(MOBase::IPluginGame const*)), &m_Settings, + SLOT(managedGameChanged(MOBase::IPluginGame const*))); + connect(this, SIGNAL(managedGameChanged(MOBase::IPluginGame const*)), &m_DownloadManager, + SLOT(managedGameChanged(MOBase::IPluginGame const*))); + connect(this, SIGNAL(managedGameChanged(MOBase::IPluginGame const*)), &m_PluginList, + SLOT(managedGameChanged(MOBase::IPluginGame const*))); + connect(this, SIGNAL(managedGameChanged(MOBase::IPluginGame const*)), NexusInterface::instance(), + SLOT(managedGameChanged(MOBase::IPluginGame const*))); + + connect(&m_PluginList, &PluginList::writePluginsList, &m_PluginListsWriter, &DelayedFileWriterBase::write); + + // make directory refresher run in a separate thread + m_RefresherThread.start(); + m_DirectoryRefresher.moveToThread(&m_RefresherThread); + + m_AskForNexusPW = initSettings.value("ask_for_nexuspw", true).toBool(); +} + +OrganizerCore::~OrganizerCore() { + m_RefresherThread.exit(); + m_RefresherThread.wait(); + + prepareStart(); + + // profile has to be cleaned up before the modinfo-buffer is cleared + delete m_CurrentProfile; + m_CurrentProfile = nullptr; + + ModInfo::clear(); + LogBuffer::cleanQuit(); + m_ModList.setProfile(nullptr); + // NexusInterface::instance()->cleanup(); + + delete m_DirectoryStructure; +} + +QString OrganizerCore::commitSettings(const QString& iniFile) { + if (!shellRename(iniFile + ".new", iniFile, true, qApp->activeWindow())) { + DWORD err = ::GetLastError(); + // make a second attempt using qt functions but if that fails print the + // error from the first attempt + if (!renameFile(iniFile + ".new", iniFile)) { + return windowsErrorString(err); + } + } + return QString(); +} + +QSettings::Status OrganizerCore::storeSettings(const QString& fileName) { + QSettings settings(fileName, QSettings::IniFormat); + if (m_UserInterface != nullptr) { + m_UserInterface->storeSettings(settings); + } + if (m_CurrentProfile != nullptr) { + settings.setValue("selected_profile", m_CurrentProfile->name().toUtf8().constData()); + } + settings.setValue("ask_for_nexuspw", m_AskForNexusPW); + + settings.remove("customExecutables"); + settings.beginWriteArray("customExecutables"); + std::vector::const_iterator current, end; + m_ExecutablesList.getExecutables(current, end); + int count = 0; + for (; current != end; ++current) { + const Executable& item = *current; + settings.setArrayIndex(count++); + settings.setValue("title", item.m_Title); + settings.setValue("custom", item.isCustom()); + settings.setValue("toolbar", item.isShownOnToolbar()); + settings.setValue("ownicon", item.usesOwnIcon()); + if (item.isCustom()) { + settings.setValue("binary", item.m_BinaryInfo.absoluteFilePath()); + settings.setValue("arguments", item.m_Arguments); + settings.setValue("workingDirectory", item.m_WorkingDirectory); + settings.setValue("steamAppID", item.m_SteamAppID); + } + } + settings.endArray(); + + FileDialogMemory::save(settings); + + settings.sync(); + return settings.status(); +} + +void OrganizerCore::storeSettings() { + QString iniFile = qApp->property("dataPath").toString() + "/" + QString::fromStdWString(AppConfig::iniFileName()); + if (QFileInfo(iniFile).exists()) { + if (!shellCopy(iniFile, iniFile + ".new", true, qApp->activeWindow())) { + QMessageBox::critical(qApp->activeWindow(), tr("Failed to write settings"), + tr("An error occured trying to update MO settings to %1: %2") + .arg(iniFile, windowsErrorString(::GetLastError()))); + return; + } + } + + QString writeTarget = iniFile + ".new"; + + QSettings::Status result = storeSettings(writeTarget); + + if (result == QSettings::NoError) { + QString errMsg = commitSettings(iniFile); + if (!errMsg.isEmpty()) { + qWarning("settings file not writable, may be locked by another " + "application, trying direct write"); + writeTarget = iniFile; + result = storeSettings(iniFile); + } + } + if (result != QSettings::NoError) { + QString reason = result == QSettings::AccessError + ? tr("File is write protected") + : result == QSettings::FormatError ? tr("Invalid file format (probably a bug)") + : tr("Unknown error %1").arg(result); + QMessageBox::critical( + qApp->activeWindow(), tr("Failed to write settings"), + tr("An error occured trying to write back MO settings to %1: %2").arg(writeTarget, reason)); + } +} + +bool OrganizerCore::testForSteam() { + size_t currentSize = 1024; + std::unique_ptr processIDs; + DWORD bytesReturned; + bool success = false; + while (!success) { + processIDs.reset(new DWORD[currentSize]); + if (!::EnumProcesses(processIDs.get(), static_cast(currentSize) * sizeof(DWORD), &bytesReturned)) { + qWarning("failed to determine if steam is running"); return true; - } } - } - } - } - - return false; -} - -void OrganizerCore::updateExecutablesList(QSettings &settings) -{ - if (m_PluginContainer == nullptr) { - qCritical("can't update executables list now"); - return; - } - - m_ExecutablesList.init(managedGame()); - - qDebug("setting up configured executables"); - - int numCustomExecutables = settings.beginReadArray("customExecutables"); - for (int i = 0; i < numCustomExecutables; ++i) { - settings.setArrayIndex(i); - - Executable::Flags flags; - if (settings.value("custom", true).toBool()) - flags |= Executable::CustomExecutable; - if (settings.value("toolbar", false).toBool()) - flags |= Executable::ShowInToolbar; - if (settings.value("ownicon", false).toBool()) - flags |= Executable::UseApplicationIcon; - - m_ExecutablesList.addExecutable( - settings.value("title").toString(), settings.value("binary").toString(), - settings.value("arguments").toString(), - settings.value("workingDirectory", "").toString(), - settings.value("steamAppID", "").toString(), flags); - } - - settings.endArray(); - - // TODO this has nothing to do with executables list move to an appropriate - // function! - ModInfo::updateFromDisc(m_Settings.getModDirectory(), &m_DirectoryStructure, - m_Settings.displayForeign(), managedGame()); -} - -void OrganizerCore::setUserInterface(IUserInterface *userInterface, - QWidget *widget) -{ - storeSettings(); - - m_UserInterface = userInterface; - - if (widget != nullptr) { - connect(&m_ModList, SIGNAL(modlist_changed(QModelIndex, int)), widget, - SLOT(modlistChanged(QModelIndex, int))); - connect(&m_ModList, SIGNAL(showMessage(QString)), widget, - SLOT(showMessage(QString))); - connect(&m_ModList, SIGNAL(modRenamed(QString, QString)), widget, - SLOT(modRenamed(QString, QString))); - connect(&m_ModList, SIGNAL(modUninstalled(QString)), widget, - SLOT(modRemoved(QString))); - connect(&m_ModList, SIGNAL(removeSelectedMods()), widget, - SLOT(removeMod_clicked())); - connect(&m_ModList, SIGNAL(requestColumnSelect(QPoint)), widget, - SLOT(displayColumnSelection(QPoint))); - connect(&m_ModList, SIGNAL(fileMoved(QString, QString, QString)), widget, - SLOT(fileMoved(QString, QString, QString))); - connect(&m_ModList, SIGNAL(modorder_changed()), widget, - SLOT(modorder_changed())); - connect(&m_DownloadManager, SIGNAL(showMessage(QString)), widget, - SLOT(showMessage(QString))); - } - - m_InstallationManager.setParentWidget(widget); - m_Updater.setUserInterface(widget); - - if (userInterface != nullptr) { - // this currently wouldn't work reliably if the ui isn't initialized yet to - // display the result - if (isOnline() && !m_Settings.offlineMode()) { - m_Updater.testForUpdate(); - } else { - qDebug("user doesn't seem to be connected to the internet"); + if (bytesReturned == (currentSize * sizeof(DWORD))) { + // maximum size used, list probably truncated + currentSize *= 2; + } else { + success = true; + } } - } + TCHAR processName[MAX_PATH]; + for (unsigned int i = 0; i < bytesReturned / sizeof(DWORD); ++i) { + memset(processName, '\0', sizeof(TCHAR) * MAX_PATH); + if (processIDs[i] != 0) { + HANDLE process = ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processIDs[i]); + + if (process != nullptr) { + + ON_BLOCK_EXIT([&]() { + if (process != INVALID_HANDLE_VALUE) + ::CloseHandle(process); + }); + + HMODULE module; + DWORD ignore; + + // first module in a process is always the binary + if (EnumProcessModules(process, &module, sizeof(HMODULE) * 1, &ignore)) { + ::GetModuleBaseName(process, module, processName, MAX_PATH); + if ((_tcsicmp(processName, TEXT("steam.exe")) == 0) || + (_tcsicmp(processName, TEXT("steamservice.exe")) == 0)) { + return true; + } + } + } + } + } + + return false; } -void OrganizerCore::connectPlugins(PluginContainer *container) -{ - m_DownloadManager.setSupportedExtensions( - m_InstallationManager.getSupportedExtensions()); - m_PluginContainer = container; +void OrganizerCore::updateExecutablesList(QSettings& settings) { + if (m_PluginContainer == nullptr) { + qCritical("can't update executables list now"); + return; + } - if (!m_GameName.isEmpty()) { - m_GamePlugin = m_PluginContainer->managedGame(m_GameName); - emit managedGameChanged(m_GamePlugin); - } + m_ExecutablesList.init(managedGame()); + + qDebug("setting up configured executables"); + + int numCustomExecutables = settings.beginReadArray("customExecutables"); + for (int i = 0; i < numCustomExecutables; ++i) { + settings.setArrayIndex(i); + + Executable::Flags flags; + if (settings.value("custom", true).toBool()) + flags |= Executable::CustomExecutable; + if (settings.value("toolbar", false).toBool()) + flags |= Executable::ShowInToolbar; + if (settings.value("ownicon", false).toBool()) + flags |= Executable::UseApplicationIcon; + + m_ExecutablesList.addExecutable(settings.value("title").toString(), settings.value("binary").toString(), + settings.value("arguments").toString(), + settings.value("workingDirectory", "").toString(), + settings.value("steamAppID", "").toString(), flags); + } + + settings.endArray(); + + // TODO this has nothing to do with executables list move to an appropriate + // function! + ModInfo::updateFromDisc(m_Settings.getModDirectory(), &m_DirectoryStructure, m_Settings.displayForeign(), + managedGame()); +} + +void OrganizerCore::setUserInterface(IUserInterface* userInterface, QWidget* widget) { + storeSettings(); + + m_UserInterface = userInterface; + + if (widget != nullptr) { + connect(&m_ModList, SIGNAL(modlist_changed(QModelIndex, int)), widget, SLOT(modlistChanged(QModelIndex, int))); + connect(&m_ModList, SIGNAL(showMessage(QString)), widget, SLOT(showMessage(QString))); + connect(&m_ModList, SIGNAL(modRenamed(QString, QString)), widget, SLOT(modRenamed(QString, QString))); + connect(&m_ModList, SIGNAL(modUninstalled(QString)), widget, SLOT(modRemoved(QString))); + connect(&m_ModList, SIGNAL(removeSelectedMods()), widget, SLOT(removeMod_clicked())); + connect(&m_ModList, SIGNAL(requestColumnSelect(QPoint)), widget, SLOT(displayColumnSelection(QPoint))); + connect(&m_ModList, SIGNAL(fileMoved(QString, QString, QString)), widget, + SLOT(fileMoved(QString, QString, QString))); + connect(&m_ModList, SIGNAL(modorder_changed()), widget, SLOT(modorder_changed())); + connect(&m_DownloadManager, SIGNAL(showMessage(QString)), widget, SLOT(showMessage(QString))); + } + + m_InstallationManager.setParentWidget(widget); + m_Updater.setUserInterface(widget); + + if (userInterface != nullptr) { + // this currently wouldn't work reliably if the ui isn't initialized yet to + // display the result + if (isOnline() && !m_Settings.offlineMode()) { + m_Updater.testForUpdate(); + } else { + qDebug("user doesn't seem to be connected to the internet"); + } + } } -void OrganizerCore::disconnectPlugins() -{ - m_AboutToRun.disconnect_all_slots(); - m_FinishedRun.disconnect_all_slots(); - m_ModInstalled.disconnect_all_slots(); - m_ModList.disconnectSlots(); - m_PluginList.disconnectSlots(); +void OrganizerCore::connectPlugins(PluginContainer* container) { + m_DownloadManager.setSupportedExtensions(m_InstallationManager.getSupportedExtensions()); + m_PluginContainer = container; - m_Settings.clearPlugins(); - m_GamePlugin = nullptr; - m_PluginContainer = nullptr; + if (!m_GameName.isEmpty()) { + m_GamePlugin = m_PluginContainer->managedGame(m_GameName); + emit managedGameChanged(m_GamePlugin); + } } -void OrganizerCore::setManagedGame(MOBase::IPluginGame *game) -{ - m_GameName = game->gameName(); - m_GamePlugin = game; - qApp->setProperty("managed_game", QVariant::fromValue(m_GamePlugin)); - emit managedGameChanged(m_GamePlugin); +void OrganizerCore::disconnectPlugins() { + m_AboutToRun.disconnect_all_slots(); + m_FinishedRun.disconnect_all_slots(); + m_ModInstalled.disconnect_all_slots(); + m_ModList.disconnectSlots(); + m_PluginList.disconnectSlots(); + + m_Settings.clearPlugins(); + m_GamePlugin = nullptr; + m_PluginContainer = nullptr; } -Settings &OrganizerCore::settings() -{ - return m_Settings; +void OrganizerCore::setManagedGame(MOBase::IPluginGame* game) { + m_GameName = game->gameName(); + m_GamePlugin = game; + qApp->setProperty("managed_game", QVariant::fromValue(m_GamePlugin)); + emit managedGameChanged(m_GamePlugin); } -bool OrganizerCore::nexusLogin(bool retry) -{ - NXMAccessManager *accessManager - = NexusInterface::instance()->getAccessManager(); +Settings& OrganizerCore::settings() { return m_Settings; } - if ((accessManager->loginAttempted() || accessManager->loggedIn()) - && !retry) { - // previous attempt, maybe even successful - return false; - } else { - QString username, password; - if ((!retry && m_Settings.getNexusLogin(username, password)) - || (m_AskForNexusPW && queryLogin(username, password))) { - // credentials stored or user entered them manually - qDebug("attempt login with username %s", qPrintable(username)); - accessManager->login(username, password); - return true; +bool OrganizerCore::nexusLogin(bool retry) { + NXMAccessManager* accessManager = NexusInterface::instance()->getAccessManager(); + + if ((accessManager->loginAttempted() || accessManager->loggedIn()) && !retry) { + // previous attempt, maybe even successful + return false; } else { - // no credentials stored and user didn't enter them - accessManager->refuseLogin(); - return false; - } - } -} - -bool OrganizerCore::queryLogin(QString &username, QString &password) -{ - CredentialsDialog dialog(qApp->activeWindow()); - int res = dialog.exec(); - if (dialog.neverAsk()) { - m_AskForNexusPW = false; - } - if (res == QDialog::Accepted) { - username = dialog.username(); - password = dialog.password(); - if (dialog.store()) { - m_Settings.setNexusLogin(username, password); + QString username, password; + if ((!retry && m_Settings.getNexusLogin(username, password)) || + (m_AskForNexusPW && queryLogin(username, password))) { + // credentials stored or user entered them manually + qDebug("attempt login with username %s", qPrintable(username)); + accessManager->login(username, password); + return true; + } else { + // no credentials stored and user didn't enter them + accessManager->refuseLogin(); + return false; + } } - return true; - } else { - return false; - } } -void OrganizerCore::startMOUpdate() -{ - if (nexusLogin()) { - m_PostLoginTasks.append([&]() { m_Updater.startUpdate(); }); - } else { - m_Updater.startUpdate(); - } +bool OrganizerCore::queryLogin(QString& username, QString& password) { + CredentialsDialog dialog(qApp->activeWindow()); + int res = dialog.exec(); + if (dialog.neverAsk()) { + m_AskForNexusPW = false; + } + if (res == QDialog::Accepted) { + username = dialog.username(); + password = dialog.password(); + if (dialog.store()) { + m_Settings.setNexusLogin(username, password); + } + return true; + } else { + return false; + } } -void OrganizerCore::downloadRequestedNXM(const QString &url) -{ - qDebug("download requested: %s", qPrintable(url)); - if (nexusLogin()) { - m_PendingDownloads.append(url); - } else { - m_DownloadManager.addNXMDownload(url); - } +void OrganizerCore::startMOUpdate() { + if (nexusLogin()) { + m_PostLoginTasks.append([&]() { m_Updater.startUpdate(); }); + } else { + m_Updater.startUpdate(); + } } -void OrganizerCore::externalMessage(const QString &message) -{ - if (MOShortcut moshortcut{ message }) { - runShortcut(moshortcut); - } - else if (isNxmLink(message)) { - MessageDialog::showMessage(tr("Download started"), qApp->activeWindow()); - downloadRequestedNXM(message); - } +void OrganizerCore::downloadRequestedNXM(const QString& url) { + qDebug("download requested: %s", qPrintable(url)); + if (nexusLogin()) { + m_PendingDownloads.append(url); + } else { + m_DownloadManager.addNXMDownload(url); + } } -void OrganizerCore::downloadRequested(QNetworkReply *reply, int modID, - const QString &fileName) -{ - try { - if (m_DownloadManager.addDownload(reply, QStringList(), fileName, modID, 0, - new ModRepositoryFileInfo(modID))) { - MessageDialog::showMessage(tr("Download started"), qApp->activeWindow()); +void OrganizerCore::externalMessage(const QString& message) { + if (MOShortcut moshortcut{message}) { + runShortcut(moshortcut); + } else if (isNxmLink(message)) { + MessageDialog::showMessage(tr("Download started"), qApp->activeWindow()); + downloadRequestedNXM(message); } - } catch (const std::exception &e) { - MessageDialog::showMessage(tr("Download failed"), qApp->activeWindow()); - qCritical("exception starting download: %s", e.what()); - } } -void OrganizerCore::removeOrigin(const QString &name) -{ - FilesOrigin &origin = m_DirectoryStructure->getOriginByName(ToWString(name)); - origin.enable(false); - refreshLists(); +void OrganizerCore::downloadRequested(QNetworkReply* reply, int modID, const QString& fileName) { + try { + if (m_DownloadManager.addDownload(reply, QStringList(), fileName, modID, 0, new ModRepositoryFileInfo(modID))) { + MessageDialog::showMessage(tr("Download started"), qApp->activeWindow()); + } + } catch (const std::exception& e) { + MessageDialog::showMessage(tr("Download failed"), qApp->activeWindow()); + qCritical("exception starting download: %s", e.what()); + } } -void OrganizerCore::downloadSpeed(const QString &serverName, int bytesPerSecond) -{ - m_Settings.setDownloadSpeed(serverName, bytesPerSecond); +void OrganizerCore::removeOrigin(const QString& name) { + FilesOrigin& origin = m_DirectoryStructure->getOriginByName(ToWString(name)); + origin.enable(false); + refreshLists(); } -InstallationManager *OrganizerCore::installationManager() -{ - return &m_InstallationManager; +void OrganizerCore::downloadSpeed(const QString& serverName, int bytesPerSecond) { + m_Settings.setDownloadSpeed(serverName, bytesPerSecond); } -bool OrganizerCore::createDirectory(const QString &path) { - if (!QDir(path).exists() && !QDir().mkpath(path)) { - QMessageBox::critical(nullptr, QObject::tr("Error"), - QObject::tr("Failed to create \"%1\". Your user " - "account probably lacks permission.") - .arg(QDir::toNativeSeparators(path))); - return false; - } else { - return true; - } +InstallationManager* OrganizerCore::installationManager() { return &m_InstallationManager; } + +bool OrganizerCore::createDirectory(const QString& path) { + if (!QDir(path).exists() && !QDir().mkpath(path)) { + QMessageBox::critical(nullptr, QObject::tr("Error"), + QObject::tr("Failed to create \"%1\". Your user " + "account probably lacks permission.") + .arg(QDir::toNativeSeparators(path))); + return false; + } else { + return true; + } } bool OrganizerCore::bootstrap() { - return createDirectory(m_Settings.getProfileDirectory()) && - createDirectory(m_Settings.getModDirectory()) && - createDirectory(m_Settings.getDownloadDirectory()) && - createDirectory(m_Settings.getOverwriteDirectory()) && - createDirectory(QString::fromStdWString(crashDumpsPath())) && cycleDiagnostics(); + return createDirectory(m_Settings.getProfileDirectory()) && createDirectory(m_Settings.getModDirectory()) && + createDirectory(m_Settings.getDownloadDirectory()) && createDirectory(m_Settings.getOverwriteDirectory()) && + createDirectory(QString::fromStdWString(crashDumpsPath())) && cycleDiagnostics(); } -void OrganizerCore::createDefaultProfile() -{ - QString profilesPath = settings().getProfileDirectory(); - if (QDir(profilesPath).entryList(QDir::AllDirs | QDir::NoDotAndDotDot).size() - == 0) { - Profile newProf("Default", managedGame(), false); - } +void OrganizerCore::createDefaultProfile() { + QString profilesPath = settings().getProfileDirectory(); + if (QDir(profilesPath).entryList(QDir::AllDirs | QDir::NoDotAndDotDot).size() == 0) { + Profile newProf("Default", managedGame(), false); + } } -void OrganizerCore::prepareVFS() -{ - m_USVFS.updateMapping(fileMapping(m_CurrentProfile->name(), QString())); -} +void OrganizerCore::prepareVFS() { m_USVFS.updateMapping(fileMapping(m_CurrentProfile->name(), QString())); } void OrganizerCore::updateVFSParams(int logLevel, int crashDumpsType) { - setGlobalCrashDumpsType(crashDumpsType); - m_USVFS.updateParams(logLevel, crashDumpsType); + setGlobalCrashDumpsType(crashDumpsType); + m_USVFS.updateParams(logLevel, crashDumpsType); } bool OrganizerCore::cycleDiagnostics() { - if (int maxDumps = settings().crashDumpsMax()) - removeOldFiles(QString::fromStdWString(crashDumpsPath()), "*.dmp", maxDumps, QDir::Time|QDir::Reversed); - return true; + if (int maxDumps = settings().crashDumpsMax()) + removeOldFiles(QString::fromStdWString(crashDumpsPath()), "*.dmp", maxDumps, QDir::Time | QDir::Reversed); + return true; } -//static +// static void OrganizerCore::setGlobalCrashDumpsType(int crashDumpsType) { - m_globalCrashDumpsType = ::crashDumpsType(crashDumpsType); + m_globalCrashDumpsType = ::crashDumpsType(crashDumpsType); } -//static +// static std::wstring OrganizerCore::crashDumpsPath() { - return ( - qApp->property("dataPath").toString() + "/" - + QString::fromStdWString(AppConfig::dumpsDir()) - ).toStdWString(); + return (qApp->property("dataPath").toString() + "/" + QString::fromStdWString(AppConfig::dumpsDir())) + .toStdWString(); } -void OrganizerCore::setCurrentProfile(const QString &profileName) -{ - if ((m_CurrentProfile != nullptr) - && (profileName == m_CurrentProfile->name())) { - return; - } +void OrganizerCore::setCurrentProfile(const QString& profileName) { + if ((m_CurrentProfile != nullptr) && (profileName == m_CurrentProfile->name())) { + return; + } - QDir profileBaseDir(settings().getProfileDirectory()); - QString profileDir = profileBaseDir.absoluteFilePath(profileName); + QDir profileBaseDir(settings().getProfileDirectory()); + QString profileDir = profileBaseDir.absoluteFilePath(profileName); - if (!QDir(profileDir).exists()) { - // selected profile doesn't exist. Ensure there is at least one profile, - // then pick any one - createDefaultProfile(); + if (!QDir(profileDir).exists()) { + // selected profile doesn't exist. Ensure there is at least one profile, + // then pick any one + createDefaultProfile(); - profileDir = profileBaseDir.absoluteFilePath( - profileBaseDir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot).at(0)); - } + profileDir = + profileBaseDir.absoluteFilePath(profileBaseDir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot).at(0)); + } - Profile *newProfile = new Profile(QDir(profileDir), managedGame()); + Profile* newProfile = new Profile(QDir(profileDir), managedGame()); - delete m_CurrentProfile; - m_CurrentProfile = newProfile; - m_ModList.setProfile(newProfile); + delete m_CurrentProfile; + m_CurrentProfile = newProfile; + m_ModList.setProfile(newProfile); - if (m_CurrentProfile->invalidationActive(nullptr)) { - m_CurrentProfile->activateInvalidation(); - } else { - m_CurrentProfile->deactivateInvalidation(); - } + if (m_CurrentProfile->invalidationActive(nullptr)) { + m_CurrentProfile->activateInvalidation(); + } else { + m_CurrentProfile->deactivateInvalidation(); + } - connect(m_CurrentProfile, SIGNAL(modStatusChanged(uint)), this, - SLOT(modStatusChanged(uint))); - refreshDirectoryStructure(); + connect(m_CurrentProfile, SIGNAL(modStatusChanged(uint)), this, SLOT(modStatusChanged(uint))); + refreshDirectoryStructure(); } -MOBase::IModRepositoryBridge *OrganizerCore::createNexusBridge() const -{ - return new NexusBridge(); -} +MOBase::IModRepositoryBridge* OrganizerCore::createNexusBridge() const { return new NexusBridge(); } -QString OrganizerCore::profileName() const -{ - if (m_CurrentProfile != nullptr) { - return m_CurrentProfile->name(); - } else { - return ""; - } +QString OrganizerCore::profileName() const { + if (m_CurrentProfile != nullptr) { + return m_CurrentProfile->name(); + } else { + return ""; + } } -QString OrganizerCore::profilePath() const -{ - if (m_CurrentProfile != nullptr) { - return m_CurrentProfile->absolutePath(); - } else { - return ""; - } +QString OrganizerCore::profilePath() const { + if (m_CurrentProfile != nullptr) { + return m_CurrentProfile->absolutePath(); + } else { + return ""; + } } -QString OrganizerCore::downloadsPath() const -{ - return QDir::fromNativeSeparators(m_Settings.getDownloadDirectory()); -} +QString OrganizerCore::downloadsPath() const { return QDir::fromNativeSeparators(m_Settings.getDownloadDirectory()); } -QString OrganizerCore::overwritePath() const -{ - return QDir::fromNativeSeparators(m_Settings.getOverwriteDirectory()); -} +QString OrganizerCore::overwritePath() const { return QDir::fromNativeSeparators(m_Settings.getOverwriteDirectory()); } -QString OrganizerCore::basePath() const -{ - return QDir::fromNativeSeparators(m_Settings.getBaseDirectory()); -} +QString OrganizerCore::basePath() const { return QDir::fromNativeSeparators(m_Settings.getBaseDirectory()); } -MOBase::VersionInfo OrganizerCore::appVersion() const -{ - return m_Updater.getVersion(); -} +MOBase::VersionInfo OrganizerCore::appVersion() const { return m_Updater.getVersion(); } -MOBase::IModInterface *OrganizerCore::getMod(const QString &name) const -{ - unsigned int index = ModInfo::getIndex(name); - return index == UINT_MAX ? nullptr : ModInfo::getByIndex(index).data(); +MOBase::IModInterface* OrganizerCore::getMod(const QString& name) const { + unsigned int index = ModInfo::getIndex(name); + return index == UINT_MAX ? nullptr : ModInfo::getByIndex(index).data(); } -MOBase::IModInterface *OrganizerCore::createMod(GuessedValue &name) -{ - bool merge = false; - if (!m_InstallationManager.testOverwrite(name, &merge)) { - return nullptr; - } +MOBase::IModInterface* OrganizerCore::createMod(GuessedValue& name) { + bool merge = false; + if (!m_InstallationManager.testOverwrite(name, &merge)) { + return nullptr; + } - m_InstallationManager.setModsDirectory(m_Settings.getModDirectory()); + m_InstallationManager.setModsDirectory(m_Settings.getModDirectory()); - QString targetDirectory - = QDir::fromNativeSeparators(m_Settings.getModDirectory()) - .append("/") - .append(name); + QString targetDirectory = QDir::fromNativeSeparators(m_Settings.getModDirectory()).append("/").append(name); - QSettings settingsFile(targetDirectory + "/meta.ini", QSettings::IniFormat); + QSettings settingsFile(targetDirectory + "/meta.ini", QSettings::IniFormat); - if (!merge) { - settingsFile.setValue("modid", 0); - settingsFile.setValue("version", ""); - settingsFile.setValue("newestVersion", ""); - settingsFile.setValue("category", 0); - settingsFile.setValue("installationFile", ""); + if (!merge) { + settingsFile.setValue("modid", 0); + settingsFile.setValue("version", ""); + settingsFile.setValue("newestVersion", ""); + settingsFile.setValue("category", 0); + settingsFile.setValue("installationFile", ""); - settingsFile.beginWriteArray("installedFiles", 0); - settingsFile.endArray(); - } + settingsFile.beginWriteArray("installedFiles", 0); + settingsFile.endArray(); + } - return ModInfo::createFrom(QDir(targetDirectory), &m_DirectoryStructure) - .data(); + return ModInfo::createFrom(QDir(targetDirectory), &m_DirectoryStructure).data(); } -bool OrganizerCore::removeMod(MOBase::IModInterface *mod) -{ - unsigned int index = ModInfo::getIndex(mod->name()); - if (index == UINT_MAX) { - return mod->remove(); - } else { - return ModInfo::removeMod(index); - } +bool OrganizerCore::removeMod(MOBase::IModInterface* mod) { + unsigned int index = ModInfo::getIndex(mod->name()); + if (index == UINT_MAX) { + return mod->remove(); + } else { + return ModInfo::removeMod(index); + } } -void OrganizerCore::modDataChanged(MOBase::IModInterface *) -{ - refreshModList(false); -} +void OrganizerCore::modDataChanged(MOBase::IModInterface*) { refreshModList(false); } -QVariant OrganizerCore::pluginSetting(const QString &pluginName, - const QString &key) const -{ - return m_Settings.pluginSetting(pluginName, key); +QVariant OrganizerCore::pluginSetting(const QString& pluginName, const QString& key) const { + return m_Settings.pluginSetting(pluginName, key); } -void OrganizerCore::setPluginSetting(const QString &pluginName, - const QString &key, const QVariant &value) -{ - m_Settings.setPluginSetting(pluginName, key, value); +void OrganizerCore::setPluginSetting(const QString& pluginName, const QString& key, const QVariant& value) { + m_Settings.setPluginSetting(pluginName, key, value); } -QVariant OrganizerCore::persistent(const QString &pluginName, - const QString &key, - const QVariant &def) const -{ - return m_Settings.pluginPersistent(pluginName, key, def); +QVariant OrganizerCore::persistent(const QString& pluginName, const QString& key, const QVariant& def) const { + return m_Settings.pluginPersistent(pluginName, key, def); } -void OrganizerCore::setPersistent(const QString &pluginName, const QString &key, - const QVariant &value, bool sync) -{ - m_Settings.setPluginPersistent(pluginName, key, value, sync); +void OrganizerCore::setPersistent(const QString& pluginName, const QString& key, const QVariant& value, bool sync) { + m_Settings.setPluginPersistent(pluginName, key, value, sync); } -QString OrganizerCore::pluginDataPath() const -{ - return qApp->applicationDirPath() + "/" + ToQString(AppConfig::pluginPath()) - + "/data"; +QString OrganizerCore::pluginDataPath() const { + return qApp->applicationDirPath() + "/" + ToQString(AppConfig::pluginPath()) + "/data"; } -MOBase::IModInterface *OrganizerCore::installMod(const QString &fileName, - const QString &initModName) -{ - if (m_CurrentProfile == nullptr) { +MOBase::IModInterface* OrganizerCore::installMod(const QString& fileName, const QString& initModName) { + if (m_CurrentProfile == nullptr) { + return nullptr; + } + + bool hasIniTweaks = false; + GuessedValue modName; + if (!initModName.isEmpty()) { + modName.update(initModName, GUESS_USER); + } + m_CurrentProfile->writeModlistNow(); + m_InstallationManager.setModsDirectory(m_Settings.getModDirectory()); + if (m_InstallationManager.install(fileName, modName, hasIniTweaks)) { + MessageDialog::showMessage(tr("Installation successful"), qApp->activeWindow()); + refreshModList(); + + int modIndex = ModInfo::getIndex(modName); + if (modIndex != UINT_MAX) { + ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex); + if (hasIniTweaks && (m_UserInterface != 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(modInfo, modIndex, ModInfoDialog::TAB_INIFILES); + } + m_ModInstalled(modName); + return modInfo.data(); + } else { + reportError(tr("mod \"%1\" not found").arg(modName)); + } + } else if (m_InstallationManager.wasCancelled()) { + QMessageBox::information(qApp->activeWindow(), tr("Installation cancelled"), + tr("The mod was not installed completely."), QMessageBox::Ok); + } return nullptr; - } - - bool hasIniTweaks = false; - GuessedValue modName; - if (!initModName.isEmpty()) { - modName.update(initModName, GUESS_USER); - } - m_CurrentProfile->writeModlistNow(); - m_InstallationManager.setModsDirectory(m_Settings.getModDirectory()); - if (m_InstallationManager.install(fileName, modName, hasIniTweaks)) { - MessageDialog::showMessage(tr("Installation successful"), - qApp->activeWindow()); - refreshModList(); +} + +void OrganizerCore::installDownload(int index) { + try { + QString fileName = m_DownloadManager.getFilePath(index); + int modID = m_DownloadManager.getModID(index); + int fileID = m_DownloadManager.getFileInfo(index)->fileID; + GuessedValue modName; + + // see if there already are mods with the specified mod id + if (modID != 0) { + std::vector modInfo = ModInfo::getByModID(modID); + for (auto iter = modInfo.begin(); iter != modInfo.end(); ++iter) { + std::vector flags = (*iter)->getFlags(); + if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_BACKUP) == flags.end()) { + modName.update((*iter)->name(), GUESS_PRESET); + (*iter)->saveMeta(); + } + } + } - int modIndex = ModInfo::getIndex(modName); - if (modIndex != UINT_MAX) { - ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex); - if (hasIniTweaks && (m_UserInterface != 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(modInfo, modIndex, - ModInfoDialog::TAB_INIFILES); - } - m_ModInstalled(modName); - return modInfo.data(); + m_CurrentProfile->writeModlistNow(); + + bool hasIniTweaks = false; + m_InstallationManager.setModsDirectory(m_Settings.getModDirectory()); + if (m_InstallationManager.install(fileName, modName, hasIniTweaks)) { + MessageDialog::showMessage(tr("Installation successful"), qApp->activeWindow()); + refreshModList(); + + int modIndex = ModInfo::getIndex(modName); + if (modIndex != UINT_MAX) { + ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex); + modInfo->addInstalledFile(modID, fileID); + + if (hasIniTweaks && m_UserInterface != 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(modInfo, modIndex, ModInfoDialog::TAB_INIFILES); + } + + m_ModInstalled(modName); + } else { + reportError(tr("mod \"%1\" not found").arg(modName)); + } + m_DownloadManager.markInstalled(index); + + emit modInstalled(modName); + } else if (m_InstallationManager.wasCancelled()) { + QMessageBox::information(qApp->activeWindow(), tr("Installation cancelled"), + tr("The mod was not installed completely."), QMessageBox::Ok); + } + } catch (const std::exception& e) { + reportError(e.what()); + } +} + +QString OrganizerCore::resolvePath(const QString& fileName) const { + if (m_DirectoryStructure == nullptr) { + return QString(); + } + const FileEntry::Ptr file = m_DirectoryStructure->searchFile(ToWString(fileName), nullptr); + if (file.get() != nullptr) { + return ToQString(file->getFullPath()); } else { - reportError(tr("mod \"%1\" not found").arg(modName)); - } - } else if (m_InstallationManager.wasCancelled()) { - QMessageBox::information(qApp->activeWindow(), tr("Installation cancelled"), - tr("The mod was not installed completely."), - QMessageBox::Ok); - } - return nullptr; -} - -void OrganizerCore::installDownload(int index) -{ - try { - QString fileName = m_DownloadManager.getFilePath(index); - int modID = m_DownloadManager.getModID(index); - int fileID = m_DownloadManager.getFileInfo(index)->fileID; - GuessedValue modName; + return QString(); + } +} - // see if there already are mods with the specified mod id - if (modID != 0) { - std::vector modInfo = ModInfo::getByModID(modID); - for (auto iter = modInfo.begin(); iter != modInfo.end(); ++iter) { - std::vector flags = (*iter)->getFlags(); - if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_BACKUP) - == flags.end()) { - modName.update((*iter)->name(), GUESS_PRESET); - (*iter)->saveMeta(); +QStringList OrganizerCore::listDirectories(const QString& directoryName) const { + QStringList result; + DirectoryEntry* dir = m_DirectoryStructure->findSubDirectoryRecursive(ToWString(directoryName)); + if (dir != nullptr) { + std::vector::iterator current, end; + dir->getSubDirectories(current, end); + for (; current != end; ++current) { + result.append(ToQString((*current)->getName())); + } + } + return result; +} + +QStringList OrganizerCore::findFiles(const QString& path, const std::function& filter) const { + QStringList result; + DirectoryEntry* dir = m_DirectoryStructure->findSubDirectoryRecursive(ToWString(path)); + if (dir != nullptr) { + std::vector files = dir->getFiles(); + foreach (FileEntry::Ptr file, files) { + if (filter(ToQString(file->getFullPath()))) { + result.append(ToQString(file->getFullPath())); + } } - } + } else { + qWarning("directory %s not found", qPrintable(path)); } + return result; +} - m_CurrentProfile->writeModlistNow(); +QStringList OrganizerCore::getFileOrigins(const QString& fileName) const { + QStringList result; + const FileEntry::Ptr file = m_DirectoryStructure->searchFile(ToWString(QFileInfo(fileName).fileName()), nullptr); - bool hasIniTweaks = false; - m_InstallationManager.setModsDirectory(m_Settings.getModDirectory()); - if (m_InstallationManager.install(fileName, modName, hasIniTweaks)) { - MessageDialog::showMessage(tr("Installation successful"), - qApp->activeWindow()); - refreshModList(); - - int modIndex = ModInfo::getIndex(modName); - if (modIndex != UINT_MAX) { - ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex); - modInfo->addInstalledFile(modID, fileID); - - if (hasIniTweaks && m_UserInterface != 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(modInfo, modIndex, - ModInfoDialog::TAB_INIFILES); + if (file.get() != nullptr) { + result.append(ToQString(m_DirectoryStructure->getOriginByID(file->getOrigin()).getName())); + foreach (auto i, file->getAlternatives()) { + result.append(ToQString(m_DirectoryStructure->getOriginByID(i.first).getName())); } + } else { + qDebug("%s not found", qPrintable(fileName)); + } + return result; +} + +QList +OrganizerCore::findFileInfos(const QString& path, + const std::function& filter) const { + QList result; + DirectoryEntry* dir = m_DirectoryStructure->findSubDirectoryRecursive(ToWString(path)); + if (dir != nullptr) { + std::vector files = dir->getFiles(); + foreach (FileEntry::Ptr file, files) { + IOrganizer::FileInfo info; + info.filePath = ToQString(file->getFullPath()); + bool fromArchive = false; + info.origins.append(ToQString(m_DirectoryStructure->getOriginByID(file->getOrigin(fromArchive)).getName())); + info.archive = fromArchive ? ToQString(file->getArchive()) : ""; + foreach (auto idx, file->getAlternatives()) { + info.origins.append(ToQString(m_DirectoryStructure->getOriginByID(idx.first).getName())); + } + + if (filter(info)) { + result.append(info); + } + } + } + return result; +} - m_ModInstalled(modName); - } else { - reportError(tr("mod \"%1\" not found").arg(modName)); - } - m_DownloadManager.markInstalled(index); +DownloadManager* OrganizerCore::downloadManager() { return &m_DownloadManager; } - emit modInstalled(modName); - } else if (m_InstallationManager.wasCancelled()) { - QMessageBox::information( - qApp->activeWindow(), tr("Installation cancelled"), - tr("The mod was not installed completely."), QMessageBox::Ok); +PluginList* OrganizerCore::pluginList() { return &m_PluginList; } + +ModList* OrganizerCore::modList() { return &m_ModList; } + +QStringList OrganizerCore::modsSortedByProfilePriority() const { + QStringList res; + for (unsigned int i = 0; i < currentProfile()->numRegularMods(); ++i) { + int modIndex = currentProfile()->modIndexByPriority(i); + res.push_back(ModInfo::getByIndex(modIndex)->name()); + } + return res; +} + +void OrganizerCore::spawnBinary(const QFileInfo& binary, const QString& arguments, const QDir& currentDirectory, + const QString& steamAppID, const QString& customOverwrite) { + DWORD processExitCode = 0; + HANDLE processHandle = spawnBinaryDirect(binary, arguments, m_CurrentProfile->name(), currentDirectory, steamAppID, + customOverwrite, &processExitCode); + if (processHandle != INVALID_HANDLE_VALUE) { + refreshDirectoryStructure(); + // need to remove our stored load order because it may be outdated if a foreign tool changed the + // file time. After removing that file, refreshESPList will use the file time as the order + if (managedGame()->loadOrderMechanism() == IPluginGame::LoadOrderMechanism::FileTime) { + qDebug("removing loadorder.txt"); + QFile::remove(m_CurrentProfile->getLoadOrderFileName()); + } + refreshDirectoryStructure(); + + refreshESPList(); + savePluginList(); + + // These callbacks should not fiddle with directoy structure and ESPs. + m_FinishedRun(binary.absoluteFilePath(), processExitCode); } - } catch (const std::exception &e) { - reportError(e.what()); - } } -QString OrganizerCore::resolvePath(const QString &fileName) const -{ - if (m_DirectoryStructure == nullptr) { - return QString(); - } - const FileEntry::Ptr file - = m_DirectoryStructure->searchFile(ToWString(fileName), nullptr); - if (file.get() != nullptr) { - return ToQString(file->getFullPath()); - } else { - return QString(); - } +HANDLE OrganizerCore::spawnBinaryDirect(const QFileInfo& binary, const QString& arguments, const QString& profileName, + const QDir& currentDirectory, const QString& steamAppID, + const QString& customOverwrite, LPDWORD exitCode) { + HANDLE processHandle = + spawnBinaryProcess(binary, arguments, profileName, currentDirectory, steamAppID, customOverwrite); + if (processHandle != INVALID_HANDLE_VALUE) { + std::unique_ptr dlg; + ILockedWaitingForProcess* uilock = nullptr; + + if (m_UserInterface != nullptr) { + uilock = m_UserInterface->lock(); + } else { + // i.e. when running command line shortcuts there is no m_UserInterface + dlg.reset(new LockedDialog); + dlg->show(); + dlg->setEnabled(true); + uilock = dlg.get(); + } + + ON_BLOCK_EXIT([&]() { + if (m_UserInterface != nullptr) { + m_UserInterface->unlock(); + } + }); + + DWORD ignoreExitCode; + waitForProcessCompletion(processHandle, exitCode ? exitCode : &ignoreExitCode, uilock); + cycleDiagnostics(); + } + + return processHandle; } -QStringList OrganizerCore::listDirectories(const QString &directoryName) const -{ - QStringList result; - DirectoryEntry *dir = m_DirectoryStructure->findSubDirectoryRecursive( - ToWString(directoryName)); - if (dir != nullptr) { - std::vector::iterator current, end; - dir->getSubDirectories(current, end); - for (; current != end; ++current) { - result.append(ToQString((*current)->getName())); - } - } - return result; -} - -QStringList OrganizerCore::findFiles( - const QString &path, - const std::function &filter) const -{ - QStringList result; - DirectoryEntry *dir - = m_DirectoryStructure->findSubDirectoryRecursive(ToWString(path)); - if (dir != nullptr) { - std::vector files = dir->getFiles(); - foreach (FileEntry::Ptr file, files) { - if (filter(ToQString(file->getFullPath()))) { - result.append(ToQString(file->getFullPath())); - } - } - } else { - qWarning("directory %s not found", qPrintable(path)); - } - return result; -} - -QStringList OrganizerCore::getFileOrigins(const QString &fileName) const -{ - QStringList result; - const FileEntry::Ptr file = m_DirectoryStructure->searchFile( - ToWString(QFileInfo(fileName).fileName()), nullptr); - - if (file.get() != nullptr) { - result.append(ToQString( - m_DirectoryStructure->getOriginByID(file->getOrigin()).getName())); - foreach (auto i, file->getAlternatives()) { - result.append( - ToQString(m_DirectoryStructure->getOriginByID(i.first).getName())); - } - } else { - qDebug("%s not found", qPrintable(fileName)); - } - return result; -} - -QList OrganizerCore::findFileInfos( - const QString &path, - const std::function &filter) - const -{ - QList result; - DirectoryEntry *dir - = m_DirectoryStructure->findSubDirectoryRecursive(ToWString(path)); - if (dir != nullptr) { - std::vector files = dir->getFiles(); - foreach (FileEntry::Ptr file, files) { - IOrganizer::FileInfo info; - info.filePath = ToQString(file->getFullPath()); - bool fromArchive = false; - info.origins.append(ToQString( - m_DirectoryStructure->getOriginByID(file->getOrigin(fromArchive)) - .getName())); - info.archive = fromArchive ? ToQString(file->getArchive()) : ""; - foreach (auto idx, file->getAlternatives()) { - info.origins.append( - ToQString(m_DirectoryStructure->getOriginByID(idx.first).getName())); - } - - if (filter(info)) { - result.append(info); - } - } - } - return result; -} - -DownloadManager *OrganizerCore::downloadManager() -{ - return &m_DownloadManager; -} - -PluginList *OrganizerCore::pluginList() -{ - return &m_PluginList; -} - -ModList *OrganizerCore::modList() -{ - return &m_ModList; -} - -QStringList OrganizerCore::modsSortedByProfilePriority() const -{ - QStringList res; - for (unsigned int i = 0; i < currentProfile()->numRegularMods(); ++i) { - int modIndex = currentProfile()->modIndexByPriority(i); - res.push_back(ModInfo::getByIndex(modIndex)->name()); - } - return res; -} - -void OrganizerCore::spawnBinary(const QFileInfo &binary, const QString &arguments, const QDir ¤tDirectory, const QString &steamAppID, const QString &customOverwrite) -{ - DWORD processExitCode = 0; - HANDLE processHandle = spawnBinaryDirect(binary, arguments, m_CurrentProfile->name(), currentDirectory, steamAppID, customOverwrite, &processExitCode); - if (processHandle != INVALID_HANDLE_VALUE) { - refreshDirectoryStructure(); - // need to remove our stored load order because it may be outdated if a foreign tool changed the - // file time. After removing that file, refreshESPList will use the file time as the order - if (managedGame()->loadOrderMechanism() == IPluginGame::LoadOrderMechanism::FileTime) { - qDebug("removing loadorder.txt"); - QFile::remove(m_CurrentProfile->getLoadOrderFileName()); +HANDLE OrganizerCore::spawnBinaryProcess(const QFileInfo& binary, const QString& arguments, const QString& profileName, + const QDir& currentDirectory, const QString& steamAppID, + const QString& customOverwrite) { + prepareStart(); + + if (!binary.exists()) { + reportError(tr("Executable \"%1\" not found").arg(binary.absoluteFilePath())); + return INVALID_HANDLE_VALUE; } - refreshDirectoryStructure(); - refreshESPList(); - savePluginList(); - - //These callbacks should not fiddle with directoy structure and ESPs. - m_FinishedRun(binary.absoluteFilePath(), processExitCode); - } -} - -HANDLE OrganizerCore::spawnBinaryDirect(const QFileInfo &binary, - const QString &arguments, - const QString &profileName, - const QDir ¤tDirectory, - const QString &steamAppID, - const QString &customOverwrite, - LPDWORD exitCode) -{ - HANDLE processHandle = spawnBinaryProcess(binary, arguments, profileName, currentDirectory, steamAppID, customOverwrite); - if (processHandle != INVALID_HANDLE_VALUE) { - std::unique_ptr dlg; - ILockedWaitingForProcess* uilock = nullptr; + if (!steamAppID.isEmpty()) { + ::SetEnvironmentVariableW(L"SteamAPPId", ToWString(steamAppID).c_str()); + } else { + ::SetEnvironmentVariableW(L"SteamAPPId", ToWString(m_Settings.getSteamAppID()).c_str()); + } - if (m_UserInterface != nullptr) { - uilock = m_UserInterface->lock(); + // This could possibly be extracted somewhere else but it's probably for when + // we have more than one provider of game registration. + if ((QFileInfo(managedGame()->gameDirectory().absoluteFilePath("steam_api.dll")).exists() || + QFileInfo(managedGame()->gameDirectory().absoluteFilePath("steam_api64.dll")).exists()) && + (m_Settings.getLoadMechanism() == LoadMechanism::LOAD_MODORGANIZER)) { + if (!testForSteam()) { + QWidget* window = qApp->activeWindow(); + if ((window != nullptr) && (!window->isVisible())) { + window = nullptr; + } + if (QuestionBoxMemory::query(window, "steamQuery", binary.fileName(), tr("Start Steam?"), + tr("Steam is required to be running already to correctly start the game. " + "Should MO try to start steam now?"), + QDialogButtonBox::Yes | QDialogButtonBox::No) == QDialogButtonBox::Yes) { + startSteam(qApp->activeWindow()); + } + } } - else { - // i.e. when running command line shortcuts there is no m_UserInterface - dlg.reset(new LockedDialog); - dlg->show(); - dlg->setEnabled(true); - uilock = dlg.get(); + + while (m_DirectoryUpdate) { + ::Sleep(100); + QCoreApplication::processEvents(); } - ON_BLOCK_EXIT([&]() { - if (m_UserInterface != nullptr) { - m_UserInterface->unlock(); - } }); - - DWORD ignoreExitCode; - waitForProcessCompletion(processHandle, exitCode ? exitCode : &ignoreExitCode, uilock); - cycleDiagnostics(); - } - - return processHandle; -} - - -HANDLE OrganizerCore::spawnBinaryProcess(const QFileInfo &binary, - const QString &arguments, - const QString &profileName, - const QDir ¤tDirectory, - const QString &steamAppID, - const QString &customOverwrite) -{ - prepareStart(); - - if (!binary.exists()) { - reportError( - tr("Executable \"%1\" not found").arg(binary.absoluteFilePath())); - return INVALID_HANDLE_VALUE; - } - - if (!steamAppID.isEmpty()) { - ::SetEnvironmentVariableW(L"SteamAPPId", ToWString(steamAppID).c_str()); - } else { - ::SetEnvironmentVariableW(L"SteamAPPId", - ToWString(m_Settings.getSteamAppID()).c_str()); - } - - // This could possibly be extracted somewhere else but it's probably for when - // we have more than one provider of game registration. - if ((QFileInfo( - managedGame()->gameDirectory().absoluteFilePath("steam_api.dll")) - .exists() - || QFileInfo(managedGame()->gameDirectory().absoluteFilePath( - "steam_api64.dll")) - .exists()) - && (m_Settings.getLoadMechanism() == LoadMechanism::LOAD_MODORGANIZER)) { - if (!testForSteam()) { - QWidget *window = qApp->activeWindow(); - if ((window != nullptr) && (!window->isVisible())) { - window = nullptr; - } - if (QuestionBoxMemory::query(window, "steamQuery", binary.fileName(), - tr("Start Steam?"), - tr("Steam is required to be running already to correctly start the game. " - "Should MO try to start steam now?"), - QDialogButtonBox::Yes | QDialogButtonBox::No) == QDialogButtonBox::Yes) { - startSteam(qApp->activeWindow()); - } - } - } - - while (m_DirectoryUpdate) { - ::Sleep(100); - QCoreApplication::processEvents(); - } - - // need to make sure all data is saved before we start the application - if (m_CurrentProfile != nullptr) { - m_CurrentProfile->writeModlistNow(true); - } - - // TODO: should also pass arguments - if (m_AboutToRun(binary.absoluteFilePath())) { - try { - m_USVFS.updateMapping(fileMapping(profileName, customOverwrite)); - } catch (const std::exception &e) { - QMessageBox::warning(qApp->activeWindow(), tr("Error"), e.what()); - return INVALID_HANDLE_VALUE; - } - - QString modsPath = settings().getModDirectory(); - - // Check if this a request with either an executable or a working directory under our mods folder - // then will start the processs 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); - cwdPath = m_GamePlugin->dataDirectory().absolutePath() + cwdPath.mid(cwdOffset, -1); - } - - if (virtualizedBin) { - int binOffset = binPath.indexOf('/', modsPath.length() + 1); - binPath = m_GamePlugin->dataDirectory().absolutePath() + binPath.mid(binOffset, -1); - } - - QString cmdline - = QString("launch \"%1\" \"%2\" %3") - .arg(QDir::toNativeSeparators(cwdPath), - QDir::toNativeSeparators(binPath), arguments); - - qDebug() << "Spawning proxyed process <" << cmdline << ">"; - - return startBinary(QFileInfo(QCoreApplication::applicationFilePath()), - cmdline, QCoreApplication::applicationDirPath(), true); - } else { - qDebug() << "Spawning direct process <" << binPath << "," << arguments << "," << cwdPath << ">"; - return startBinary(binary, arguments, currentDirectory, true); - } - } else { - qDebug("start of \"%s\" canceled by plugin", - qPrintable(binary.absoluteFilePath())); - return INVALID_HANDLE_VALUE; - } -} - -HANDLE OrganizerCore::runShortcut(const MOShortcut& shortcut) -{ - if (shortcut.hasInstance() && shortcut.instance() != InstanceManager::instance().currentInstance()) - throw std::runtime_error( - QString("Refusing to run executable from different instance %1:%2") - .arg(shortcut.instance(),shortcut.executable()) - .toLocal8Bit().constData()); - - Executable& exe = m_ExecutablesList.find(shortcut.executable()); - - return spawnBinaryDirect( - exe.m_BinaryInfo, exe.m_Arguments, - m_CurrentProfile->name(), - exe.m_WorkingDirectory.length() != 0 - ? exe.m_WorkingDirectory - : exe.m_BinaryInfo.absolutePath(), - exe.m_SteamAppID, ""); -} - -HANDLE OrganizerCore::startApplication(const QString &executable, - const QStringList &args, - const QString &cwd, - const QString &profile) -{ - QFileInfo binary; - QString arguments = args.join(" "); - QString currentDirectory = cwd; - QString profileName = profile; - if (profile.length() == 0) { + // need to make sure all data is saved before we start the application if (m_CurrentProfile != nullptr) { - profileName = m_CurrentProfile->name(); + m_CurrentProfile->writeModlistNow(true); + } + + // TODO: should also pass arguments + if (m_AboutToRun(binary.absoluteFilePath())) { + try { + m_USVFS.updateMapping(fileMapping(profileName, customOverwrite)); + } catch (const std::exception& e) { + QMessageBox::warning(qApp->activeWindow(), tr("Error"), e.what()); + return INVALID_HANDLE_VALUE; + } + + QString modsPath = settings().getModDirectory(); + + // Check if this a request with either an executable or a working directory under our mods folder + // then will start the processs 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); + cwdPath = m_GamePlugin->dataDirectory().absolutePath() + cwdPath.mid(cwdOffset, -1); + } + + if (virtualizedBin) { + int binOffset = binPath.indexOf('/', modsPath.length() + 1); + binPath = m_GamePlugin->dataDirectory().absolutePath() + binPath.mid(binOffset, -1); + } + + QString cmdline = QString("launch \"%1\" \"%2\" %3") + .arg(QDir::toNativeSeparators(cwdPath), QDir::toNativeSeparators(binPath), arguments); + + qDebug() << "Spawning proxyed process <" << cmdline << ">"; + + return startBinary(QFileInfo(QCoreApplication::applicationFilePath()), cmdline, + QCoreApplication::applicationDirPath(), true); + } else { + qDebug() << "Spawning direct process <" << binPath << "," << arguments << "," << cwdPath << ">"; + return startBinary(binary, arguments, currentDirectory, true); + } } else { - throw MyException(tr("No profile set")); + qDebug("start of \"%s\" canceled by plugin", qPrintable(binary.absoluteFilePath())); + return INVALID_HANDLE_VALUE; } - } - QString steamAppID; - QString customOverwrite; - if (executable.contains('\\') || executable.contains('/')) { - // file path +} - binary = QFileInfo(executable); - if (binary.isRelative()) { - // relative path, should be relative to game directory - binary = QFileInfo( - managedGame()->gameDirectory().absoluteFilePath(executable)); +HANDLE OrganizerCore::runShortcut(const MOShortcut& shortcut) { + if (shortcut.hasInstance() && shortcut.instance() != InstanceManager::instance().currentInstance()) + throw std::runtime_error(QString("Refusing to run executable from different instance %1:%2") + .arg(shortcut.instance(), shortcut.executable()) + .toLocal8Bit() + .constData()); + + Executable& exe = m_ExecutablesList.find(shortcut.executable()); + + return spawnBinaryDirect(exe.m_BinaryInfo, exe.m_Arguments, m_CurrentProfile->name(), + exe.m_WorkingDirectory.length() != 0 ? exe.m_WorkingDirectory + : exe.m_BinaryInfo.absolutePath(), + exe.m_SteamAppID, ""); +} + +HANDLE OrganizerCore::startApplication(const QString& executable, const QStringList& args, const QString& cwd, + const QString& profile) { + QFileInfo binary; + QString arguments = args.join(" "); + QString currentDirectory = cwd; + QString profileName = profile; + if (profile.length() == 0) { + if (m_CurrentProfile != nullptr) { + profileName = m_CurrentProfile->name(); + } else { + throw MyException(tr("No profile set")); + } } - if (cwd.length() == 0) { - currentDirectory = binary.absolutePath(); + QString steamAppID; + QString customOverwrite; + if (executable.contains('\\') || executable.contains('/')) { + // file path + + binary = QFileInfo(executable); + if (binary.isRelative()) { + // relative path, should be relative to game directory + binary = QFileInfo(managedGame()->gameDirectory().absoluteFilePath(executable)); + } + if (cwd.length() == 0) { + currentDirectory = binary.absolutePath(); + } + try { + const Executable& exe = m_ExecutablesList.findByBinary(binary); + steamAppID = exe.m_SteamAppID; + customOverwrite = m_CurrentProfile->setting("custom_overwrites", exe.m_Title).toString(); + } catch (const std::runtime_error&) { + // nop + } + } else { + // only a file name, search executables list + try { + const Executable& exe = m_ExecutablesList.find(executable); + steamAppID = exe.m_SteamAppID; + customOverwrite = m_CurrentProfile->setting("custom_overwrites", exe.m_Title).toString(); + if (arguments == "") { + arguments = exe.m_Arguments; + } + binary = exe.m_BinaryInfo; + if (cwd.length() == 0) { + currentDirectory = exe.m_WorkingDirectory; + } + } catch (const std::runtime_error&) { + qWarning("\"%s\" not set up as executable", executable.toUtf8().constData()); + binary = QFileInfo(executable); + } } - try { - const Executable &exe = m_ExecutablesList.findByBinary(binary); - steamAppID = exe.m_SteamAppID; - customOverwrite - = m_CurrentProfile->setting("custom_overwrites", exe.m_Title) - .toString(); - } catch (const std::runtime_error &) { - // nop - } - } else { - // only a file name, search executables list - try { - const Executable &exe = m_ExecutablesList.find(executable); - steamAppID = exe.m_SteamAppID; - customOverwrite - = m_CurrentProfile->setting("custom_overwrites", exe.m_Title) - .toString(); - if (arguments == "") { - arguments = exe.m_Arguments; - } - binary = exe.m_BinaryInfo; - if (cwd.length() == 0) { - currentDirectory = exe.m_WorkingDirectory; - } - } catch (const std::runtime_error &) { - qWarning("\"%s\" not set up as executable", - executable.toUtf8().constData()); - binary = QFileInfo(executable); - } - } - - return spawnBinaryDirect(binary, arguments, profileName, currentDirectory, steamAppID, customOverwrite); -} - -bool OrganizerCore::waitForApplication(HANDLE handle, LPDWORD exitCode) -{ - ILockedWaitingForProcess* uilock = nullptr; - if (m_UserInterface != nullptr) { - uilock = m_UserInterface->lock(); - } - - ON_BLOCK_EXIT([&] () { + + return spawnBinaryDirect(binary, arguments, profileName, currentDirectory, steamAppID, customOverwrite); +} + +bool OrganizerCore::waitForApplication(HANDLE handle, LPDWORD exitCode) { + ILockedWaitingForProcess* uilock = nullptr; if (m_UserInterface != nullptr) { - m_UserInterface->unlock(); - } }); - return waitForProcessCompletion(handle, exitCode, uilock); -} - -bool OrganizerCore::waitForProcessCompletion(HANDLE handle, LPDWORD exitCode, ILockedWaitingForProcess* uilock) -{ - bool originalHandle = true; - bool newHandle = true; - bool uiunlocked = false; - - DWORD currentPID = 0; - QString processName; - auto waitForChildUntil = GetTickCount64(); - if (handle != INVALID_HANDLE_VALUE) { - currentPID = GetProcessId(handle); - processName = QString::fromStdWString(getProcessName(handle)); - } - - // Certain process names we wish to "hide" for aesthetic reason: - bool waitingOnHidden = false; - std::vector hiddenList; - hiddenList.push_back(QFileInfo(QCoreApplication::applicationFilePath()).fileName()); - for (QString hide : hiddenList) - if (processName.contains(hide, Qt::CaseInsensitive)) - waitingOnHidden = true; - // The main reason for adding the hidden list is to hide the MO proxy we use to spawn virtualized processes. - // On the one hand we want to display the real executable without it feeling laggy, on the other we don't want - // to requery processes all the time if for some reason we are waiting on hidden processes and find no "unhidden" - // process. For this reason we use exponential backoff and also start with a delibrately low value to improve - // the responsiveness of the initial update - DWORD64 nextHiddenCheck = GetTickCount64(); - DWORD64 nextHiddenCheckDelay = 50; - - constexpr DWORD INPUT_EVENT = WAIT_OBJECT_0 + 1; - DWORD res = WAIT_TIMEOUT; - while (handle != INVALID_HANDLE_VALUE && (newHandle || res == WAIT_TIMEOUT || res == INPUT_EVENT)) - { - if (newHandle) { - processName += QString(" (%1)").arg(currentPID); - if (uilock) - uilock->setProcessName(processName); - qDebug() << "Waiting for" - << (originalHandle ? "spawned" : "usvfs") - << "process completion :" << processName.toUtf8().constData(); - newHandle = false; - } - - // Wait for a an event on the handle, a key press, mouse click or timeout - res = MsgWaitForMultipleObjects(1, &handle, FALSE, 200, QS_KEY | QS_MOUSEBUTTON); - if (res == WAIT_FAILED) { - qWarning() << "Failed waiting for process completion : MsgWaitForMultipleObjects WAIT_FAILED" << GetLastError(); - break; - } - - // keep processing events so the app doesn't appear dead - QCoreApplication::sendPostedEvents(); - QCoreApplication::processEvents(); - - if (uilock && uilock->unlockForced()) { - uiunlocked = true; - break; - } - - if (res == WAIT_OBJECT_0) { - // process we were waiting on has completed - if (originalHandle && exitCode && !::GetExitCodeProcess(handle, exitCode)) - qWarning() << "Failed getting exit code of complete process :" << GetLastError(); - CloseHandle(handle); - handle = INVALID_HANDLE_VALUE; - originalHandle = false; - // if the previous process spawned a child process and immediately exits we may miss it if we check immediately - waitForChildUntil = GetTickCount64() + 800; - } - - // search for another process to wait on if either: - // 1. we just completed waiting for a process and need to find/wait for an inject child - // 2. we are currently waiting on a hidden process so periodically check if there is a non-hidden process to wait on - bool firstIteration = true; - while ((handle == INVALID_HANDLE_VALUE && GetTickCount64() <= waitForChildUntil) - || (waitingOnHidden && GetTickCount64() >= nextHiddenCheck)) - { - if (firstIteration) - firstIteration = false; - else { - QThread::msleep(200); - QCoreApplication::sendPostedEvents(); - QCoreApplication::processEvents(); - } + uilock = m_UserInterface->lock(); + } + + ON_BLOCK_EXIT([&]() { + if (m_UserInterface != nullptr) { + m_UserInterface->unlock(); + } + }); + return waitForProcessCompletion(handle, exitCode, uilock); +} - // search if there is another usvfs process active - handle = findAndOpenAUSVFSProcess(hiddenList, currentPID); - waitingOnHidden = false; - newHandle = handle != INVALID_HANDLE_VALUE; - if (newHandle) { +bool OrganizerCore::waitForProcessCompletion(HANDLE handle, LPDWORD exitCode, ILockedWaitingForProcess* uilock) { + bool originalHandle = true; + bool newHandle = true; + bool uiunlocked = false; + + DWORD currentPID = 0; + QString processName; + auto waitForChildUntil = GetTickCount64(); + if (handle != INVALID_HANDLE_VALUE) { currentPID = GetProcessId(handle); processName = QString::fromStdWString(getProcessName(handle)); - for (QString hide : hiddenList) - if (processName.contains(hide, Qt::CaseInsensitive)) + } + + // Certain process names we wish to "hide" for aesthetic reason: + bool waitingOnHidden = false; + std::vector hiddenList; + hiddenList.push_back(QFileInfo(QCoreApplication::applicationFilePath()).fileName()); + for (QString hide : hiddenList) + if (processName.contains(hide, Qt::CaseInsensitive)) waitingOnHidden = true; - } - if (waitingOnHidden) { - nextHiddenCheck = GetTickCount64() + nextHiddenCheckDelay; - nextHiddenCheckDelay = std::min(nextHiddenCheckDelay * 2, (DWORD64) 2000); - } - else { - nextHiddenCheck = GetTickCount64(); - nextHiddenCheckDelay = 200; - } + // The main reason for adding the hidden list is to hide the MO proxy we use to spawn virtualized processes. + // On the one hand we want to display the real executable without it feeling laggy, on the other we don't want + // to requery processes all the time if for some reason we are waiting on hidden processes and find no "unhidden" + // process. For this reason we use exponential backoff and also start with a delibrately low value to improve + // the responsiveness of the initial update + DWORD64 nextHiddenCheck = GetTickCount64(); + DWORD64 nextHiddenCheckDelay = 50; + + constexpr DWORD INPUT_EVENT = WAIT_OBJECT_0 + 1; + DWORD res = WAIT_TIMEOUT; + while (handle != INVALID_HANDLE_VALUE && (newHandle || res == WAIT_TIMEOUT || res == INPUT_EVENT)) { + if (newHandle) { + processName += QString(" (%1)").arg(currentPID); + if (uilock) + uilock->setProcessName(processName); + qDebug() << "Waiting for" << (originalHandle ? "spawned" : "usvfs") + << "process completion :" << processName.toUtf8().constData(); + newHandle = false; + } + + // Wait for a an event on the handle, a key press, mouse click or timeout + res = MsgWaitForMultipleObjects(1, &handle, FALSE, 200, QS_KEY | QS_MOUSEBUTTON); + if (res == WAIT_FAILED) { + qWarning() << "Failed waiting for process completion : MsgWaitForMultipleObjects WAIT_FAILED" + << GetLastError(); + break; + } + + // keep processing events so the app doesn't appear dead + QCoreApplication::sendPostedEvents(); + QCoreApplication::processEvents(); + + if (uilock && uilock->unlockForced()) { + uiunlocked = true; + break; + } + + if (res == WAIT_OBJECT_0) { + // process we were waiting on has completed + if (originalHandle && exitCode && !::GetExitCodeProcess(handle, exitCode)) + qWarning() << "Failed getting exit code of complete process :" << GetLastError(); + CloseHandle(handle); + handle = INVALID_HANDLE_VALUE; + originalHandle = false; + // if the previous process spawned a child process and immediately exits we may miss it if we check + // immediately + waitForChildUntil = GetTickCount64() + 800; + } + + // search for another process to wait on if either: + // 1. we just completed waiting for a process and need to find/wait for an inject child + // 2. we are currently waiting on a hidden process so periodically check if there is a non-hidden process to + // wait on + bool firstIteration = true; + while ((handle == INVALID_HANDLE_VALUE && GetTickCount64() <= waitForChildUntil) || + (waitingOnHidden && GetTickCount64() >= nextHiddenCheck)) { + if (firstIteration) + firstIteration = false; + else { + QThread::msleep(200); + QCoreApplication::sendPostedEvents(); + QCoreApplication::processEvents(); + } + + // search if there is another usvfs process active + handle = findAndOpenAUSVFSProcess(hiddenList, currentPID); + waitingOnHidden = false; + newHandle = handle != INVALID_HANDLE_VALUE; + if (newHandle) { + currentPID = GetProcessId(handle); + processName = QString::fromStdWString(getProcessName(handle)); + for (QString hide : hiddenList) + if (processName.contains(hide, Qt::CaseInsensitive)) + waitingOnHidden = true; + } + if (waitingOnHidden) { + nextHiddenCheck = GetTickCount64() + nextHiddenCheckDelay; + nextHiddenCheckDelay = std::min(nextHiddenCheckDelay * 2, (DWORD64)2000); + } else { + nextHiddenCheck = GetTickCount64(); + nextHiddenCheckDelay = 200; + } + } } - } - if (res == WAIT_OBJECT_0) - qDebug() << "Waiting for process completion successfull"; - else if (uiunlocked) - qDebug() << "Waiting for process completion aborted by UI"; - else - qDebug() << "Waiting for process completion not successfull :" << res; + if (res == WAIT_OBJECT_0) + qDebug() << "Waiting for process completion successfull"; + else if (uiunlocked) + qDebug() << "Waiting for process completion aborted by UI"; + else + qDebug() << "Waiting for process completion not successfull :" << res; - if (handle != INVALID_HANDLE_VALUE) - ::CloseHandle(handle); + if (handle != INVALID_HANDLE_VALUE) + ::CloseHandle(handle); - return res == WAIT_OBJECT_0; + return res == WAIT_OBJECT_0; } HANDLE OrganizerCore::findAndOpenAUSVFSProcess(const std::vector& hiddenList, DWORD preferedParentPid) { - // for practical reasons a querySize of 1 is probably enough, we use a larger query as a heuristics - // to find a more "aesthetic injected processes (attempting to comply to hiddenList and preferedParentPid) - constexpr size_t querySize = 100; - DWORD pids[querySize]; - size_t found = querySize; - if (!::GetVFSProcessList(&found, pids)) { - qWarning() << "Failed seeking USVFS processes : GetVFSProcessList failed?!"; - return INVALID_HANDLE_VALUE; - } - - HANDLE best_match = INVALID_HANDLE_VALUE; - bool best_match_hidden = true; - for (size_t i = 0; i < found; ++i) { - if (pids[i] == GetCurrentProcessId()) - continue; // obviously don't wait for MO process - - HANDLE handle = ::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION | SYNCHRONIZE, FALSE, pids[i]); - if (handle == INVALID_HANDLE_VALUE) { - qWarning() << "Failed openning USVFS process " << pids[i] << " : OpenProcess failed" << GetLastError(); - continue; - } - - QString pname = QString::fromStdWString(getProcessName(handle)); - bool phidden = false; - for (auto hide : hiddenList) - if (pname.contains(hide, Qt::CaseInsensitive)) - phidden = true; - - bool pprefered = preferedParentPid && getProcessParentID(pids[i]) == preferedParentPid; - - if (best_match == INVALID_HANDLE_VALUE || best_match_hidden || (!phidden && pprefered)) { - if (best_match != INVALID_HANDLE_VALUE) - CloseHandle(best_match); - best_match = handle; - best_match_hidden = phidden; + // for practical reasons a querySize of 1 is probably enough, we use a larger query as a heuristics + // to find a more "aesthetic injected processes (attempting to comply to hiddenList and preferedParentPid) + constexpr size_t querySize = 100; + DWORD pids[querySize]; + size_t found = querySize; + if (!::GetVFSProcessList(&found, pids)) { + qWarning() << "Failed seeking USVFS processes : GetVFSProcessList failed?!"; + return INVALID_HANDLE_VALUE; } - else - CloseHandle(handle); - if (!phidden && pprefered) - return best_match; - } + HANDLE best_match = INVALID_HANDLE_VALUE; + bool best_match_hidden = true; + for (size_t i = 0; i < found; ++i) { + if (pids[i] == GetCurrentProcessId()) + continue; // obviously don't wait for MO process + + HANDLE handle = ::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION | SYNCHRONIZE, FALSE, pids[i]); + if (handle == INVALID_HANDLE_VALUE) { + qWarning() << "Failed openning USVFS process " << pids[i] << " : OpenProcess failed" << GetLastError(); + continue; + } + + QString pname = QString::fromStdWString(getProcessName(handle)); + bool phidden = false; + for (auto hide : hiddenList) + if (pname.contains(hide, Qt::CaseInsensitive)) + phidden = true; - return best_match; + bool pprefered = preferedParentPid && getProcessParentID(pids[i]) == preferedParentPid; + + if (best_match == INVALID_HANDLE_VALUE || best_match_hidden || (!phidden && pprefered)) { + if (best_match != INVALID_HANDLE_VALUE) + CloseHandle(best_match); + best_match = handle; + best_match_hidden = phidden; + } else + CloseHandle(handle); + + if (!phidden && pprefered) + return best_match; + } + + return best_match; } -bool OrganizerCore::onAboutToRun( - const std::function &func) -{ - auto conn = m_AboutToRun.connect(func); - return conn.connected(); +bool OrganizerCore::onAboutToRun(const std::function& func) { + auto conn = m_AboutToRun.connect(func); + return conn.connected(); } -bool OrganizerCore::onFinishedRun( - const std::function &func) -{ - auto conn = m_FinishedRun.connect(func); - return conn.connected(); +bool OrganizerCore::onFinishedRun(const std::function& func) { + auto conn = m_FinishedRun.connect(func); + return conn.connected(); } -bool OrganizerCore::onModInstalled( - const std::function &func) -{ - auto conn = m_ModInstalled.connect(func); - return conn.connected(); +bool OrganizerCore::onModInstalled(const std::function& func) { + auto conn = m_ModInstalled.connect(func); + return conn.connected(); } -void OrganizerCore::refreshModList(bool saveChanges) -{ - // don't lose changes! - if (saveChanges) { - m_CurrentProfile->writeModlistNow(true); - } - ModInfo::updateFromDisc(m_Settings.getModDirectory(), &m_DirectoryStructure, - m_Settings.displayForeign(), managedGame()); +void OrganizerCore::refreshModList(bool saveChanges) { + // don't lose changes! + if (saveChanges) { + m_CurrentProfile->writeModlistNow(true); + } + ModInfo::updateFromDisc(m_Settings.getModDirectory(), &m_DirectoryStructure, m_Settings.displayForeign(), + managedGame()); - m_CurrentProfile->refreshModStatus(); + m_CurrentProfile->refreshModStatus(); - m_ModList.notifyChange(-1); + m_ModList.notifyChange(-1); - refreshDirectoryStructure(); + refreshDirectoryStructure(); } -void OrganizerCore::refreshESPList() -{ - if (m_DirectoryUpdate) { - // don't mess up the esp list if we're currently updating the directory - // structure - m_PostRefreshTasks.append([this]() { - this->refreshESPList(); - }); - return; - } - m_CurrentProfile->writeModlist(); +void OrganizerCore::refreshESPList() { + if (m_DirectoryUpdate) { + // don't mess up the esp list if we're currently updating the directory + // structure + m_PostRefreshTasks.append([this]() { this->refreshESPList(); }); + return; + } + m_CurrentProfile->writeModlist(); - // clear list - try { - m_PluginList.refresh(m_CurrentProfile->name(), *m_DirectoryStructure, - m_CurrentProfile->getLockedOrderFileName()); - } catch (const std::exception &e) { - reportError(tr("Failed to refresh list of esps: %1").arg(e.what())); - } + // clear list + try { + m_PluginList.refresh(m_CurrentProfile->name(), *m_DirectoryStructure, + m_CurrentProfile->getLockedOrderFileName()); + } catch (const std::exception& e) { + reportError(tr("Failed to refresh list of esps: %1").arg(e.what())); + } } -void OrganizerCore::refreshBSAList() -{ - DataArchives *archives = m_GamePlugin->feature(); +void OrganizerCore::refreshBSAList() { + DataArchives* archives = m_GamePlugin->feature(); + + if (archives != nullptr) { + m_ArchivesInit = false; + + // default archives are the ones enabled outside MO. if the list can't be + // found (which might + // happen if ini files are missing) use hard-coded defaults (preferrably the + // same the game would use) + m_DefaultArchives = archives->archives(m_CurrentProfile); + if (m_DefaultArchives.length() == 0) { + m_DefaultArchives = archives->vanillaArchives(); + } - if (archives != nullptr) { - m_ArchivesInit = false; + m_ActiveArchives.clear(); - // default archives are the ones enabled outside MO. if the list can't be - // found (which might - // happen if ini files are missing) use hard-coded defaults (preferrably the - // same the game would use) - m_DefaultArchives = archives->archives(m_CurrentProfile); - if (m_DefaultArchives.length() == 0) { - m_DefaultArchives = archives->vanillaArchives(); + auto iter = enabledArchives(); + m_ActiveArchives = toStringList(iter.begin(), iter.end()); + if (m_ActiveArchives.isEmpty()) { + m_ActiveArchives = m_DefaultArchives; + } + + if (m_UserInterface != nullptr) { + m_UserInterface->updateBSAList(m_DefaultArchives, m_ActiveArchives); + } + + m_ArchivesInit = true; } +} + +void OrganizerCore::refreshLists() { + if ((m_CurrentProfile != nullptr) && m_DirectoryStructure->isPopulated()) { + refreshESPList(); + refreshBSAList(); + } // no point in refreshing lists if no files have been added to the directory + // tree +} - m_ActiveArchives.clear(); +void OrganizerCore::updateModActiveState(int index, bool active) { + ModInfo::Ptr modInfo = ModInfo::getByIndex(index); + QDir dir(modInfo->absolutePath()); + for (const QString& esm : dir.entryList(QStringList() << "*.esm", QDir::Files)) { + m_PluginList.enableESP(esm, active); + } + int enabled = 0; + for (const QString& esl : dir.entryList(QStringList() << "*.esl", QDir::Files)) { + const FileEntry::Ptr file = m_DirectoryStructure->findFile(ToWString(esl)); + if (file.get() == nullptr) { + qWarning("failed to activate %s", qPrintable(esl)); + continue; + } - auto iter = enabledArchives(); - m_ActiveArchives = toStringList(iter.begin(), iter.end()); - if (m_ActiveArchives.isEmpty()) { - m_ActiveArchives = m_DefaultArchives; + if (active != m_PluginList.isEnabled(esl) && file->getAlternatives().empty()) { + m_PluginList.enableESP(esl, active); + ++enabled; + } } - + QStringList esps = dir.entryList(QStringList() << "*.esp", QDir::Files); + for (const QString& esp : esps) { + const FileEntry::Ptr file = m_DirectoryStructure->findFile(ToWString(esp)); + if (file.get() == nullptr) { + qWarning("failed to activate %s", qPrintable(esp)); + continue; + } + + if (active != m_PluginList.isEnabled(esp) && file->getAlternatives().empty()) { + m_PluginList.enableESP(esp, active); + ++enabled; + } + } + if (active && (enabled > 1)) { + MessageDialog::showMessage(tr("Multiple esps/esls activated, please check that they don't conflict."), + qApp->activeWindow()); + } + m_PluginList.refreshLoadOrder(); + // immediately save affected lists + m_PluginListsWriter.writeImmediately(false); +} + +void OrganizerCore::updateModInDirectoryStructure(unsigned int index, ModInfo::Ptr modInfo) { + // add files of the bsa to the directory structure + m_DirectoryRefresher.addModFilesToStructure(m_DirectoryStructure, modInfo->name(), + m_CurrentProfile->getModPriority(index), modInfo->absolutePath(), + modInfo->stealFiles()); + DirectoryRefresher::cleanStructure(m_DirectoryStructure); + // need to refresh plugin list now so we can activate esps + refreshESPList(); + // activate all esps of the specified mod so the bsas get activated along with + // it + updateModActiveState(index, true); + // now we need to refresh the bsa list and save it so there is no confusion + // about what archives are avaiable and active + refreshBSAList(); if (m_UserInterface != nullptr) { - m_UserInterface->updateBSAList(m_DefaultArchives, m_ActiveArchives); + m_UserInterface->archivesWriter().writeImmediately(false); + } + + std::vector archives = enabledArchives(); + m_DirectoryRefresher.setMods(m_CurrentProfile->getActiveMods(), + std::set(archives.begin(), archives.end())); + + // finally also add files from bsas to the directory structure + m_DirectoryRefresher.addModBSAToStructure(m_DirectoryStructure, modInfo->name(), + m_CurrentProfile->getModPriority(index), modInfo->absolutePath(), + modInfo->archives()); +} + +void OrganizerCore::requestDownload(const QUrl& url, QNetworkReply* reply) { + if (m_PluginContainer != nullptr) { + for (IPluginModPage* modPage : m_PluginContainer->plugins()) { + ModRepositoryFileInfo* fileInfo = new ModRepositoryFileInfo(); + if (modPage->handlesDownload(url, reply->url(), *fileInfo)) { + fileInfo->repository = modPage->name(); + m_DownloadManager.addDownload(reply, fileInfo); + return; + } + } } - m_ArchivesInit = true; - } + // no mod found that could handle the download. Is it a nexus mod? + if (url.host() == "www.nexusmods.com") { + int modID = 0; + int fileID = 0; + QRegExp modExp("mods/(\\d+)"); + if (modExp.indexIn(url.toString()) != -1) { + modID = modExp.cap(1).toInt(); + } + QRegExp fileExp("fid=(\\d+)"); + if (fileExp.indexIn(reply->url().toString()) != -1) { + fileID = fileExp.cap(1).toInt(); + } + m_DownloadManager.addDownload(reply, new ModRepositoryFileInfo(modID, fileID)); + } else { + if (QMessageBox::question(qApp->activeWindow(), tr("Download?"), + tr("A download has been started but no installed " + "page plugin recognizes it.\n" + "If you download anyway no information (i.e. " + "version) will be associated with the " + "download.\n" + "Continue?"), + QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { + m_DownloadManager.addDownload(reply, new ModRepositoryFileInfo()); + } + } } -void OrganizerCore::refreshLists() -{ - if ((m_CurrentProfile != nullptr) && m_DirectoryStructure->isPopulated()) { - refreshESPList(); - refreshBSAList(); - } // no point in refreshing lists if no files have been added to the directory - // tree -} - -void OrganizerCore::updateModActiveState(int index, bool active) -{ - ModInfo::Ptr modInfo = ModInfo::getByIndex(index); - QDir dir(modInfo->absolutePath()); - for (const QString &esm : - dir.entryList(QStringList() << "*.esm", QDir::Files)) { - m_PluginList.enableESP(esm, active); - } - int enabled = 0; - for (const QString &esl : - dir.entryList(QStringList() << "*.esl", QDir::Files)) { - const FileEntry::Ptr file = m_DirectoryStructure->findFile(ToWString(esl)); - if (file.get() == nullptr) { - qWarning("failed to activate %s", qPrintable(esl)); - continue; - } - - if (active != m_PluginList.isEnabled(esl) - && file->getAlternatives().empty()) { - m_PluginList.enableESP(esl, active); - ++enabled; - } - } - QStringList esps = dir.entryList(QStringList() << "*.esp", QDir::Files); - for (const QString &esp : esps) { - const FileEntry::Ptr file = m_DirectoryStructure->findFile(ToWString(esp)); - if (file.get() == nullptr) { - qWarning("failed to activate %s", qPrintable(esp)); - continue; - } - - if (active != m_PluginList.isEnabled(esp) - && file->getAlternatives().empty()) { - m_PluginList.enableESP(esp, active); - ++enabled; - } - } - if (active && (enabled > 1)) { - MessageDialog::showMessage( - tr("Multiple esps/esls activated, please check that they don't conflict."), - qApp->activeWindow()); - } - m_PluginList.refreshLoadOrder(); - // immediately save affected lists - m_PluginListsWriter.writeImmediately(false); -} - -void OrganizerCore::updateModInDirectoryStructure(unsigned int index, - ModInfo::Ptr modInfo) -{ - // add files of the bsa to the directory structure - m_DirectoryRefresher.addModFilesToStructure( - m_DirectoryStructure, modInfo->name(), - m_CurrentProfile->getModPriority(index), modInfo->absolutePath(), - modInfo->stealFiles()); - DirectoryRefresher::cleanStructure(m_DirectoryStructure); - // need to refresh plugin list now so we can activate esps - refreshESPList(); - // activate all esps of the specified mod so the bsas get activated along with - // it - updateModActiveState(index, true); - // now we need to refresh the bsa list and save it so there is no confusion - // about what archives are avaiable and active - refreshBSAList(); - if (m_UserInterface != nullptr) { - m_UserInterface->archivesWriter().writeImmediately(false); - } - - std::vector archives = enabledArchives(); - m_DirectoryRefresher.setMods( - m_CurrentProfile->getActiveMods(), - std::set(archives.begin(), archives.end())); - - // finally also add files from bsas to the directory structure - m_DirectoryRefresher.addModBSAToStructure( - m_DirectoryStructure, modInfo->name(), - m_CurrentProfile->getModPriority(index), modInfo->absolutePath(), - modInfo->archives()); -} - -void OrganizerCore::requestDownload(const QUrl &url, QNetworkReply *reply) -{ - if (m_PluginContainer != nullptr) { - for (IPluginModPage *modPage : - m_PluginContainer->plugins()) { - ModRepositoryFileInfo *fileInfo = new ModRepositoryFileInfo(); - if (modPage->handlesDownload(url, reply->url(), *fileInfo)) { - fileInfo->repository = modPage->name(); - m_DownloadManager.addDownload(reply, fileInfo); - return; - } - } - } - - // no mod found that could handle the download. Is it a nexus mod? - if (url.host() == "www.nexusmods.com") { - int modID = 0; - int fileID = 0; - QRegExp modExp("mods/(\\d+)"); - if (modExp.indexIn(url.toString()) != -1) { - modID = modExp.cap(1).toInt(); - } - QRegExp fileExp("fid=(\\d+)"); - if (fileExp.indexIn(reply->url().toString()) != -1) { - fileID = fileExp.cap(1).toInt(); - } - m_DownloadManager.addDownload(reply, - new ModRepositoryFileInfo(modID, fileID)); - } else { - if (QMessageBox::question(qApp->activeWindow(), tr("Download?"), - tr("A download has been started but no installed " - "page plugin recognizes it.\n" - "If you download anyway no information (i.e. " - "version) will be associated with the " - "download.\n" - "Continue?"), - QMessageBox::Yes | QMessageBox::No) - == QMessageBox::Yes) { - m_DownloadManager.addDownload(reply, new ModRepositoryFileInfo()); - } - } -} - -ModListSortProxy *OrganizerCore::createModListProxyModel() -{ - ModListSortProxy *result = new ModListSortProxy(m_CurrentProfile, this); - result->setSourceModel(&m_ModList); - return result; -} - -PluginListSortProxy *OrganizerCore::createPluginListProxyModel() -{ - PluginListSortProxy *result = new PluginListSortProxy(this); - result->setSourceModel(&m_PluginList); - return result; -} - -IPluginGame const *OrganizerCore::managedGame() const -{ - return m_GamePlugin; -} - -std::vector OrganizerCore::enabledArchives() -{ - std::vector result; - QFile archiveFile(m_CurrentProfile->getArchivesFileName()); - if (archiveFile.open(QIODevice::ReadOnly)) { - while (!archiveFile.atEnd()) { - result.push_back(QString::fromUtf8(archiveFile.readLine()).trimmed()); - } - archiveFile.close(); - } - return result; -} - -void OrganizerCore::refreshDirectoryStructure() -{ - if (!m_DirectoryUpdate) { - m_CurrentProfile->writeModlistNow(true); - - m_DirectoryUpdate = true; - std::vector> activeModList - = m_CurrentProfile->getActiveMods(); - auto archives = enabledArchives(); - m_DirectoryRefresher.setMods( - activeModList, std::set(archives.begin(), archives.end())); - - QTimer::singleShot(0, &m_DirectoryRefresher, SLOT(refresh())); - } -} - -void OrganizerCore::directory_refreshed() -{ - DirectoryEntry *newStructure = m_DirectoryRefresher.getDirectoryStructure(); - Q_ASSERT(newStructure != m_DirectoryStructure); - if (newStructure != nullptr) { - std::swap(m_DirectoryStructure, newStructure); - delete newStructure; - } else { - // TODO: don't know why this happens, this slot seems to get called twice - // with only one emit - return; - } - m_DirectoryUpdate = false; - - for (int i = 0; i < m_ModList.rowCount(); ++i) { - ModInfo::Ptr modInfo = ModInfo::getByIndex(i); - modInfo->clearCaches(); - } - for (auto task : m_PostRefreshTasks) { - task(); - } - m_PostRefreshTasks.clear(); - - if (m_CurrentProfile != nullptr) { - refreshLists(); - } +ModListSortProxy* OrganizerCore::createModListProxyModel() { + ModListSortProxy* result = new ModListSortProxy(m_CurrentProfile, this); + result->setSourceModel(&m_ModList); + return result; +} + +PluginListSortProxy* OrganizerCore::createPluginListProxyModel() { + PluginListSortProxy* result = new PluginListSortProxy(this); + result->setSourceModel(&m_PluginList); + return result; +} + +IPluginGame const* OrganizerCore::managedGame() const { return m_GamePlugin; } + +std::vector OrganizerCore::enabledArchives() { + std::vector result; + QFile archiveFile(m_CurrentProfile->getArchivesFileName()); + if (archiveFile.open(QIODevice::ReadOnly)) { + while (!archiveFile.atEnd()) { + result.push_back(QString::fromUtf8(archiveFile.readLine()).trimmed()); + } + archiveFile.close(); + } + return result; } -void OrganizerCore::profileRefresh() -{ - // have to refresh mods twice (again in refreshModList), otherwise the refresh - // isn't complete. Not sure why - ModInfo::updateFromDisc(m_Settings.getModDirectory(), &m_DirectoryStructure, - m_Settings.displayForeign(), managedGame()); - m_CurrentProfile->refreshModStatus(); +void OrganizerCore::refreshDirectoryStructure() { + if (!m_DirectoryUpdate) { + m_CurrentProfile->writeModlistNow(true); + + m_DirectoryUpdate = true; + std::vector> activeModList = m_CurrentProfile->getActiveMods(); + auto archives = enabledArchives(); + m_DirectoryRefresher.setMods(activeModList, std::set(archives.begin(), archives.end())); - refreshModList(); + QTimer::singleShot(0, &m_DirectoryRefresher, SLOT(refresh())); + } } -void OrganizerCore::modStatusChanged(unsigned int index) -{ - try { - ModInfo::Ptr modInfo = ModInfo::getByIndex(index); - if (m_CurrentProfile->modEnabled(index)) { - updateModInDirectoryStructure(index, modInfo); +void OrganizerCore::directory_refreshed() { + DirectoryEntry* newStructure = m_DirectoryRefresher.getDirectoryStructure(); + Q_ASSERT(newStructure != m_DirectoryStructure); + if (newStructure != nullptr) { + std::swap(m_DirectoryStructure, newStructure); + delete newStructure; } else { - updateModActiveState(index, false); - refreshESPList(); - if (m_DirectoryStructure->originExists(ToWString(modInfo->name()))) { - FilesOrigin &origin - = m_DirectoryStructure->getOriginByName(ToWString(modInfo->name())); - origin.enable(false); - } - if (m_UserInterface != nullptr) { - m_UserInterface->archivesWriter().write(); - } - } - modInfo->clearCaches(); - - for (unsigned int i = 0; i < m_CurrentProfile->numMods(); ++i) { - ModInfo::Ptr modInfo = ModInfo::getByIndex(i); - int priority = m_CurrentProfile->getModPriority(i); - if (m_DirectoryStructure->originExists(ToWString(modInfo->name()))) { - // priorities in the directory structure are one higher because data is - // 0 - m_DirectoryStructure->getOriginByName(ToWString(modInfo->name())) - .setPriority(priority + 1); - } - } - m_DirectoryStructure->getFileRegister()->sortOrigins(); + // TODO: don't know why this happens, this slot seems to get called twice + // with only one emit + return; + } + m_DirectoryUpdate = false; - refreshLists(); - } catch (const std::exception &e) { - reportError(tr("failed to update mod list: %1").arg(e.what())); - } -} - -void OrganizerCore::loginSuccessful(bool necessary) -{ - if (necessary) { - MessageDialog::showMessage(tr("login successful"), qApp->activeWindow()); - } - for (QString url : m_PendingDownloads) { - downloadRequestedNXM(url); - } - m_PendingDownloads.clear(); - for (auto task : m_PostLoginTasks) { - task(); - } - - m_PostLoginTasks.clear(); - NexusInterface::instance()->loginCompleted(); -} - -void OrganizerCore::loginSuccessfulUpdate(bool necessary) -{ - if (necessary) { - MessageDialog::showMessage(tr("login successful"), qApp->activeWindow()); - } - m_Updater.startUpdate(); -} - -void OrganizerCore::loginFailed(const QString &message) -{ - if (QMessageBox::question(qApp->activeWindow(), tr("Login failed"), - tr("Login failed, try again?")) - == QMessageBox::Yes) { - if (nexusLogin(true)) { - return; - } - } - - if (!m_PendingDownloads.isEmpty()) { - MessageDialog::showMessage( - tr("login failed: %1. Download will not be associated with an account") - .arg(message), - qApp->activeWindow()); + for (int i = 0; i < m_ModList.rowCount(); ++i) { + ModInfo::Ptr modInfo = ModInfo::getByIndex(i); + modInfo->clearCaches(); + } + for (auto task : m_PostRefreshTasks) { + task(); + } + m_PostRefreshTasks.clear(); + + if (m_CurrentProfile != nullptr) { + refreshLists(); + } +} + +void OrganizerCore::profileRefresh() { + // have to refresh mods twice (again in refreshModList), otherwise the refresh + // isn't complete. Not sure why + ModInfo::updateFromDisc(m_Settings.getModDirectory(), &m_DirectoryStructure, m_Settings.displayForeign(), + managedGame()); + m_CurrentProfile->refreshModStatus(); + + refreshModList(); +} + +void OrganizerCore::modStatusChanged(unsigned int index) { + try { + ModInfo::Ptr modInfo = ModInfo::getByIndex(index); + if (m_CurrentProfile->modEnabled(index)) { + updateModInDirectoryStructure(index, modInfo); + } else { + updateModActiveState(index, false); + refreshESPList(); + if (m_DirectoryStructure->originExists(ToWString(modInfo->name()))) { + FilesOrigin& origin = m_DirectoryStructure->getOriginByName(ToWString(modInfo->name())); + origin.enable(false); + } + if (m_UserInterface != nullptr) { + m_UserInterface->archivesWriter().write(); + } + } + modInfo->clearCaches(); + + for (unsigned int i = 0; i < m_CurrentProfile->numMods(); ++i) { + ModInfo::Ptr modInfo = ModInfo::getByIndex(i); + int priority = m_CurrentProfile->getModPriority(i); + if (m_DirectoryStructure->originExists(ToWString(modInfo->name()))) { + // priorities in the directory structure are one higher because data is + // 0 + m_DirectoryStructure->getOriginByName(ToWString(modInfo->name())).setPriority(priority + 1); + } + } + m_DirectoryStructure->getFileRegister()->sortOrigins(); + + refreshLists(); + } catch (const std::exception& e) { + reportError(tr("failed to update mod list: %1").arg(e.what())); + } +} + +void OrganizerCore::loginSuccessful(bool necessary) { + if (necessary) { + MessageDialog::showMessage(tr("login successful"), qApp->activeWindow()); + } for (QString url : m_PendingDownloads) { - downloadRequestedNXM(url); + downloadRequestedNXM(url); } m_PendingDownloads.clear(); - } else { - MessageDialog::showMessage(tr("login failed: %1").arg(message), - qApp->activeWindow()); + for (auto task : m_PostLoginTasks) { + task(); + } + m_PostLoginTasks.clear(); - } - NexusInterface::instance()->loginCompleted(); -} - -void OrganizerCore::loginFailedUpdate(const QString &message) -{ - MessageDialog::showMessage( - tr("login failed: %1. You need to log-in with Nexus to update MO.") - .arg(message), - qApp->activeWindow()); -} - -void OrganizerCore::syncOverwrite() -{ - unsigned int overwriteIndex = ModInfo::findMod([](ModInfo::Ptr mod) -> bool { - std::vector flags = mod->getFlags(); - return std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) - != flags.end(); - }); - - ModInfo::Ptr modInfo = ModInfo::getByIndex(overwriteIndex); - SyncOverwriteDialog syncDialog(modInfo->absolutePath(), m_DirectoryStructure, - qApp->activeWindow()); - if (syncDialog.exec() == QDialog::Accepted) { - syncDialog.apply(QDir::fromNativeSeparators(m_Settings.getModDirectory())); - modInfo->testValid(); - refreshDirectoryStructure(); - } -} - -QString OrganizerCore::oldMO1HookDll() const -{ - if (auto extender = managedGame()->feature()) { - QString hookdll = QDir::toNativeSeparators( - managedGame()->dataDirectory().absoluteFilePath(extender->PluginPath() + "/hook.dll")); - if (QFile(hookdll).exists()) - return hookdll; - } - return QString(); -} - -std::vector OrganizerCore::activeProblems() const -{ - std::vector problems; - const auto& hookdll = oldMO1HookDll(); - if (!hookdll.isEmpty()) { - // This warning will now be shown every time the problems are checked, which is a bit - // of a "log spam". But since this is a sevre error which will most likely make the - // game crash/freeze/etc. and is very hard to diagnose, this "log spam" will make it - // easier for the user to notice the warning. - qWarning("hook.dll found in game folder: %s", qPrintable(hookdll)); - problems.push_back(PROBLEM_MO1SCRIPTEXTENDERWORKAROUND); - } - return problems; -} - -QString OrganizerCore::shortDescription(unsigned int key) const -{ - switch (key) { - case PROBLEM_MO1SCRIPTEXTENDERWORKAROUND: { - return tr("MO1 \"Script Extender\" load mechanism has left hook.dll in your game folder"); - } break; - default: { - return tr("Description missing"); - } break; - } + NexusInterface::instance()->loginCompleted(); +} + +void OrganizerCore::loginSuccessfulUpdate(bool necessary) { + if (necessary) { + MessageDialog::showMessage(tr("login successful"), qApp->activeWindow()); + } + m_Updater.startUpdate(); +} + +void OrganizerCore::loginFailed(const QString& message) { + if (QMessageBox::question(qApp->activeWindow(), tr("Login failed"), tr("Login failed, try again?")) == + QMessageBox::Yes) { + if (nexusLogin(true)) { + return; + } + } + + if (!m_PendingDownloads.isEmpty()) { + MessageDialog::showMessage(tr("login failed: %1. Download will not be associated with an account").arg(message), + qApp->activeWindow()); + for (QString url : m_PendingDownloads) { + downloadRequestedNXM(url); + } + m_PendingDownloads.clear(); + } else { + MessageDialog::showMessage(tr("login failed: %1").arg(message), qApp->activeWindow()); + m_PostLoginTasks.clear(); + } + NexusInterface::instance()->loginCompleted(); +} + +void OrganizerCore::loginFailedUpdate(const QString& message) { + MessageDialog::showMessage(tr("login failed: %1. You need to log-in with Nexus to update MO.").arg(message), + qApp->activeWindow()); +} + +void OrganizerCore::syncOverwrite() { + unsigned int overwriteIndex = ModInfo::findMod([](ModInfo::Ptr mod) -> bool { + std::vector flags = mod->getFlags(); + return std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end(); + }); + + ModInfo::Ptr modInfo = ModInfo::getByIndex(overwriteIndex); + SyncOverwriteDialog syncDialog(modInfo->absolutePath(), m_DirectoryStructure, qApp->activeWindow()); + if (syncDialog.exec() == QDialog::Accepted) { + syncDialog.apply(QDir::fromNativeSeparators(m_Settings.getModDirectory())); + modInfo->testValid(); + refreshDirectoryStructure(); + } +} + +QString OrganizerCore::oldMO1HookDll() const { + if (auto extender = managedGame()->feature()) { + QString hookdll = QDir::toNativeSeparators( + managedGame()->dataDirectory().absoluteFilePath(extender->PluginPath() + "/hook.dll")); + if (QFile(hookdll).exists()) + return hookdll; + } + return QString(); +} + +std::vector OrganizerCore::activeProblems() const { + std::vector problems; + const auto& hookdll = oldMO1HookDll(); + if (!hookdll.isEmpty()) { + // This warning will now be shown every time the problems are checked, which is a bit + // of a "log spam". But since this is a sevre error which will most likely make the + // game crash/freeze/etc. and is very hard to diagnose, this "log spam" will make it + // easier for the user to notice the warning. + qWarning("hook.dll found in game folder: %s", qPrintable(hookdll)); + problems.push_back(PROBLEM_MO1SCRIPTEXTENDERWORKAROUND); + } + return problems; } -QString OrganizerCore::fullDescription(unsigned int key) const -{ - switch (key) { +QString OrganizerCore::shortDescription(unsigned int key) const { + switch (key) { case PROBLEM_MO1SCRIPTEXTENDERWORKAROUND: { - return tr("hook.dll has been found in your game folder (right click to copy the full path). " - "This is most likely a leftover of setting the ModOrganizer 1 load mechanism to \"Script Extender\", " - "in which case you must remove this file either by changing the load mechanism in ModOrganizer 1 or " - "manually removing the file, otherwise the game is likely to crash and burn.").arg(oldMO1HookDll()); - break; - } - default: { - return tr("Description missing"); + return tr("MO1 \"Script Extender\" load mechanism has left hook.dll in your game folder"); } break; - } + default: { return tr("Description missing"); } break; + } } -bool OrganizerCore::hasGuidedFix(unsigned int) const -{ - return false; +QString OrganizerCore::fullDescription(unsigned int key) const { + switch (key) { + case PROBLEM_MO1SCRIPTEXTENDERWORKAROUND: { + return tr("hook.dll has been found in your game folder (right click to copy the full path). " + "This is most likely a leftover of setting the ModOrganizer 1 load mechanism to \"Script Extender\", " + "in which case you must remove this file either by changing the load mechanism in ModOrganizer 1 or " + "manually removing the file, otherwise the game is likely to crash and burn.") + .arg(oldMO1HookDll()); + break; + } + default: { return tr("Description missing"); } break; + } } -void OrganizerCore::startGuidedFix(unsigned int) const -{ -} +bool OrganizerCore::hasGuidedFix(unsigned int) const { return false; } -bool OrganizerCore::saveCurrentLists() -{ - if (m_DirectoryUpdate) { - qWarning("not saving lists during directory update"); - return false; - } +void OrganizerCore::startGuidedFix(unsigned int) const {} - try { - savePluginList(); - if (m_UserInterface != nullptr) { - m_UserInterface->archivesWriter().write(); +bool OrganizerCore::saveCurrentLists() { + if (m_DirectoryUpdate) { + qWarning("not saving lists during directory update"); + return false; } - } catch (const std::exception &e) { - reportError(tr("failed to save load order: %1").arg(e.what())); - } - return true; + try { + savePluginList(); + if (m_UserInterface != nullptr) { + m_UserInterface->archivesWriter().write(); + } + } catch (const std::exception& e) { + reportError(tr("failed to save load order: %1").arg(e.what())); + } + + return true; } -void OrganizerCore::savePluginList() -{ - if (m_DirectoryUpdate) { - // delay save till after directory update - m_PostRefreshTasks.append([this]() { - this->savePluginList(); - }); - return; - } - m_PluginList.saveTo(m_CurrentProfile->getLockedOrderFileName(), - m_CurrentProfile->getDeleterFileName(), - m_Settings.hideUncheckedPlugins()); - m_PluginList.saveLoadOrder(*m_DirectoryStructure); +void OrganizerCore::savePluginList() { + if (m_DirectoryUpdate) { + // delay save till after directory update + m_PostRefreshTasks.append([this]() { this->savePluginList(); }); + return; + } + m_PluginList.saveTo(m_CurrentProfile->getLockedOrderFileName(), m_CurrentProfile->getDeleterFileName(), + m_Settings.hideUncheckedPlugins()); + m_PluginList.saveLoadOrder(*m_DirectoryStructure); } -void OrganizerCore::prepareStart() -{ - if (m_CurrentProfile == nullptr) { - return; - } - m_CurrentProfile->writeModlist(); - m_CurrentProfile->createTweakedIniFile(); - saveCurrentLists(); - m_Settings.setupLoadMechanism(); - storeSettings(); +void OrganizerCore::prepareStart() { + if (m_CurrentProfile == nullptr) { + return; + } + m_CurrentProfile->writeModlist(); + m_CurrentProfile->createTweakedIniFile(); + saveCurrentLists(); + m_Settings.setupLoadMechanism(); + storeSettings(); } -std::vector OrganizerCore::fileMapping(const QString &profileName, - const QString &customOverwrite) -{ - // need to wait until directory structure - while (m_DirectoryUpdate) { - ::Sleep(100); - QCoreApplication::processEvents(); - } +std::vector OrganizerCore::fileMapping(const QString& profileName, const QString& customOverwrite) { + // need to wait until directory structure + while (m_DirectoryUpdate) { + ::Sleep(100); + QCoreApplication::processEvents(); + } + + IPluginGame* game = qApp->property("managed_game").value(); + Profile profile(QDir(m_Settings.getProfileDirectory() + "/" + profileName), game); - IPluginGame *game = qApp->property("managed_game").value(); - Profile profile(QDir(m_Settings.getProfileDirectory() + "/" + profileName), - game); + MappingType result; - MappingType result; + QString dataPath = QDir::toNativeSeparators(game->dataDirectory().absolutePath()); - QString dataPath - = QDir::toNativeSeparators(game->dataDirectory().absolutePath()); + bool overwriteActive = false; - bool overwriteActive = false; + for (auto mod : profile.getActiveMods()) { + if (std::get<0>(mod).compare("overwrite", Qt::CaseInsensitive) == 0) { + continue; + } + + unsigned int modIndex = ModInfo::getIndex(std::get<0>(mod)); + ModInfo::Ptr modPtr = ModInfo::getByIndex(modIndex); + + bool createTarget = customOverwrite == std::get<0>(mod); + + overwriteActive |= createTarget; - for (auto mod : profile.getActiveMods()) { - if (std::get<0>(mod).compare("overwrite", Qt::CaseInsensitive) == 0) { - continue; + if (modPtr->isRegular()) { + result.insert(result.end(), {QDir::toNativeSeparators(std::get<1>(mod)), dataPath, true, createTarget}); + } } - unsigned int modIndex = ModInfo::getIndex(std::get<0>(mod)); - ModInfo::Ptr modPtr = ModInfo::getByIndex(modIndex); + if (!overwriteActive && !customOverwrite.isEmpty()) { + throw MyException(tr("The designated write target \"%1\" is not enabled.").arg(customOverwrite)); + } - bool createTarget = customOverwrite == std::get<0>(mod); + if (m_CurrentProfile->localSavesEnabled()) { + LocalSavegames* localSaves = game->feature(); + if (localSaves != nullptr) { + MappingType saveMap = localSaves->mappings(currentProfile()->absolutePath() + "/saves"); + result.reserve(result.size() + saveMap.size()); + result.insert(result.end(), saveMap.begin(), saveMap.end()); + } else { + qWarning("local save games not supported by this game plugin"); + } + } - overwriteActive |= createTarget; + result.insert(result.end(), {QDir::toNativeSeparators(m_Settings.getOverwriteDirectory()), dataPath, true, + customOverwrite.isEmpty()}); - if (modPtr->isRegular()) { - result.insert(result.end(), {QDir::toNativeSeparators(std::get<1>(mod)), - dataPath, true, createTarget}); + for (MOBase::IPluginFileMapper* mapper : m_PluginContainer->plugins()) { + IPlugin* plugin = dynamic_cast(mapper); + if (plugin->isActive()) { + MappingType pluginMap = mapper->mappings(); + result.reserve(result.size() + pluginMap.size()); + result.insert(result.end(), pluginMap.begin(), pluginMap.end()); + } } - } - if (!overwriteActive && !customOverwrite.isEmpty()) { - throw MyException(tr("The designated write target \"%1\" is not enabled.") - .arg(customOverwrite)); - } + return result; +} - if (m_CurrentProfile->localSavesEnabled()) { - LocalSavegames *localSaves = game->feature(); - if (localSaves != nullptr) { - MappingType saveMap - = localSaves->mappings(currentProfile()->absolutePath() + "/saves"); - result.reserve(result.size() + saveMap.size()); - result.insert(result.end(), saveMap.begin(), saveMap.end()); - } else { - qWarning("local save games not supported by this game plugin"); +std::vector OrganizerCore::fileMapping(const QString& dataPath, const QString& relPath, + const DirectoryEntry* base, const DirectoryEntry* directoryEntry, + int createDestination) { + std::vector result; + + for (FileEntry::Ptr current : directoryEntry->getFiles()) { + bool isArchive = false; + int origin = current->getOrigin(isArchive); + if (isArchive || (origin == 0)) { + continue; + } + + QString originPath = QString::fromStdWString(base->getOriginByID(origin).getPath()); + QString fileName = QString::fromStdWString(current->getName()); + // QString fileName = ToQString(current->getName()); + QString source = originPath + relPath + fileName; + QString target = dataPath + relPath + fileName; + if (source != target) { + result.push_back({source, target, false, false}); + } } - } - result.insert(result.end(), { - QDir::toNativeSeparators(m_Settings.getOverwriteDirectory()), - dataPath, - true, - customOverwrite.isEmpty() - }); + // recurse into subdirectories + std::vector::const_iterator current, end; + directoryEntry->getSubDirectories(current, end); + for (; current != end; ++current) { + int origin = (*current)->anyOrigin(); - for (MOBase::IPluginFileMapper *mapper : - m_PluginContainer->plugins()) { - IPlugin *plugin = dynamic_cast(mapper); - if (plugin->isActive()) { - MappingType pluginMap = mapper->mappings(); - result.reserve(result.size() + pluginMap.size()); - result.insert(result.end(), pluginMap.begin(), pluginMap.end()); - } - } - - return result; -} - - -std::vector OrganizerCore::fileMapping( - const QString &dataPath, const QString &relPath, const DirectoryEntry *base, - const DirectoryEntry *directoryEntry, int createDestination) -{ - std::vector result; - - for (FileEntry::Ptr current : directoryEntry->getFiles()) { - bool isArchive = false; - int origin = current->getOrigin(isArchive); - if (isArchive || (origin == 0)) { - continue; - } - - QString originPath - = QString::fromStdWString(base->getOriginByID(origin).getPath()); - QString fileName = QString::fromStdWString(current->getName()); -// QString fileName = ToQString(current->getName()); - QString source = originPath + relPath + fileName; - QString target = dataPath + relPath + fileName; - if (source != target) { - result.push_back({source, target, false, false}); - } - } - - // recurse into subdirectories - std::vector::const_iterator current, end; - directoryEntry->getSubDirectories(current, end); - for (; current != end; ++current) { - int origin = (*current)->anyOrigin(); - - QString originPath - = QString::fromStdWString(base->getOriginByID(origin).getPath()); - QString dirName = QString::fromStdWString((*current)->getName()); - QString source = originPath + relPath + dirName; - QString target = dataPath + relPath + dirName; - - bool writeDestination - = (base == directoryEntry) && (origin == createDestination); - - result.push_back({source, target, true, writeDestination}); - std::vector subRes = fileMapping( - dataPath, relPath + dirName + "\\", base, *current, createDestination); - result.insert(result.end(), subRes.begin(), subRes.end()); - } - return result; + QString originPath = QString::fromStdWString(base->getOriginByID(origin).getPath()); + QString dirName = QString::fromStdWString((*current)->getName()); + QString source = originPath + relPath + dirName; + QString target = dataPath + relPath + dirName; + + bool writeDestination = (base == directoryEntry) && (origin == createDestination); + + result.push_back({source, target, true, writeDestination}); + std::vector subRes = + fileMapping(dataPath, relPath + dirName + "\\", base, *current, createDestination); + result.insert(result.end(), subRes.begin(), subRes.end()); + } + return result; } -- cgit v1.3.1