From ea6292168a6acd4c263913f0ccd7dd64daf4f5cf Mon Sep 17 00:00:00 2001 From: Sandro Jäckel Date: Fri, 23 Feb 2018 01:45:58 +0100 Subject: Revert "Applied clang-format on source" This reverts commit 5e5c9c07291f6b09623d31c92b1fb61c4ede576e. --- src/organizercore.cpp | 3491 +++++++++++++++++++++++++++---------------------- 1 file changed, 1893 insertions(+), 1598 deletions(-) (limited to 'src/organizercore.cpp') diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 4b6567c5..b84488da 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 -#include +#include "nxmaccessmanager.h" #include +#include #include -#include -#include +#include #include -#include -#include #include +#include +#include "appconfig.h" +#include +#include +#include "lockeddialog.h" +#include "instancemanager.h" +#include #include #include @@ -51,1834 +51,2129 @@ #include #include -#include // for _tcsicmp #include +#include // for _tcsicmp #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"); +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)) { 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)) { - 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); + } + } + } + + 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"); } - - 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); +void OrganizerCore::connectPlugins(PluginContainer *container) +{ + m_DownloadManager.setSupportedExtensions( + m_InstallationManager.getSupportedExtensions()); + m_PluginContainer = container; - 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 (!m_GameName.isEmpty()) { + m_GamePlugin = m_PluginContainer->managedGame(m_GameName); + emit managedGameChanged(m_GamePlugin); + } } -void OrganizerCore::connectPlugins(PluginContainer* container) { - m_DownloadManager.setSupportedExtensions(m_InstallationManager.getSupportedExtensions()); - m_PluginContainer = container; +void OrganizerCore::disconnectPlugins() +{ + m_AboutToRun.disconnect_all_slots(); + m_FinishedRun.disconnect_all_slots(); + m_ModInstalled.disconnect_all_slots(); + m_ModList.disconnectSlots(); + m_PluginList.disconnectSlots(); - if (!m_GameName.isEmpty()) { - m_GamePlugin = m_PluginContainer->managedGame(m_GameName); - emit managedGameChanged(m_GamePlugin); - } + m_Settings.clearPlugins(); + m_GamePlugin = nullptr; + m_PluginContainer = nullptr; } -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; +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::setManagedGame(MOBase::IPluginGame* game) { - m_GameName = game->gameName(); - m_GamePlugin = game; - qApp->setProperty("managed_game", QVariant::fromValue(m_GamePlugin)); - emit managedGameChanged(m_GamePlugin); +Settings &OrganizerCore::settings() +{ + return m_Settings; } -Settings& OrganizerCore::settings() { return m_Settings; } - -bool OrganizerCore::nexusLogin(bool retry) { - NXMAccessManager* accessManager = NexusInterface::instance()->getAccessManager(); +bool OrganizerCore::nexusLogin(bool retry) +{ + NXMAccessManager *accessManager + = NexusInterface::instance()->getAccessManager(); - if ((accessManager->loginAttempted() || accessManager->loggedIn()) && !retry) { - // previous attempt, maybe even successful - return false; + 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; } 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; - } else { - // no credentials stored and user didn't enter them - accessManager->refuseLogin(); - return false; - } + // 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); } + return true; + } else { + 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); - } - return true; - } else { - return false; - } +void OrganizerCore::startMOUpdate() +{ + if (nexusLogin()) { + m_PostLoginTasks.append([&]() { m_Updater.startUpdate(); }); + } else { + m_Updater.startUpdate(); + } } -void OrganizerCore::startMOUpdate() { - if (nexusLogin()) { - m_PostLoginTasks.append([&]() { m_Updater.startUpdate(); }); - } else { - m_Updater.startUpdate(); - } +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::downloadRequestedNXM(const QString& url) { - qDebug("download requested: %s", qPrintable(url)); - if (nexusLogin()) { - m_PendingDownloads.append(url); - } else { - m_DownloadManager.addNXMDownload(url); - } +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::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::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::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::removeOrigin(const QString &name) +{ + FilesOrigin &origin = m_DirectoryStructure->getOriginByName(ToWString(name)); + origin.enable(false); + refreshLists(); } -void OrganizerCore::removeOrigin(const QString& name) { - FilesOrigin& origin = m_DirectoryStructure->getOriginByName(ToWString(name)); - origin.enable(false); - refreshLists(); +void OrganizerCore::downloadSpeed(const QString &serverName, int bytesPerSecond) +{ + m_Settings.setDownloadSpeed(serverName, bytesPerSecond); } -void OrganizerCore::downloadSpeed(const QString& serverName, int bytesPerSecond) { - m_Settings.setDownloadSpeed(serverName, bytesPerSecond); +InstallationManager *OrganizerCore::installationManager() +{ + return &m_InstallationManager; } -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::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(); } - -QString OrganizerCore::profileName() const { - if (m_CurrentProfile != nullptr) { - return m_CurrentProfile->name(); - } else { - return ""; - } +MOBase::IModRepositoryBridge *OrganizerCore::createNexusBridge() const +{ + return new NexusBridge(); } -QString OrganizerCore::profilePath() const { - if (m_CurrentProfile != nullptr) { - return m_CurrentProfile->absolutePath(); - } else { - return ""; - } +QString OrganizerCore::profileName() const +{ + if (m_CurrentProfile != nullptr) { + return m_CurrentProfile->name(); + } else { + return ""; + } } -QString OrganizerCore::downloadsPath() const { return QDir::fromNativeSeparators(m_Settings.getDownloadDirectory()); } +QString OrganizerCore::profilePath() const +{ + if (m_CurrentProfile != nullptr) { + return m_CurrentProfile->absolutePath(); + } else { + return ""; + } +} -QString OrganizerCore::overwritePath() const { return QDir::fromNativeSeparators(m_Settings.getOverwriteDirectory()); } +QString OrganizerCore::downloadsPath() const +{ + return QDir::fromNativeSeparators(m_Settings.getDownloadDirectory()); +} -QString OrganizerCore::basePath() const { return QDir::fromNativeSeparators(m_Settings.getBaseDirectory()); } +QString OrganizerCore::overwritePath() const +{ + return QDir::fromNativeSeparators(m_Settings.getOverwriteDirectory()); +} -MOBase::VersionInfo OrganizerCore::appVersion() const { return m_Updater.getVersion(); } +QString OrganizerCore::basePath() const +{ + return QDir::fromNativeSeparators(m_Settings.getBaseDirectory()); +} -MOBase::IModInterface* OrganizerCore::getMod(const QString& name) const { - unsigned int index = ModInfo::getIndex(name); - return index == UINT_MAX ? nullptr : ModInfo::getByIndex(index).data(); +MOBase::VersionInfo OrganizerCore::appVersion() const +{ + return m_Updater.getVersion(); } -MOBase::IModInterface* OrganizerCore::createMod(GuessedValue& name) { - bool merge = false; - if (!m_InstallationManager.testOverwrite(name, &merge)) { - return nullptr; - } +MOBase::IModInterface *OrganizerCore::getMod(const QString &name) const +{ + unsigned int index = ModInfo::getIndex(name); + return index == UINT_MAX ? nullptr : ModInfo::getByIndex(index).data(); +} - m_InstallationManager.setModsDirectory(m_Settings.getModDirectory()); +MOBase::IModInterface *OrganizerCore::createMod(GuessedValue &name) +{ + bool merge = false; + if (!m_InstallationManager.testOverwrite(name, &merge)) { + return nullptr; + } - QString targetDirectory = QDir::fromNativeSeparators(m_Settings.getModDirectory()).append("/").append(name); + m_InstallationManager.setModsDirectory(m_Settings.getModDirectory()); - QSettings settingsFile(targetDirectory + "/meta.ini", QSettings::IniFormat); + QString targetDirectory + = QDir::fromNativeSeparators(m_Settings.getModDirectory()) + .append("/") + .append(name); - if (!merge) { - settingsFile.setValue("modid", 0); - settingsFile.setValue("version", ""); - settingsFile.setValue("newestVersion", ""); - settingsFile.setValue("category", 0); - settingsFile.setValue("installationFile", ""); + QSettings settingsFile(targetDirectory + "/meta.ini", QSettings::IniFormat); - settingsFile.beginWriteArray("installedFiles", 0); - settingsFile.endArray(); - } + if (!merge) { + settingsFile.setValue("modid", 0); + settingsFile.setValue("version", ""); + settingsFile.setValue("newestVersion", ""); + settingsFile.setValue("category", 0); + settingsFile.setValue("installationFile", ""); - return ModInfo::createFrom(QDir(targetDirectory), &m_DirectoryStructure).data(); -} + settingsFile.beginWriteArray("installedFiles", 0); + settingsFile.endArray(); + } -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); - } + return ModInfo::createFrom(QDir(targetDirectory), &m_DirectoryStructure) + .data(); } -void OrganizerCore::modDataChanged(MOBase::IModInterface*) { refreshModList(false); } - -QVariant OrganizerCore::pluginSetting(const QString& pluginName, const QString& key) const { - return m_Settings.pluginSetting(pluginName, key); +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::setPluginSetting(const QString& pluginName, const QString& key, const QVariant& value) { - m_Settings.setPluginSetting(pluginName, key, value); +void OrganizerCore::modDataChanged(MOBase::IModInterface *) +{ + refreshModList(false); } -QVariant OrganizerCore::persistent(const QString& pluginName, const QString& key, const QVariant& def) const { - return m_Settings.pluginPersistent(pluginName, key, def); +QVariant OrganizerCore::pluginSetting(const QString &pluginName, + const QString &key) const +{ + return m_Settings.pluginSetting(pluginName, key); } -void OrganizerCore::setPersistent(const QString& pluginName, const QString& key, const QVariant& value, bool sync) { - m_Settings.setPluginPersistent(pluginName, key, value, sync); +void OrganizerCore::setPluginSetting(const QString &pluginName, + const QString &key, const QVariant &value) +{ + m_Settings.setPluginSetting(pluginName, key, value); } -QString OrganizerCore::pluginDataPath() const { - return qApp->applicationDirPath() + "/" + ToQString(AppConfig::pluginPath()) + "/data"; +QVariant OrganizerCore::persistent(const QString &pluginName, + const QString &key, + const QVariant &def) const +{ + return m_Settings.pluginPersistent(pluginName, key, def); } -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; +void OrganizerCore::setPersistent(const QString &pluginName, const QString &key, + const QVariant &value, bool sync) +{ + m_Settings.setPluginPersistent(pluginName, key, value, sync); } -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(); - } - } - } - - 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::pluginDataPath() const +{ + return qApp->applicationDirPath() + "/" + ToQString(AppConfig::pluginPath()) + + "/data"; } -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(); - } -} +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(); -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())); - } - } + 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 { - 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); + 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; - 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())); + // 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(); } - } 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; } + m_CurrentProfile->writeModlistNow(); -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()); + 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); } - refreshDirectoryStructure(); - refreshESPList(); - savePluginList(); + m_ModInstalled(modName); + } else { + reportError(tr("mod \"%1\" not found").arg(modName)); + } + m_DownloadManager.markInstalled(index); - // These callbacks should not fiddle with directoy structure and ESPs. - m_FinishedRun(binary.absoluteFilePath(), processExitCode); + 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()); + } } -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; +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::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; +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()); } + refreshDirectoryStructure(); - if (!steamAppID.isEmpty()) { - ::SetEnvironmentVariableW(L"SteamAPPId", ToWString(steamAppID).c_str()); - } else { - ::SetEnvironmentVariableW(L"SteamAPPId", ToWString(m_Settings.getSteamAppID()).c_str()); - } + 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; - // 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()); - } - } + if (m_UserInterface != nullptr) { + uilock = m_UserInterface->lock(); } - - while (m_DirectoryUpdate) { - ::Sleep(100); - QCoreApplication::processEvents(); + 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(); } - // need to make sure all data is saved before we start the application + 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) { 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); - } + profileName = m_CurrentProfile->name(); } else { - qDebug("start of \"%s\" canceled by plugin", qPrintable(binary.absoluteFilePath())); - return INVALID_HANDLE_VALUE; + throw MyException(tr("No profile set")); } -} + } + QString steamAppID; + QString customOverwrite; + if (executable.contains('\\') || executable.contains('/')) { + // file path -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")); - } + binary = QFileInfo(executable); + if (binary.isRelative()) { + // relative path, should be relative to game directory + binary = QFileInfo( + managedGame()->gameDirectory().absoluteFilePath(executable)); } - 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); - } + if (cwd.length() == 0) { + currentDirectory = binary.absolutePath(); } - - return spawnBinaryDirect(binary, arguments, profileName, currentDirectory, steamAppID, customOverwrite); -} - -bool OrganizerCore::waitForApplication(HANDLE handle, LPDWORD exitCode) { - ILockedWaitingForProcess* uilock = nullptr; + 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([&] () { if (m_UserInterface != nullptr) { - uilock = m_UserInterface->lock(); - } - - ON_BLOCK_EXIT([&]() { - 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; + 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(); + } - DWORD currentPID = 0; - QString processName; - auto waitForChildUntil = GetTickCount64(); - if (handle != INVALID_HANDLE_VALUE) { + // 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)); - } - - // 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)) + 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(); - } - - // 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 (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; + // 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; } + else + CloseHandle(handle); - 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; - } else - CloseHandle(handle); - - if (!phidden && pprefered) - return best_match; - } + if (!phidden && pprefered) + return best_match; + } - 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(); - - 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(); - } +void OrganizerCore::refreshBSAList() +{ + DataArchives *archives = m_GamePlugin->feature(); - m_ActiveArchives.clear(); + if (archives != nullptr) { + m_ArchivesInit = false; - 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; + // 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(); } -} - -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; - } + m_ActiveArchives.clear(); - if (active != m_PluginList.isEnabled(esl) && file->getAlternatives().empty()) { - m_PluginList.enableESP(esl, active); - ++enabled; - } + auto iter = enabledArchives(); + m_ActiveArchives = toStringList(iter.begin(), iter.end()); + if (m_ActiveArchives.isEmpty()) { + m_ActiveArchives = m_DefaultArchives; } - 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()); - } + m_UserInterface->updateBSAList(m_DefaultArchives, m_ActiveArchives); } -} - -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())); - } + m_ArchivesInit = true; + } } -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 +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; - } - 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(); - } + } + } + } + + // 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(); + } } -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::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(); + 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::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(); -void OrganizerCore::loginSuccessful(bool necessary) { - if (necessary) { - MessageDialog::showMessage(tr("login successful"), qApp->activeWindow()); - } + 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 (QString url : m_PendingDownloads) { - downloadRequestedNXM(url); + 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 (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), + } else { + MessageDialog::showMessage(tr("login failed: %1").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) { + 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"); + return tr("MO1 \"Script Extender\" load mechanism has left hook.dll in your game folder"); } break; - default: { return tr("Description missing"); } break; - } + default: { + return tr("Description missing"); + } break; + } } -QString OrganizerCore::fullDescription(unsigned int key) const { - switch (key) { +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; - } + 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; + } } -bool OrganizerCore::hasGuidedFix(unsigned int) const { return false; } +bool OrganizerCore::hasGuidedFix(unsigned int) const +{ + return false; +} -void OrganizerCore::startGuidedFix(unsigned int) const {} +void OrganizerCore::startGuidedFix(unsigned int) const +{ +} -bool OrganizerCore::saveCurrentLists() { - if (m_DirectoryUpdate) { - qWarning("not saving lists during directory update"); - return false; - } +bool OrganizerCore::saveCurrentLists() +{ + if (m_DirectoryUpdate) { + qWarning("not saving lists during directory update"); + return false; + } - 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())); + 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; + 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(); - } - - IPluginGame* game = qApp->property("managed_game").value(); - Profile profile(QDir(m_Settings.getProfileDirectory() + "/" + profileName), game); +std::vector OrganizerCore::fileMapping(const QString &profileName, + const QString &customOverwrite) +{ + // need to wait until directory structure + while (m_DirectoryUpdate) { + ::Sleep(100); + QCoreApplication::processEvents(); + } - MappingType result; + IPluginGame *game = qApp->property("managed_game").value(); + Profile profile(QDir(m_Settings.getProfileDirectory() + "/" + profileName), + game); - QString dataPath = QDir::toNativeSeparators(game->dataDirectory().absolutePath()); + MappingType result; - bool overwriteActive = false; + QString dataPath + = QDir::toNativeSeparators(game->dataDirectory().absolutePath()); - 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; + bool overwriteActive = false; - if (modPtr->isRegular()) { - result.insert(result.end(), {QDir::toNativeSeparators(std::get<1>(mod)), dataPath, true, createTarget}); - } + for (auto mod : profile.getActiveMods()) { + if (std::get<0>(mod).compare("overwrite", Qt::CaseInsensitive) == 0) { + continue; } - if (!overwriteActive && !customOverwrite.isEmpty()) { - throw MyException(tr("The designated write target \"%1\" is not enabled.").arg(customOverwrite)); - } + unsigned int modIndex = ModInfo::getIndex(std::get<0>(mod)); + ModInfo::Ptr modPtr = ModInfo::getByIndex(modIndex); - 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"); - } - } + bool createTarget = customOverwrite == std::get<0>(mod); - result.insert(result.end(), {QDir::toNativeSeparators(m_Settings.getOverwriteDirectory()), dataPath, true, - customOverwrite.isEmpty()}); + overwriteActive |= 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 (modPtr->isRegular()) { + result.insert(result.end(), {QDir::toNativeSeparators(std::get<1>(mod)), + dataPath, true, createTarget}); } + } - return result; -} - -std::vector OrganizerCore::fileMapping(const QString& dataPath, const QString& relPath, - const DirectoryEntry* base, const DirectoryEntry* directoryEntry, - int createDestination) { - std::vector result; + if (!overwriteActive && !customOverwrite.isEmpty()) { + throw MyException(tr("The designated write target \"%1\" is not enabled.") + .arg(customOverwrite)); + } - 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}); - } + 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"); } + } - // 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.insert(result.end(), { + QDir::toNativeSeparators(m_Settings.getOverwriteDirectory()), + dataPath, + true, + customOverwrite.isEmpty() + }); - 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; + 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; } -- cgit v1.3.1