summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBrian Munro <brian.alexander.munro@gmail.com>2017-12-27 10:40:59 +0200
committerGitHub <noreply@github.com>2017-12-27 10:40:59 +0200
commit91bd9b1eb6badea4558ba1c4ad0bac9549c5b314 (patch)
treee236e2c472188d834bcf9215ed19527332e31e27 /src
parent35e0bc22856c8f29e3c29ec27bd7c3c5a882197f (diff)
parent548744d746223f0f96b33587ca41d2ed6a314f33 (diff)
Merge pull request #168 from erasmux/dev
Various fixes
Diffstat (limited to 'src')
-rw-r--r--src/main.cpp81
-rw-r--r--src/mainwindow.cpp31
-rw-r--r--src/mainwindow.h1
-rw-r--r--src/modlist.cpp15
-rw-r--r--src/organizercore.cpp31
-rw-r--r--src/settings.cpp21
6 files changed, 149 insertions, 31 deletions
diff --git a/src/main.cpp b/src/main.cpp
index 3d315f1d..4c5ac75c 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -78,6 +78,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <cstdarg>
#include <iostream>
+#include <sstream>
#include <stdexcept>
@@ -139,6 +140,60 @@ static LONG WINAPI MyUnhandledExceptionFilter(struct _EXCEPTION_POINTERS *except
return EXCEPTION_CONTINUE_SEARCH;
}
+// Parses the first parseArgCount arguments of the current process command line and returns
+// them in parsedArgs, the rest of the command line is returned untouched.
+LPCWSTR UntouchedCommandLineArguments(int parseArgCount, std::vector<std::wstring>& parsedArgs)
+{
+ LPCWSTR cmd = GetCommandLineW();
+ LPCWSTR arg = nullptr; // to skip executable name
+ for (; parseArgCount >= 0 && *cmd; ++cmd)
+ {
+ if (*cmd == '"') {
+ int escaped = 0;
+ for (++cmd; *cmd && (*cmd != '"' || escaped % 2 != 0); ++cmd)
+ escaped = *cmd == '\\' ? escaped + 1 : 0;
+ }
+ if (*cmd == ' ') {
+ if (arg)
+ if (cmd-1 > arg && *arg == '"' && *(cmd-1) == '"')
+ parsedArgs.push_back(std::wstring(arg+1, cmd-1));
+ else
+ parsedArgs.push_back(std::wstring(arg, cmd));
+ arg = cmd + 1;
+ --parseArgCount;
+ }
+ }
+ return cmd;
+}
+
+static int SpawnWaitProcess(LPCWSTR workingDirectory, LPCWSTR commandLine) {
+ PROCESS_INFORMATION pi{ 0 };
+ STARTUPINFO si{ 0 };
+ si.cb = sizeof(si);
+ std::wstring commandLineCopy = commandLine;
+
+ if (!CreateProcessW(NULL, &commandLineCopy[0], NULL, NULL, FALSE, 0, NULL, workingDirectory, &si, &pi)) {
+ // A bit of a problem where to log the error message here, at least this way you can get the message
+ // using a either DebugView or a live debugger:
+ std::wostringstream ost;
+ ost << L"CreateProcess failed: " << commandLine << ", " << GetLastError();
+ OutputDebugStringW(ost.str().c_str());
+ return -1;
+ }
+
+ WaitForSingleObject(pi.hProcess, INFINITE);
+
+ DWORD exitCode = (DWORD)-1;
+ ::GetExitCodeProcess(pi.hProcess, &exitCode);
+ CloseHandle(pi.hThread);
+ CloseHandle(pi.hProcess);
+ return static_cast<int>(exitCode);
+}
+
+static DWORD WaitForProcess() {
+
+}
+
static bool HaveWriteAccess(const std::wstring &path)
{
bool writable = false;
@@ -389,6 +444,14 @@ int runApplication(MOApplication &application, SingleInstance &instance,
// global crashDumpType sits in OrganizerCore to make a bit less ugly to update it when the settings are changed during runtime
OrganizerCore::setGlobalCrashDumpsType(settings.value("Settings/crash_dumps_type", static_cast<int>(CrashDumpsType::Mini)).toInt());
+ qDebug("Loaded settings:");
+ settings.beginGroup("Settings");
+ for (auto k : settings.allKeys())
+ if (!k.contains("username") && !k.contains("password"))
+ qDebug(" %s=%s", k.toUtf8().data(), settings.value(k).toString().toUtf8().data());
+ settings.endGroup();
+
+
qDebug("initializing core");
OrganizerCore organizer(settings);
if (!organizer.bootstrap()) {
@@ -525,20 +588,16 @@ int runApplication(MOApplication &application, SingleInstance &instance,
int main(int argc, char *argv[])
{
+ if (argc >= 4) {
+ std::vector<std::wstring> arg;
+ auto args = UntouchedCommandLineArguments(2, arg);
+ if (arg[0] == L"launch")
+ return SpawnWaitProcess(arg[1].c_str(), args);
+ }
+
MOApplication application(argc, argv);
QStringList arguments = application.arguments();
- if ((arguments.length() >= 4) && (arguments.at(1) == "launch")) {
- // all we're supposed to do is launch another process
- QProcess process;
- process.setWorkingDirectory(QDir::fromNativeSeparators(arguments.at(2)));
- process.setProgram(QDir::fromNativeSeparators(arguments.at(3)));
- process.setArguments(arguments.mid(4));
- process.start();
- process.waitForFinished(-1);
- return process.exitCode();
- }
-
setupPath();
#if !defined(QT_NO_SSL)
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index 1c5e321d..c29e4ca4 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -2595,6 +2595,8 @@ void MainWindow::createEmptyMod_clicked()
if (newMod == nullptr) {
return;
}
+
+ m_OrganizerCore.refreshModList();
}
void MainWindow::createModFromOverwrite()
@@ -2634,6 +2636,31 @@ void MainWindow::createModFromOverwrite()
m_OrganizerCore.refreshModList();
}
+void MainWindow::clearOverwrite()
+{
+ unsigned int overwriteIndex = ModInfo::findMod([](ModInfo::Ptr mod) -> bool {
+ std::vector<ModInfo::EFlag> flags = mod->getFlags();
+ return std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE)
+ != flags.end();
+ });
+
+ ModInfo::Ptr modInfo = ModInfo::getByIndex(overwriteIndex);
+ if (modInfo)
+ {
+ QDir overwriteDir(modInfo->absolutePath());
+ if (QMessageBox::question(this, tr("Are you sure?"),
+ tr("About to recursively delete:\n") + overwriteDir.absolutePath(),
+ QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok)
+ {
+ QStringList delList;
+ for (auto f : overwriteDir.entryList(QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot))
+ delList.push_back(overwriteDir.absoluteFilePath(f));
+ shellDelete(delList, true);
+ updateProblemsButton();
+ }
+ }
+}
+
void MainWindow::cancelModListEditor()
{
ui->modList->setEnabled(false);
@@ -3072,6 +3099,8 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos)
if (QDir(info->absolutePath()).count() > 2) {
menu->addAction(tr("Sync to Mods..."), &m_OrganizerCore, SLOT(syncOverwrite()));
menu->addAction(tr("Create Mod..."), this, SLOT(createModFromOverwrite()));
+ menu->addAction(tr("Clear Overwrite..."), this, SLOT(clearOverwrite()));
+ menu->addAction(tr("Open in explorer"), this, SLOT(openExplorer_clicked()));
}
} else if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_BACKUP) != flags.end()) {
menu->addAction(tr("Restore Backup"), this, SLOT(restoreBackup_clicked()));
@@ -3850,7 +3879,7 @@ void MainWindow::updateDownloadListDelegate()
connect(ui->downloadFilterEdit, SIGNAL(textChanged(QString)), this, SLOT(downloadFilterChanged(QString)));
ui->downloadView->setModel(sortProxy);
- ui->downloadView->sortByColumn(1, Qt::AscendingOrder);
+ ui->downloadView->sortByColumn(1, Qt::DescendingOrder);
ui->downloadView->header()->resizeSections(QHeaderView::Fixed);
connect(ui->downloadView->itemDelegate(), SIGNAL(installDownload(int)), &m_OrganizerCore, SLOT(installDownload(int)));
diff --git a/src/mainwindow.h b/src/mainwindow.h
index a4ded688..c9ba170c 100644
--- a/src/mainwindow.h
+++ b/src/mainwindow.h
@@ -425,6 +425,7 @@ private slots:
BSA::EErrorCode extractBSA(BSA::Archive &archive, BSA::Folder::Ptr folder, const QString &destination, QProgressDialog &extractProgress);
void createModFromOverwrite();
+ void clearOverwrite();
void procError(QProcess::ProcessError error);
void procFinished(int exitCode, QProcess::ExitStatus exitStatus);
diff --git a/src/modlist.cpp b/src/modlist.cpp
index 046e2280..4002a424 100644
--- a/src/modlist.cpp
+++ b/src/modlist.cpp
@@ -426,13 +426,16 @@ bool ModList::renameMod(int index, const QString &newName)
ModInfo::Ptr modInfo = ModInfo::getByIndex(index);
QString oldName = modInfo->name();
- if (newName != oldName && modInfo->setName(nameFixed)) {
- // before we rename, write back the current profile so we don't lose changes and to ensure
- // there is no scheduled asynchronous rewrite anytime soon
- m_Profile->writeModlistNow();
+ if (newName != oldName) {
+ // before we rename, ensure there is no scheduled asynchronous to rewrite
+ m_Profile->cancelModlistWrite();
- // this just disabled the mod in all profiles. The recipient of modRenamed must fix that
- emit modRenamed(oldName, nameFixed);
+
+ if (modInfo->setName(nameFixed))
+ // Notice there is a good chance that setName() updated the modinfo indexes
+ // the modRenamed() call will refresh the indexes in the current profile
+ // and update the modlists in all profiles
+ emit modRenamed(oldName, nameFixed);
}
// invalidate the currently displayed state of this list
diff --git a/src/organizercore.cpp b/src/organizercore.cpp
index 7668485c..121de21a 100644
--- a/src/organizercore.cpp
+++ b/src/organizercore.cpp
@@ -1196,30 +1196,35 @@ HANDLE OrganizerCore::spawnBinaryProcess(const QFileInfo &binary,
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();
- if (binPath.startsWith(modsPath, Qt::CaseInsensitive)) {
- // binary was installed as a MO mod. Need to start it through a (hooked)
- // proxy to ensure pathes are correct
+ 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);
+ }
- QString cwdPath = currentDirectory.absolutePath();
+ if (virtualizedBin) {
+ int binOffset = binPath.indexOf('/', modsPath.length() + 1);
+ binPath = m_GamePlugin->dataDirectory().absolutePath() + binPath.mid(binOffset, -1);
+ }
- int binOffset = binPath.indexOf('/', modsPath.length() + 1);
- int cwdOffset = cwdPath.indexOf('/', modsPath.length() + 1);
- QString dataBinPath = m_GamePlugin->dataDirectory().absolutePath()
- + binPath.mid(binOffset, -1);
- QString dataCwd = m_GamePlugin->dataDirectory().absolutePath()
- + cwdPath.mid(cwdOffset, -1);
QString cmdline
= QString("launch \"%1\" \"%2\" %3")
- .arg(QDir::toNativeSeparators(dataCwd),
- QDir::toNativeSeparators(dataBinPath), arguments);
+ .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 <" << binary.absoluteFilePath() << "," << arguments << "," << currentDirectory.absolutePath() << ">";
+ qDebug() << "Spawning direct process <" << binPath << "," << arguments << "," << cwdPath << ">";
return startBinary(binary, arguments, currentDirectory, true);
}
} else {
diff --git a/src/settings.cpp b/src/settings.cpp
index b2fc8939..d1130e05 100644
--- a/src/settings.cpp
+++ b/src/settings.cpp
@@ -601,10 +601,31 @@ void Settings::query(QWidget *parent)
tabs.push_back(std::unique_ptr<SettingsTab>(new WorkaroundsTab(this, dialog)));
if (dialog.exec() == QDialog::Accepted) {
+ // remember settings before change
+ QMap<QString, QString> before;
+ m_Settings.beginGroup("Settings");
+ for (auto k : m_Settings.allKeys())
+ before[k] = m_Settings.value(k).toString();
+ m_Settings.endGroup();
+
// transfer modified settings to configuration file
for (std::unique_ptr<SettingsTab> const &tab: tabs) {
tab->update();
}
+
+ // print "changed" settings
+ m_Settings.beginGroup("Settings");
+ bool first_update = true;
+ for (auto k : m_Settings.allKeys())
+ if (m_Settings.value(k).toString() != before[k] && !k.contains("username") && !k.contains("password"))
+ {
+ if (first_update) {
+ qDebug("Changed settings:");
+ first_update = false;
+ }
+ qDebug(" %s=%s", k.toUtf8().data(), m_Settings.value(k).toString().toUtf8().data());
+ }
+ m_Settings.endGroup();
}
}