Skip to content

Commit

Permalink
Merge pull request #168 from erasmux/dev
Browse files Browse the repository at this point in the history
Various fixes
  • Loading branch information
LePresidente authored Dec 27, 2017
2 parents 35e0bc2 + 548744d commit 91bd9b1
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 33 deletions.
81 changes: 70 additions & 11 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.

#include <cstdarg>
#include <iostream>
#include <sstream>
#include <stdexcept>


Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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()) {
Expand Down Expand Up @@ -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)
Expand Down
31 changes: 30 additions & 1 deletion src/mainwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2595,6 +2595,8 @@ void MainWindow::createEmptyMod_clicked()
if (newMod == nullptr) {
return;
}

m_OrganizerCore.refreshModList();
}

void MainWindow::createModFromOverwrite()
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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()));
Expand Down Expand Up @@ -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)));
Expand Down
1 change: 1 addition & 0 deletions src/mainwindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
15 changes: 9 additions & 6 deletions src/modlist.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
35 changes: 20 additions & 15 deletions src/organizercore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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

QString cwdPath = currentDirectory.absolutePath();

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);
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(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 {
Expand Down
21 changes: 21 additions & 0 deletions src/settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
}

Expand Down

0 comments on commit 91bd9b1

Please sign in to comment.