Compare commits

...
18 Commits
Author SHA1 Message Date
LePresidente afa0cb9446 Version Bump,
Basic SkyrimSE changes
2016-12-12 05:12:20 -08:00
lepresidente 0657774e2e Use my repo for the updates for now. 2016-12-12 08:26:19 +02:00
lepresidente 695593968b updated email for dump reports. 2016-12-04 18:15:59 +02:00
lepresidente 61edeb12f1 gameShortName has been replaced by gameNexusName due to syrimse on nexus using skyrimspecialediton. 2016-12-04 17:56:07 +02:00
lepresidente db6b536fb1 Updated icu libs to 54 in release build. 2016-11-30 20:38:24 +02:00
Tannin 64590a10d7 Merge branch 'master' into new_vfs_library 2016-06-30 22:15:50 +02:00
TanninOne 4a582e524d Merge pull request #837 from ThosRTanner/issue/309
Changes for TanninOne/modorganizer#309 (3 of 3)
2016-06-30 19:03:45 +02:00
Tannin a00cda607c updated to changed usvfs api 2016-06-30 18:56:41 +02:00
Thomas Tanner 42407866e9 Some include-what-you-use suggested changes 2016-06-27 21:21:54 +01:00
Thomas Tanner c3a8a0b9eb Moved QuestionBoxMemory clearance from settings into QuestionBoxMemory 2016-06-26 20:27:50 +01:00
Thomas Tanner 607b2a10aa Restore commented out code 2016-06-26 20:27:13 +01:00
Thomas Tanner 6b60d54853 Extra question box functionality 2016-06-25 14:03:37 +01:00
Thomas Tanner c7101be7d8 Some refactoring of the spawn code and to make waitForApplication get the right error code (usually)
A note: It is possible for the executed program to completely exit before MO attempts to get hold of the pid from the job handle,
in which case strangeness will happen (this has always been an issue)
2016-06-25 11:47:15 +01:00
Thomas Tanner d80a77a4cb Fix a small bug in listing forward references 2016-06-25 09:47:28 +01:00
Tannin 33b544112c fixed categories list didn't get updated on switching instance 2016-06-23 21:38:54 +02:00
Tannin 8a3af93b39 fixed creation of data paths after changing paths in settings and update of profile list after change 2016-06-23 21:00:46 +02:00
Thomas Tanner a625acd17b Merge remote-tracking branch 'refs/remotes/TanninOne/master' 2016-05-15 12:23:55 +01:00
Thomas Tanner f5a815185f Updates for QT5.5 2016-05-15 11:03:43 +01:00
27 changed files with 810 additions and 689 deletions
+3 -1
View File
@@ -669,7 +669,9 @@ if qt_env['QT_MAJOR_VERSION'] > 4:
dlls_to_install += [
os.path.join(env['QTDIR'],
'bin',
'icu%s%d%d.dll' % (lib, qt_env['QT_MAJOR_VERSION'], qt_env['QT_MINOR_VERSION'] - 1))
'icu%s%d%d.dll' % (lib,
qt_env['QT_MAJOR_VERSION'],
qt_env['QT_MINOR_VERSION'] - 1))
for lib in ('dt','in', 'uc')
]
+13 -10
View File
@@ -35,7 +35,7 @@ removing = None
includes = dict()
adding = None
added = dict()
added = defaultdict(list)
lcadded = dict()
foundline = None
@@ -70,10 +70,10 @@ def process_next_line(line, outfile):
elif adding:
m = re.match(r'.*class (.*);', line)
if m:
added[m.group(1)] = (adding, line)
added[m.group(1)].append((adding, line))
lcadded[m.group(1).lower() + '.h'] = m.group(1)
else:
added[line] = (adding, line)
added[line].append((adding, line))
elif removing:
# Really we should stash these so that if we get a 'class xxx' in
# the add lines we can print it here. also we could do the case
@@ -88,10 +88,12 @@ def process_next_line(line, outfile):
if clname in lcadded:
clname = lcadded[clname]
if clname in added:
messages[removing].append(
'%s(%s) : warning I0004: Replace include of %s with '
'forward reference %s' % (
removing, m.group(2), m.group(1), added[clname][1]))
for item in added[clname]:
if removing == item[0]:
messages[removing].append(
'%s(%s) : warning I0004: Replace include of %s'
' with forward reference %s' % (
removing, m.group(2), m.group(1), item[1]))
del added[clname]
else:
messages[removing].append(
@@ -140,9 +142,10 @@ if foundline is None:
foundline = '1'
for add in added:
messages[added[add][0]].append(
'%s(%s) : warning I0003: Need to include %s' % (
added[add][0], foundline, added[add][1]))
for item in added[add]:
messages[item[0]].append(
'%s(%s) : warning I0003: Need to include %s' % (
item[0], foundline, item[1]))
for file in sorted(messages.keys(), reverse = True):
for line in messages[file]:
+4
View File
@@ -44,6 +44,10 @@ QString CategoryFactory::categoriesFilePath()
CategoryFactory::CategoryFactory()
{
atexit(&cleanup);
}
void CategoryFactory::loadCategories()
{
reset();
QFile categoryFile(categoriesFilePath());
+5
View File
@@ -75,6 +75,11 @@ public:
**/
void reset();
/**
* @brief read categories from file
*/
void loadCategories();
/**
* @brief save the categories to the categories.dat file
**/
+3 -3
View File
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity type="win32" name="dlls" version="1.0.0.0" processorArchitecture="x86"/>
<file name="icuin53.dll"/>
<file name="icuuc53.dll"/>
<file name="icudt53.dll"/>
<file name="icuin54.dll"/>
<file name="icuuc54.dll"/>
<file name="icudt54.dll"/>
<file name="Qt5Cored.dll"/>
<file name="Qt5Declaratived.dll"/>
<file name="Qt5Guid.dll"/>
+3 -3
View File
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity type="win32" name="dlls" version="1.0.0.0" processorArchitecture="x86"/>
<file name="icuin53.dll"/>
<file name="icuuc53.dll"/>
<file name="icudt53.dll"/>
<file name="icuin54.dll"/>
<file name="icuuc54.dll"/>
<file name="icudt54.dll"/>
<file name="libeay32.dll"/>
<file name="ssleay32.dll"/>
<file name="Qt5Core.dll"/>
+1
View File
@@ -39,6 +39,7 @@ IGameInfo::Type GameInfoImpl::type() const
case GameInfo::TYPE_FALLOUT4: return IGameInfo::TYPE_FALLOUT4;
case GameInfo::TYPE_FALLOUTNV: return IGameInfo::TYPE_FALLOUTNV;
case GameInfo::TYPE_SKYRIM: return IGameInfo::TYPE_SKYRIM;
case GameInfo::TYPE_SKYRIMSE: return IGameInfo::TYPE_SKYRIMSE;
default: throw MyException(QObject::tr("invalid game type %1").arg(GameInfo::instance().getType()));
}
}
+6 -8
View File
@@ -77,16 +77,14 @@ static T resolveFunction(QLibrary &lib, const char *name)
return temp;
}
InstallationManager::InstallationManager()
: m_ParentWidget(nullptr)
, m_SupportedExtensions({ "zip", "rar", "7z", "fomod", "001" })
{
QLibrary archiveLib(QCoreApplication::applicationDirPath()
+ "\\dlls\\archive.dll");
: m_ParentWidget(nullptr),
m_SupportedExtensions({"zip", "rar", "7z", "fomod", "001"}) {
QLibrary archiveLib(QCoreApplication::applicationDirPath() +
"\\dlls\\archive.dll");
if (!archiveLib.load()) {
throw MyException(
tr("archive.dll not loaded: \"%1\"").arg(archiveLib.errorString()));
throw MyException(QObject::tr("archive.dll not loaded: \"%1\"")
.arg(archiveLib.errorString()));
}
CreateArchiveType CreateArchiveFunc
+1 -1
View File
@@ -72,7 +72,7 @@ QString InstanceManager::queryInstanceName() const
// would be neat if we could take the names from the game plugins but
// the required initialization order requires the ini file to be
// available *before* we load plugins
dialog.setComboBoxItems({ "Oblivion", "Skyrim", "Fallout 3",
dialog.setComboBoxItems({ "Oblivion", "Skyrim", "SkyrimSE", "Fallout 3",
"Fallout NV", "Fallout 4" });
dialog.setComboBoxEditable(true);
dialog.setWindowTitle(QObject::tr("Enter Instance Name"));
+1
View File
@@ -30,6 +30,7 @@ public:
virtual void lock() = 0;
virtual void unlock() = 0;
virtual bool unlockClicked() = 0;
virtual void setProcessName(QString const &) = 0;
};
+4 -1
View File
@@ -19,8 +19,11 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "lockeddialog.h"
#include "ui_lockeddialog.h"
#include <QResizeEvent>
#include <QPoint>
#include <QResizeEvent>
#include <QWidget>
#include <Qt> // for Qt::FramelessWindowHint, etc
LockedDialog::LockedDialog(QWidget *parent, const QString &text, bool unlockButton)
: QDialog(parent)
+10 -1
View File
@@ -20,7 +20,12 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#ifndef LOCKEDDIALOG_H
#define LOCKEDDIALOG_H
#include <QDialog>
#include <QDialog> // for QDialog
#include <QObject> // for Q_OBJECT, slots
#include <QString> // for QString
class QResizeEvent;
class QWidget;
namespace Ui {
class LockedDialog;
@@ -49,6 +54,10 @@ public:
**/
bool unlockClicked() const { return m_UnlockClicked; }
/**
* @brief set the name of the process being run
* @param name of process
*/
void setProcessName(const QString &name);
protected:
+18 -33
View File
@@ -85,38 +85,21 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
using namespace MOBase;
using namespace MOShared;
bool createAndMakeWritable(const std::wstring &subPath)
{
bool createAndMakeWritable(const std::wstring &subPath) {
QString const dataPath = qApp->property("dataPath").toString();
QString fullPath = dataPath + "/" + QString::fromStdWString(subPath);
if (!QDir(fullPath).exists()) {
QDir().mkdir(fullPath);
if (!QDir(fullPath).exists() && !QDir().mkdir(fullPath)) {
QMessageBox::critical(nullptr, QObject::tr("Error"),
QObject::tr("Failed to create \"%1\". Your user "
"account probably lacks permission.")
.arg(fullPath));
return false;
} else {
return true;
}
QFileInfo fileInfo(fullPath);
if (!fileInfo.exists() || !fileInfo.isWritable()) {
if (QMessageBox::question(nullptr, QObject::tr("Permissions required"),
QObject::tr("The current user account doesn't have the required access rights to run "
"Mod Organizer. The neccessary changes can be made automatically (the MO directory "
"will be made writable for the current user account). You will be asked to run "
"\"helper.exe\" with administrative rights."),
QMessageBox::Yes | QMessageBox::Cancel) == QMessageBox::Yes) {
if (!Helper::init(qApp->applicationDirPath().toStdWString(),
dataPath.toStdWString())) {
return false;
}
} else {
return false;
}
// no matter which directory didn't exist/wasn't writable, the helper
// should have created them all so we don't have to worry this message box would appear repeatedly
}
return true;
}
bool bootstrap()
{
// remove the temporary backup directory in case we're restarting after an update
@@ -129,11 +112,9 @@ bool bootstrap()
removeOldFiles(qApp->property("dataPath").toString() + "/" + QString::fromStdWString(AppConfig::logPath()),
"usvfs*.log", 5, QDir::Name);
createAndMakeWritable(AppConfig::profilesPath());
createAndMakeWritable(AppConfig::modsPath());
createAndMakeWritable(AppConfig::downloadPath());
createAndMakeWritable(AppConfig::overwritePath());
createAndMakeWritable(AppConfig::logPath());
if (!createAndMakeWritable(AppConfig::logPath())) {
return false;
}
return true;
}
@@ -168,7 +149,7 @@ static LONG WINAPI MyUnhandledExceptionFilter(struct _EXCEPTION_POINTERS *except
if (QMessageBox::question(nullptr, QObject::tr("Woops"),
QObject::tr("ModOrganizer has crashed! "
"Should a diagnostic file be created? "
"If you send me this file (%1) to sherb@gmx.net, "
"If you send me this file (%1) to modorganizer@gmail.com, "
"the bug is a lot more likely to be fixed. "
"Please include a short description of what you were "
"doing when the crash happened"
@@ -445,7 +426,7 @@ int runApplication(MOApplication &application, SingleInstance &instance,
qDebug("data path: %s", qPrintable(dataPath));
if (!bootstrap()) {
reportError("failed to set up data path");
reportError("failed to set up data paths");
return 1;
}
@@ -465,6 +446,10 @@ int runApplication(MOApplication &application, SingleInstance &instance,
QSettings::IniFormat);
qDebug("initializing core");
OrganizerCore organizer(settings);
if (!organizer.bootstrap()) {
reportError("failed to set up data paths");
return 1;
}
qDebug("initialize plugins");
PluginContainer pluginContainer(&organizer);
pluginContainer.loadPlugins();
+77 -27
View File
@@ -34,6 +34,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "organizercore.h"
#include "pluginlistsortproxy.h"
#include "previewgenerator.h"
#include "serverinfo.h"
#include "savegameinfo.h"
#include "spawn.h"
#include "versioninfo.h"
@@ -200,8 +201,11 @@ MainWindow::MainWindow(QSettings &initSettings
{
ui->setupUi(this);
updateWindowTitle(QString(), false);
languageChange(m_OrganizerCore.settings().language());
m_CategoryFactory.loadCategories();
ui->logList->setModel(LogBuffer::instance());
ui->logList->setColumnWidth(0, 100);
ui->logList->setAutoScroll(true);
@@ -1008,8 +1012,14 @@ void MainWindow::startExeAction()
{
QAction *action = qobject_cast<QAction*>(sender());
if (action != nullptr) {
m_OrganizerCore.spawnBinary(
const Executable &selectedExecutable(
m_OrganizerCore.executablesList()->find(action->text()));
m_OrganizerCore.spawnBinary(
selectedExecutable.m_BinaryInfo, selectedExecutable.m_Arguments,
selectedExecutable.m_WorkingDirectory.length() != 0
? selectedExecutable.m_WorkingDirectory
: selectedExecutable.m_BinaryInfo.absolutePath(),
selectedExecutable.m_SteamAppID);
} else {
qCritical("not an action?");
}
@@ -1447,18 +1457,31 @@ void MainWindow::storeSettings(QSettings &settings) {
void MainWindow::lock()
{
if (m_LockDialog != nullptr) {
++m_LockCount;
return;
}
m_LockDialog = new LockedDialog(qApp->activeWindow());
m_LockDialog->show();
setEnabled(false);
m_LockDialog->setEnabled(true); //What's the point otherwise?
++m_LockCount;
}
void MainWindow::unlock()
{
if (m_LockDialog != nullptr) {
//If you come through here with a null lock pointer, it's a bug!
if (m_LockDialog == nullptr) {
qDebug("Unlocking main window when already unlocked");
return;
}
--m_LockCount;
if (m_LockCount == 0) {
m_LockDialog->hide();
m_LockDialog->deleteLater();
m_LockDialog = nullptr;
setEnabled(true);
}
setEnabled(true);
}
bool MainWindow::unlockClicked()
@@ -1470,6 +1493,13 @@ bool MainWindow::unlockClicked()
}
}
void MainWindow::setProcessName(QString const &name)
{
if (m_LockDialog != nullptr) {
m_LockDialog->setProcessName(name);
}
}
void MainWindow::on_btnRefreshData_clicked()
{
m_OrganizerCore.refreshDirectoryStructure();
@@ -1512,13 +1542,17 @@ void MainWindow::installMod(QString fileName)
}
}
void MainWindow::on_startButton_clicked() {
const Executable &selectedExecutable(getSelectedExecutable());
void MainWindow::on_startButton_clicked()
{
m_OrganizerCore.spawnBinary(getSelectedExecutable());
m_OrganizerCore.spawnBinary(
selectedExecutable.m_BinaryInfo, selectedExecutable.m_Arguments,
selectedExecutable.m_WorkingDirectory.length() != 0
? selectedExecutable.m_WorkingDirectory
: selectedExecutable.m_BinaryInfo.absolutePath(),
selectedExecutable.m_SteamAppID);
}
static HRESULT CreateShortcut(LPCWSTR targetFileName, LPCWSTR arguments,
LPCSTR linkFileName, LPCWSTR description,
LPCTSTR iconFileName, int iconNumber,
@@ -3188,42 +3222,58 @@ void MainWindow::linkMenu()
void MainWindow::on_actionSettings_triggered()
{
QString oldModDirectory(m_OrganizerCore.settings().getModDirectory());
QString oldCacheDirectory(m_OrganizerCore.settings().getCacheDirectory());
bool oldDisplayForeign(m_OrganizerCore.settings().displayForeign());
bool proxy = m_OrganizerCore.settings().useProxy();
m_OrganizerCore.settings().query(this);
m_OrganizerCore.installationManager()->setModsDirectory(m_OrganizerCore.settings().getModDirectory());
m_OrganizerCore.installationManager()->setDownloadDirectory(m_OrganizerCore.settings().getDownloadDirectory());
Settings &settings = m_OrganizerCore.settings();
QString oldModDirectory(settings.getModDirectory());
QString oldCacheDirectory(settings.getCacheDirectory());
QString oldProfilesDirectory(settings.getProfileDirectory());
bool oldDisplayForeign(settings.displayForeign());
bool proxy = settings.useProxy();
settings.query(this);
InstallationManager *instManager = m_OrganizerCore.installationManager();
instManager->setModsDirectory(settings.getModDirectory());
instManager->setDownloadDirectory(settings.getDownloadDirectory());
fixCategories();
refreshFilters();
if (QDir::fromNativeSeparators(m_OrganizerCore.downloadManager()->getOutputDirectory()) != QDir::fromNativeSeparators(m_OrganizerCore.settings().getDownloadDirectory())) {
if (m_OrganizerCore.downloadManager()->downloadsInProgress()) {
MessageDialog::showMessage(tr("Can't change download directory while downloads are in progress!"), this);
if (settings.getProfileDirectory() != oldProfilesDirectory) {
refreshProfiles();
}
DownloadManager *dlManager = m_OrganizerCore.downloadManager();
if (dlManager->getOutputDirectory() != settings.getDownloadDirectory()) {
if (dlManager->downloadsInProgress()) {
MessageDialog::showMessage(tr("Can't change download directory while "
"downloads are in progress!"),
this);
} else {
m_OrganizerCore.downloadManager()->setOutputDirectory(m_OrganizerCore.settings().getDownloadDirectory());
dlManager->setOutputDirectory(settings.getDownloadDirectory());
}
}
m_OrganizerCore.downloadManager()->setPreferredServers(m_OrganizerCore.settings().getPreferredServers());
dlManager->setPreferredServers(settings.getPreferredServers());
if ((m_OrganizerCore.settings().getModDirectory() != oldModDirectory)
|| (m_OrganizerCore.settings().displayForeign() != oldDisplayForeign)) {
if ((settings.getModDirectory() != oldModDirectory)
|| (settings.displayForeign() != oldDisplayForeign)) {
m_OrganizerCore.profileRefresh();
}
if (m_OrganizerCore.settings().getCacheDirectory() != oldCacheDirectory) {
NexusInterface::instance()->setCacheDirectory(m_OrganizerCore.settings().getCacheDirectory());
if (settings.getCacheDirectory() != oldCacheDirectory) {
NexusInterface::instance()->setCacheDirectory(settings.getCacheDirectory());
}
if (proxy != m_OrganizerCore.settings().useProxy()) {
activateProxy(m_OrganizerCore.settings().useProxy());
if (proxy != settings.useProxy()) {
activateProxy(settings.useProxy());
}
NexusInterface::instance()->setNMMVersion(m_OrganizerCore.settings().getNMMVersion());
NexusInterface::instance()->setNMMVersion(settings.getNMMVersion());
updateDownloadListDelegate();
m_OrganizerCore.setLogLevel(m_OrganizerCore.settings().logLevel());
m_OrganizerCore.setLogLevel(settings.logLevel());
}
+2
View File
@@ -119,6 +119,7 @@ public:
virtual void lock() override;
virtual void unlock() override;
virtual bool unlockClicked() override;
virtual void setProcessName(QString const &name) override;
bool addProfile();
void refreshDataTree();
@@ -350,6 +351,7 @@ private:
bool m_DidUpdateMasterList;
LockedDialog *m_LockDialog { nullptr };
uint64_t m_LockCount { 0 };
std::vector<std::pair<QString, QHeaderView*>> m_PersistedGeometry;
+2 -1
View File
@@ -1,6 +1,7 @@
#include "modinfooverwrite.h"
#include "appconfig.h"
#include "settings.h"
#include <QApplication>
#include <QDirIterator>
@@ -21,7 +22,7 @@ bool ModInfoOverwrite::isEmpty() const
QString ModInfoOverwrite::absolutePath() const
{
return QDir::fromNativeSeparators(qApp->property("dataPath").toString() + "/" + QString::fromStdWString(AppConfig::overwritePath()));
return Settings::instance().getOverwriteDirectory();
}
std::vector<ModInfo::EFlag> ModInfoOverwrite::getFlags() const
+3 -3
View File
@@ -252,12 +252,12 @@ bool NexusInterface::isURLGameRelated(const QUrl &url) const
QString NexusInterface::getGameURL() const
{
return "http://www.nexusmods.com/" + m_Game->gameShortName().toLower();
return "http://www.nexusmods.com/" + m_Game->gameNexusName().toLower();
}
QString NexusInterface::getOldModsURL() const
{
return "http://" + m_Game->gameShortName().toLower() + ".nexusmods.com/mods";
return "http://" + m_Game->gameNexusName().toLower() + ".nexusmods.com/mods";
}
@@ -599,7 +599,7 @@ void NexusInterface::managedGameChanged(IPluginGame const *game)
namespace {
QString get_management_url(MOBase::IPluginGame const *game)
{
return "http://nmm.nexusmods.com/" + game->gameShortName().toLower();
return "http://nmm.nexusmods.com/" + game->gameNexusName().toLower();
}
}
+424 -402
View File
File diff suppressed because it is too large Load Diff
+138 -161
View File
@@ -1,17 +1,22 @@
#include "organizercore.h"
#include "delayedfilewriter.h"
#include "guessedvalue.h"
#include "imodinterface.h"
#include "imoinfo.h"
#include "iplugingame.h"
#include "iuserinterface.h"
#include "loadmechanism.h"
#include "messagedialog.h"
#include "modlistsortproxy.h"
#include "modrepositoryfileinfo.h"
#include "nexusinterface.h"
#include "plugincontainer.h"
#include "pluginlistsortproxy.h"
#include "profile.h"
#include "logbuffer.h"
#include "credentialsdialog.h"
#include "filedialogmemory.h"
#include "lockeddialog.h"
#include "modinfodialog.h"
#include "spawn.h"
#include "syncoverwritedialog.h"
@@ -28,22 +33,33 @@
#include <questionboxmemory.h>
#include <QApplication>
#include <QCoreApplication>
#include <QDialog>
#include <QDialogButtonBox>
#include <QMessageBox>
#include <QNetworkInterface>
#include <QProcess>
#include <QTimer>
#include <QUrl>
#include <QWidget>
#include <QtDebug>
#include <QtGlobal> // for qPrintable, etc
#include <Psapi.h>
#include <tchar.h> // for _tcsicmp
#include <limits.h>
#include <stddef.h>
#include <string.h> // for memset, wcsrchr
#include <exception>
#include <functional>
#include <boost/algorithm/string/predicate.hpp>
#include <memory>
#include <set>
#include <string> //for wstring
#include <tuple>
#include <utility>
@@ -586,6 +602,25 @@ InstallationManager *OrganizerCore::installationManager()
return &m_InstallationManager;
}
bool OrganizerCore::createDirectory(const QString &path) {
if (!QDir(path).exists() && !QDir().mkpath(path)) {
QMessageBox::critical(nullptr, QObject::tr("Error"),
QObject::tr("Failed to create \"%1\". Your user "
"account probably lacks permission.")
.arg(QDir::toNativeSeparators(path)));
return false;
} else {
return true;
}
}
bool OrganizerCore::bootstrap() {
return createDirectory(m_Settings.getProfileDirectory()) &&
createDirectory(m_Settings.getModDirectory()) &&
createDirectory(m_Settings.getDownloadDirectory()) &&
createDirectory(m_Settings.getOverwriteDirectory());
}
void OrganizerCore::createDefaultProfile()
{
QString profilesPath = settings().getProfileDirectory();
@@ -610,7 +645,19 @@ void OrganizerCore::setCurrentProfile(const QString &profileName)
&& (profileName == m_CurrentProfile->name())) {
return;
}
QString profileDir = settings().getProfileDirectory() + "/" + 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();
profileDir = profileBaseDir.absoluteFilePath(
profileBaseDir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot).at(0));
}
Profile *newProfile = new Profile(QDir(profileDir), managedGame());
delete m_CurrentProfile;
@@ -974,120 +1021,34 @@ QStringList OrganizerCore::modsSortedByProfilePriority() const
return res;
}
void OrganizerCore::spawnBinary(const Executable &exe)
void OrganizerCore::spawnBinary(const QFileInfo &binary, const QString &arguments, const QDir &currentDirectory, const QString &steamAppID, const QString &customOverwrite)
{
spawnBinary(
exe.m_BinaryInfo, exe.m_Arguments,
exe.m_WorkingDirectory.length() != 0 ? exe.m_WorkingDirectory
: exe.m_BinaryInfo.absolutePath(),
exe.m_SteamAppID,
m_CurrentProfile->setting("custom_overwrites", exe.m_Title).toString());
}
void OrganizerCore::spawnBinary(const QFileInfo &binary,
const QString &arguments,
const QDir &currentDirectory,
const QString &steamAppID,
const QString &customOverwrite)
{
LockedDialog *dialog = new LockedDialog(qApp->activeWindow());
dialog->show();
ON_BLOCK_EXIT([&]() {
dialog->hide();
dialog->deleteLater();
if (m_UserInterface != nullptr) {
m_UserInterface->lock();
}
ON_BLOCK_EXIT([&] () {
if (m_UserInterface != nullptr) { m_UserInterface->unlock(); }
});
HANDLE processHandle
= spawnBinaryDirect(binary, arguments, m_CurrentProfile->name(),
currentDirectory, steamAppID, customOverwrite);
HANDLE processHandle = spawnBinaryDirect(binary, arguments, m_CurrentProfile->name(), currentDirectory, steamAppID, customOverwrite);
if (processHandle != INVALID_HANDLE_VALUE) {
if (m_UserInterface != nullptr) {
m_UserInterface->setWindowEnabled(false);
}
// re-enable the locked dialog because what'd be the point otherwise?
dialog->setEnabled(true);
QCoreApplication::processEvents();
DWORD processExitCode;
(void)waitForProcessCompletion(processHandle, &processExitCode);
dialog->setProcessName(
QString::fromStdWString(getProcessName(::GetProcessId(processHandle))));
{
DWORD currentProcess = 0UL;
DWORD res = ::MsgWaitForMultipleObjects(1, &processHandle, false, 1000,
QS_KEY | QS_MOUSE);
bool tryAgain = true;
while ((res != WAIT_FAILED) && !dialog->unlockClicked()) {
if (res == WAIT_OBJECT_0) {
// process ended, is there another one in the group?
static const DWORD maxCount = 5;
size_t numProcesses = maxCount;
LPDWORD processes = new DWORD[maxCount];
if (::GetVFSProcessList(&numProcesses, processes)) {
bool found = false;
size_t count =
std::min<size_t>(static_cast<size_t>(maxCount), numProcesses);
for (size_t i = 0; i < count; ++i) {
std::wstring processName = getProcessName(processes[i]);
if (!boost::starts_with(processName, L"ModOrganizer.exe")) {
currentProcess = processes[i];
dialog->setProcessName(QString::fromStdWString(processName));
processHandle
= ::OpenProcess(SYNCHRONIZE, FALSE, currentProcess);
found = true;
}
}
if (!found) {
// it's possible the previous process has deregistered before
// the new one has registered, so we should try one more time
// with a little delay
if (tryAgain) {
tryAgain = false;
QThread::msleep(500);
continue;
} else {
break;
}
} else {
tryAgain = true;
}
} else {
break;
}
}
// keep processing events so the app doesn't appear dead
QCoreApplication::processEvents();
res = ::MsgWaitForMultipleObjects(1, &processHandle, false, 1000,
QS_KEY | QS_MOUSE);
}
::GetExitCodeProcess(processHandle, &processExitCode);
::CloseHandle(processHandle);
if (m_UserInterface != nullptr) {
m_UserInterface->setWindowEnabled(true);
}
// need to remove our stored load order because it may be outdated if a
// foreign tool changed the
// file time. After removing that file, refreshESPList will use the file
// time as the order
if (managedGame()->loadOrderMechanism()
== IPluginGame::LoadOrderMechanism::FileTime) {
qDebug("removing loadorder.txt");
QFile::remove(m_CurrentProfile->getLoadOrderFileName());
}
refreshDirectoryStructure();
refreshESPList();
savePluginList();
m_FinishedRun(binary.absoluteFilePath(), processExitCode);
refreshDirectoryStructure();
// need to remove our stored load order because it may be outdated if a foreign tool changed the
// file time. After removing that file, refreshESPList will use the file time as the order
if (managedGame()->loadOrderMechanism() == IPluginGame::LoadOrderMechanism::FileTime) {
qDebug("removing loadorder.txt");
QFile::remove(m_CurrentProfile->getLoadOrderFileName());
}
refreshDirectoryStructure();
refreshESPList();
savePluginList();
//These callbacks should not fiddle with directoy structure and ESPs.
m_FinishedRun(binary.absoluteFilePath(), processExitCode);
}
}
@@ -1127,12 +1088,11 @@ HANDLE OrganizerCore::spawnBinaryDirect(const QFileInfo &binary,
if ((window != nullptr) && (!window->isVisible())) {
window = nullptr;
}
if (QuestionBoxMemory::query(window, "steamQuery", 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) {
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());
}
}
@@ -1148,6 +1108,7 @@ HANDLE OrganizerCore::spawnBinaryDirect(const QFileInfo &binary,
m_CurrentProfile->modlistWriter().writeImmediately(true);
}
// TODO: should also pass arguments
if (m_AboutToRun(binary.absoluteFilePath())) {
try {
m_USVFS.updateMapping(fileMapping(profileName, customOverwrite));
@@ -1256,67 +1217,83 @@ bool OrganizerCore::waitForApplication(HANDLE handle, LPDWORD exitCode)
{
if (m_UserInterface != nullptr) {
m_UserInterface->lock();
ON_BLOCK_EXIT([&]() { m_UserInterface->unlock(); });
}
DWORD retLen;
JOBOBJECT_BASIC_PROCESS_ID_LIST info;
ON_BLOCK_EXIT([&] () {
if (m_UserInterface != nullptr) {
m_UserInterface->unlock();
} });
return waitForProcessCompletion(handle, exitCode);
}
bool isJobHandle = true;
ULONG lastProcessID = ULONG_MAX;
bool OrganizerCore::waitForProcessCompletion(HANDLE handle, LPDWORD exitCode)
{
HANDLE processHandle = handle;
DWORD res
= ::MsgWaitForMultipleObjects(1, &handle, false, 500, QS_KEY | QS_MOUSE);
static const DWORD maxCount = 5;
size_t numProcesses = maxCount;
LPDWORD processes = new DWORD[maxCount];
DWORD currentProcess = 0UL;
bool tryAgain = true;
DWORD res;
// Wait for a an event on the handle, a key press, mouse click or timeout
while (
(res != WAIT_FAILED) && (res != WAIT_OBJECT_0)
&& ((m_UserInterface == nullptr) || !m_UserInterface->unlockClicked())) {
if (isJobHandle) {
if (::QueryInformationJobObject(handle, JobObjectBasicProcessIdList,
&info, sizeof(info), &retLen)
> 0) {
if (info.NumberOfProcessIdsInList == 0) {
// fake signaled state
res = WAIT_OBJECT_0;
break;
} else {
// this is indeed a job handle. Figure out one of the process handles
// as well.
if (lastProcessID != info.ProcessIdList[0]) {
lastProcessID = info.ProcessIdList[0];
if (processHandle != handle) {
::CloseHandle(processHandle);
}
processHandle = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE,
lastProcessID);
}
}
} else {
// the info-object I passed only provides space for 1 process id. but
// since this code only cares about whether there
// is more than one that's good enough. ERROR_MORE_DATA simply signals
// there are at least two processes running.
// any other error probably means the handle is a regular process
// handle, probably caused by running MO in a job without
// the right to break out.
if (::GetLastError() != ERROR_MORE_DATA) {
isJobHandle = false;
}
res = ::MsgWaitForMultipleObjects(1, &handle, false, 500,
QS_KEY | QS_MOUSE),
(MOBase::isOneOf(res, {WAIT_FAILED, WAIT_OBJECT_0}) &&
((m_UserInterface == nullptr) || !m_UserInterface->unlockClicked()))) {
if (!::GetVFSProcessList(&numProcesses, processes)) {
break;
}
bool found = false;
size_t count =
std::min<size_t>(static_cast<size_t>(maxCount), numProcesses);
for (size_t i = 0; i < count; ++i) {
std::wstring processName = getProcessName(processes[i]);
if (!boost::starts_with(processName, L"ModOrganizer.exe")) {
currentProcess = processes[i];
m_UserInterface->setProcessName(QString::fromStdWString(processName));
processHandle = ::OpenProcess(SYNCHRONIZE, FALSE, currentProcess);
found = true;
}
}
if (!found) {
// it's possible the previous process has deregistered before
// the new one has registered, so we should try one more time
// with a little delay
if (tryAgain) {
tryAgain = false;
QThread::msleep(500);
continue;
} else {
break;
}
} else {
tryAgain = true;
}
// keep processing events so the app doesn't appear dead
QCoreApplication::processEvents();
res = ::MsgWaitForMultipleObjects(1, &handle, false, 500,
QS_KEY | QS_MOUSE);
}
if (exitCode != nullptr) {
::GetExitCodeProcess(processHandle, exitCode);
//This is actually wrong if the process we started finished before we
//got the event and so we end up with a job handle.
if (! ::GetExitCodeProcess(processHandle, exitCode))
{
DWORD error = ::GetLastError();
qDebug() << "Failed to get process exit code: Error " << error;
}
}
::CloseHandle(processHandle);
if (handle != processHandle) {
::CloseHandle(handle);
}
return res == WAIT_OBJECT_0;
}
@@ -1870,7 +1847,7 @@ std::vector<Mapping> OrganizerCore::fileMapping(const QString &profileName,
result.insert(result.end(), {
QDir::toNativeSeparators(m_Settings.getOverwriteDirectory()),
QDir::toNativeSeparators(game->dataDirectory().absolutePath()),
dataPath,
true,
customOverwrite.isEmpty()
});
+23 -7
View File
@@ -23,21 +23,28 @@
class ModListSortProxy;
class PluginListSortProxy;
class Profile;
namespace MOBase { template <typename T> class GuessedValue; }
namespace MOBase {
template <typename T> class GuessedValue;
class IModInterface;
}
namespace MOShared { class DirectoryEntry; }
#include <QDir>
#include <QFileInfo>
#include <QList>
#include <QObject>
#include <QSettings>
#include <QString>
#include <QStringList>
#include <QThread>
#include <QVariant>
class QNetworkReply;
class QUrl;
class QWidget;
#include <Windows.h> //for HANDLE, LPDWORD
#include <functional>
#include <vector>
@@ -129,12 +136,22 @@ public:
void doAfterLogin(const std::function<void()> &function) { m_PostLoginTasks.append(function); }
void spawnBinary(const Executable &exe);
HANDLE spawnBinaryDirect(const QFileInfo &binary, const QString &arguments, const QString &profileName, const QDir &currentDirectory, const QString &steamAppID, const QString &customOverwrite);
void spawnBinary(const QFileInfo &binary, const QString &arguments = "",
const QDir &currentDirectory = QDir(),
const QString &steamAppID = "",
const QString &customOverwrite = "");
HANDLE spawnBinaryDirect(const QFileInfo &binary, const QString &arguments,
const QString &profileName,
const QDir &currentDirectory,
const QString &steamAppID,
const QString &customOverwrite);
void loginSuccessfulUpdate(bool necessary);
void loginFailedUpdate(const QString &message);
static bool createAndMakeWritable(const QString &path);
bool bootstrap();
void createDefaultProfile();
MOBase::DelayedFileWriter &pluginsWriter() { return m_PluginListsWriter; }
@@ -228,6 +245,8 @@ private:
bool testForSteam();
bool createDirectory(const QString &path);
/**
* @brief return a descriptor of the mappings real file->virtual file
*/
@@ -240,10 +259,7 @@ private:
const MOShared::DirectoryEntry *directoryEntry,
int createDestination);
void spawnBinary(const QFileInfo &binary, const QString &arguments = "",
const QDir &currentDirectory = QDir(),
const QString &steamAppID = "",
const QString &customOverwrite = "");
bool waitForProcessCompletion(HANDLE handle, LPDWORD exitCode);
private slots:

Some files were not shown because too many files have changed in this diff Show More