mirror of
https://github.com/ModOrganizer2/modorganizer.git
synced 2026-07-27 13:58:24 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c1a56f77ea | ||
|
|
91bd9b1eb6 | ||
|
|
548744d746 | ||
|
|
57e65abf75 | ||
|
|
d41cd8d877 | ||
|
|
9c82a1102e | ||
|
|
e623c13400 | ||
|
|
9c9a43ec8d | ||
|
|
eb3cfe2594 | ||
|
|
482f3d1f6d | ||
|
|
fed4ea257a |
+70
-11
@@ -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)
|
||||
|
||||
+30
-1
@@ -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)));
|
||||
|
||||
@@ -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);
|
||||
|
||||
+9
-6
@@ -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
|
||||
|
||||
+18
-13
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
#include "Winver.h"
|
||||
|
||||
#define VER_FILEVERSION 2,1,0,1
|
||||
#define VER_FILEVERSION_STR "2.1.0.1\0"
|
||||
#define VER_FILEVERSION 2,1,1
|
||||
#define VER_FILEVERSION_STR "2.1.1\0"
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION VER_FILEVERSION
|
||||
|
||||
Reference in New Issue
Block a user