From afe0d749c438b086a7efa08e4d443be94f56d101 Mon Sep 17 00:00:00 2001 From: Tannin Date: Sat, 7 May 2016 00:18:23 +0200 Subject: added option to use a mod as the target to create now files instead of "overwrite" --- src/CMakeLists.txt | 2 + src/editexecutablesdialog.cpp | 57 +++++++++++++++++++++++------ src/editexecutablesdialog.h | 21 +++++++++-- src/editexecutablesdialog.ui | 45 +++++++++++++++++++++++ src/mainwindow.cpp | 55 +++++++++++++++++++--------- src/mainwindow.h | 1 + src/organizercore.cpp | 85 ++++++++++++++++++++++++++++++++++++------- src/organizercore.h | 20 ++++++---- src/usvfsconnector.cpp | 1 + 9 files changed, 235 insertions(+), 52 deletions(-) (limited to 'src') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a8b43c59..c6c038e8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -84,6 +84,7 @@ SET(organizer_SRCS organizercore.cpp instancemanager.cpp usvfsconnector.cpp + eventfilter.cpp shared/inject.cpp shared/windows_error.cpp @@ -170,6 +171,7 @@ SET(organizer_HDRS iuserinterface.h instancemanager.h usvfsconnector.h + eventfilter.h shared/inject.h shared/windows_error.h diff --git a/src/editexecutablesdialog.cpp b/src/editexecutablesdialog.cpp index 05a7603d..42d6f58b 100644 --- a/src/editexecutablesdialog.cpp +++ b/src/editexecutablesdialog.cpp @@ -29,15 +29,20 @@ along with Mod Organizer. If not, see . using namespace MOBase; using namespace MOShared; -EditExecutablesDialog::EditExecutablesDialog(const ExecutablesList &executablesList, QWidget *parent) +EditExecutablesDialog::EditExecutablesDialog( + const ExecutablesList &executablesList, const ModList &modList, + Profile *profile, QWidget *parent) : TutorableDialog("EditExecutables", parent) , ui(new Ui::EditExecutablesDialog) , m_CurrentItem(nullptr) , m_ExecutablesList(executablesList) + , m_Profile(profile) { ui->setupUi(this); refreshExecutablesWidget(); + + ui->newFilesModBox->addItems(modList.allMods()); } EditExecutablesDialog::~EditExecutablesDialog() @@ -86,23 +91,30 @@ void EditExecutablesDialog::resetInput() ui->appIDOverwriteEdit->clear(); ui->overwriteAppIDBox->setChecked(false); ui->useAppIconCheckBox->setChecked(false); + ui->newFilesModCheckBox->setChecked(false); m_CurrentItem = nullptr; } void EditExecutablesDialog::saveExecutable() { - m_ExecutablesList.updateExecutable(ui->titleEdit->text(), - QDir::fromNativeSeparators(ui->binaryEdit->text()), - ui->argumentsEdit->text(), - QDir::fromNativeSeparators(ui->workingDirEdit->text()), - ui->overwriteAppIDBox->isChecked() ? - ui->appIDOverwriteEdit->text() : "", - Executable::UseApplicationIcon | Executable::CustomExecutable, - (ui->useAppIconCheckBox->isChecked() ? - Executable::UseApplicationIcon : Executable::Flags()) - | Executable::CustomExecutable); + m_ExecutablesList.updateExecutable( + ui->titleEdit->text(), + QDir::fromNativeSeparators(ui->binaryEdit->text()), + ui->argumentsEdit->text(), + QDir::fromNativeSeparators(ui->workingDirEdit->text()), + ui->overwriteAppIDBox->isChecked() ? + ui->appIDOverwriteEdit->text() : "", + Executable::UseApplicationIcon | Executable::CustomExecutable, + (ui->useAppIconCheckBox->isChecked() ? + Executable::UseApplicationIcon : Executable::Flags()) + | Executable::CustomExecutable); + + if (ui->newFilesModCheckBox->isChecked()) { + m_Profile->storeSetting("custom_overwrites", ui->titleEdit->text(), + ui->newFilesModBox->currentText()); } +} void EditExecutablesDialog::delayedRefresh() @@ -212,8 +224,13 @@ bool EditExecutablesDialog::executableChanged() { if (m_CurrentItem != nullptr) { Executable const &selectedExecutable(m_ExecutablesList.find(m_CurrentItem->text())); + + QString storedCustomOverwrite = m_Profile->setting("custom_overwrites", selectedExecutable.m_Title).toString(); + return selectedExecutable.m_Arguments != ui->argumentsEdit->text() || selectedExecutable.m_SteamAppID != ui->appIDOverwriteEdit->text() + || !storedCustomOverwrite.isEmpty() != ui->newFilesModCheckBox->isChecked() + || !storedCustomOverwrite.isEmpty() && (storedCustomOverwrite != ui->newFilesModBox->currentText()) || selectedExecutable.m_WorkingDirectory != QDir::fromNativeSeparators(ui->workingDirEdit->text()) || selectedExecutable.m_BinaryInfo.absoluteFilePath() != QDir::fromNativeSeparators(ui->binaryEdit->text()) || selectedExecutable.usesOwnIcon() != ui->useAppIconCheckBox->isChecked(); @@ -296,5 +313,23 @@ void EditExecutablesDialog::on_executablesListBox_clicked(const QModelIndex &cur ui->appIDOverwriteEdit->clear(); } ui->useAppIconCheckBox->setChecked(selectedExecutable.usesOwnIcon()); + + int index = -1; + + QString customOverwrite = m_Profile->setting("custom_overwrites", selectedExecutable.m_Title).toString(); + if (!customOverwrite.isEmpty()) { + index = ui->newFilesModBox->findText(customOverwrite); + qDebug("find %s -> %d", qPrintable(customOverwrite), index); + } + + ui->newFilesModCheckBox->setChecked(index != -1); + if (index != -1) { + ui->newFilesModBox->setCurrentIndex(index); + } } } + +void EditExecutablesDialog::on_newFilesModCheckBox_toggled(bool checked) +{ + ui->newFilesModBox->setEnabled(checked); +} diff --git a/src/editexecutablesdialog.h b/src/editexecutablesdialog.h index 4f6c5315..0f3dbaff 100644 --- a/src/editexecutablesdialog.h +++ b/src/editexecutablesdialog.h @@ -24,11 +24,16 @@ along with Mod Organizer. If not, see . #include #include #include "executableslist.h" +#include "profile.h" namespace Ui { class EditExecutablesDialog; } + +class ModList; + + /** * @brief Dialog to manage the list of executables **/ @@ -44,7 +49,10 @@ public: * @param executablesList current list of executables * @param parent parent widget **/ - explicit EditExecutablesDialog(const ExecutablesList &executablesList, QWidget *parent = 0); + explicit EditExecutablesDialog(const ExecutablesList &executablesList, + const ModList &modList, + Profile *profile, + QWidget *parent = 0); ~EditExecutablesDialog(); @@ -56,6 +64,10 @@ public: ExecutablesList getExecutablesList() const; void saveExecutable(); + +private slots: + void on_newFilesModCheckBox_toggled(bool checked); + private slots: void on_binaryEdit_textChanged(const QString &arg1); @@ -89,12 +101,13 @@ private: bool executableChanged(); private: - Ui::EditExecutablesDialog *ui; + Ui::EditExecutablesDialog *ui; - QListWidgetItem *m_CurrentItem; + QListWidgetItem *m_CurrentItem; - ExecutablesList m_ExecutablesList; + ExecutablesList m_ExecutablesList; + Profile *m_Profile; }; #endif // EDITEXECUTABLESDIALOG_H diff --git a/src/editexecutablesdialog.ui b/src/editexecutablesdialog.ui index 2640bb15..b3543c95 100644 --- a/src/editexecutablesdialog.ui +++ b/src/editexecutablesdialog.ui @@ -164,6 +164,27 @@ Right now the only case I know of where this needs to be overwritten is for the + + + + + + If this is enabled, new files are created in the specified mod instead of the "Overwrite" mod. + + + Create Files in Mod instead of Overwrite (*) + + + + + + + false + + + + + @@ -171,6 +192,13 @@ Right now the only case I know of where this needs to be overwritten is for the + + + + (*) This setting is profile-specific + + + @@ -235,6 +263,23 @@ Right now the only case I know of where this needs to be overwritten is for the + + executablesListBox + titleEdit + binaryEdit + browseButton + workingDirEdit + browseDirButton + argumentsEdit + overwriteAppIDBox + appIDOverwriteEdit + newFilesModCheckBox + newFilesModBox + useAppIconCheckBox + addButton + removeButton + closeButton + diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index e174a17c..7944acfc 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1004,13 +1004,8 @@ void MainWindow::startExeAction() { QAction *action = qobject_cast(sender()); if (action != nullptr) { - 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); + m_OrganizerCore.executablesList()->find(action->text())); } else { qCritical("not an action?"); } @@ -1516,14 +1511,7 @@ void MainWindow::installMod(QString fileName) void MainWindow::on_startButton_clicked() { - const Executable &selectedExecutable(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); + m_OrganizerCore.spawnBinary(getSelectedExecutable()); } @@ -1613,7 +1601,9 @@ bool MainWindow::modifyExecutablesDialog() { bool result = false; try { - EditExecutablesDialog dialog(*m_OrganizerCore.executablesList()); + EditExecutablesDialog dialog(*m_OrganizerCore.executablesList(), + *m_OrganizerCore.modList(), + m_OrganizerCore.currentProfile()); if (dialog.exec() == QDialog::Accepted) { m_OrganizerCore.setExecutablesList(dialog.getExecutablesList()); result = true; @@ -2423,7 +2413,32 @@ void MainWindow::information_clicked() } } +void MainWindow::createEmptyMod_clicked() +{ + GuessedValue name; + name.setFilter(&fixDirectoryName); + + while (name->isEmpty()) { + bool ok; + name.update(QInputDialog::getText(this, tr("Create Mod..."), + tr("This will create an empty mod.\n" + "Please enter a name:"), QLineEdit::Normal, "", &ok), + GUESS_USER); + if (!ok) { + return; + } + } + + if (m_OrganizerCore.getMod(name) != nullptr) { + reportError(tr("A mod with this name already exists")); + return; + } + IModInterface *newMod = m_OrganizerCore.createMod(name); + if (newMod == nullptr) { + return; + } +} void MainWindow::createModFromOverwrite() { @@ -2862,6 +2877,8 @@ QMenu *MainWindow::modListContextMenu() QMenu *menu = new QMenu(this); menu->addAction(tr("Install Mod..."), this, SLOT(installMod_clicked())); + menu->addAction(tr("Create empty mod"), this, SLOT(createEmptyMod_clicked())); + menu->addAction(tr("Enable all visible"), this, SLOT(enableVisibleMods())); menu->addAction(tr("Disable all visible"), this, SLOT(disableVisibleMods())); @@ -3529,10 +3546,14 @@ void MainWindow::openDataFile() QString arguments; switch (getBinaryExecuteInfo(targetInfo, binaryInfo, arguments)) { case 1: { - m_OrganizerCore.spawnBinaryDirect(binaryInfo, arguments, m_OrganizerCore.currentProfile()->name(), targetInfo.absolutePath(), ""); + m_OrganizerCore.spawnBinaryDirect( + binaryInfo, arguments, m_OrganizerCore.currentProfile()->name(), + targetInfo.absolutePath(), "", ""); } break; case 2: { - ::ShellExecuteW(nullptr, L"open", ToWString(targetInfo.absoluteFilePath()).c_str(), nullptr, nullptr, SW_SHOWNORMAL); + ::ShellExecuteW(nullptr, L"open", + ToWString(targetInfo.absoluteFilePath()).c_str(), + nullptr, nullptr, SW_SHOWNORMAL); } break; default: { // nop diff --git a/src/mainwindow.h b/src/mainwindow.h index 4e2e8e21..d2f3fb47 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -369,6 +369,7 @@ private slots: // modlist context menu void installMod_clicked(); + void createEmptyMod_clicked(); void restoreBackup_clicked(); void renameMod_clicked(); void removeMod_clicked(); diff --git a/src/organizercore.cpp b/src/organizercore.cpp index b1eaaef3..8f308c88 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -596,7 +596,7 @@ void OrganizerCore::createDefaultProfile() void OrganizerCore::prepareVFS() { - m_USVFS.updateMapping(fileMapping()); + m_USVFS.updateMapping(fileMapping(m_CurrentProfile->name(), QString())); } void OrganizerCore::setCurrentProfile(const QString &profileName) @@ -969,10 +969,21 @@ QStringList OrganizerCore::modsSortedByProfilePriority() const return res; } +void OrganizerCore::spawnBinary(const Executable &exe) +{ + 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 ¤tDirectory, - const QString &steamAppID) + const QString &steamAppID, + const QString &customOverwrite) { LockedDialog *dialog = new LockedDialog(qApp->activeWindow()); dialog->show(); @@ -983,7 +994,7 @@ void OrganizerCore::spawnBinary(const QFileInfo &binary, HANDLE processHandle = spawnBinaryDirect(binary, arguments, m_CurrentProfile->name(), - currentDirectory, steamAppID); + currentDirectory, steamAppID, customOverwrite); if (processHandle != INVALID_HANDLE_VALUE) { if (m_UserInterface != nullptr) { m_UserInterface->setWindowEnabled(false); @@ -1082,7 +1093,8 @@ HANDLE OrganizerCore::spawnBinaryDirect(const QFileInfo &binary, const QString &arguments, const QString &profileName, const QDir ¤tDirectory, - const QString &steamAppID) + const QString &steamAppID, + const QString &customOverwrite) { prepareStart(); @@ -1135,11 +1147,16 @@ HANDLE OrganizerCore::spawnBinaryDirect(const QFileInfo &binary, } if (m_AboutToRun(binary.absoluteFilePath())) { - m_USVFS.updateMapping(fileMapping()); + 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(); 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 @@ -1185,6 +1202,7 @@ HANDLE OrganizerCore::startApplication(const QString &executable, } } QString steamAppID; + QString customOverwrite; if (executable.contains('\\') || executable.contains('/')) { // file path @@ -1200,6 +1218,9 @@ HANDLE OrganizerCore::startApplication(const QString &executable, 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 } @@ -1208,6 +1229,9 @@ HANDLE OrganizerCore::startApplication(const QString &executable, 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; } @@ -1223,7 +1247,7 @@ HANDLE OrganizerCore::startApplication(const QString &executable, } return spawnBinaryDirect(binary, arguments, profileName, currentDirectory, - steamAppID); + steamAppID, customOverwrite); } bool OrganizerCore::waitForApplication(HANDLE handle, LPDWORD exitCode) @@ -1781,7 +1805,8 @@ void OrganizerCore::prepareStart() storeSettings(); } -std::vector OrganizerCore::fileMapping() +std::vector OrganizerCore::fileMapping(const QString &profileName, + const QString &customOverwrite) { // need to wait until directory structure while (m_DirectoryUpdate) { @@ -1789,12 +1814,39 @@ std::vector OrganizerCore::fileMapping() QCoreApplication::processEvents(); } - int overwriteId = m_DirectoryStructure->getOriginByName(L"Overwrite").getID(); - IPluginGame *game = qApp->property("managed_game").value(); - MappingType result = fileMapping( - QDir::toNativeSeparators(game->dataDirectory().absolutePath()), "\\", - directoryStructure(), directoryStructure(), overwriteId); + Profile profile(QDir(m_Settings.getProfileDirectory() + "/" + profileName), + game); + + MappingType result; + + QString dataPath + = QDir::toNativeSeparators(game->dataDirectory().absolutePath()); + + bool overwriteActive = false; + + 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; + + if (modPtr->isRegular()) { + result.insert(result.end(), {QDir::toNativeSeparators(std::get<1>(mod)), + dataPath, true, createTarget}); + } + } + + if (!overwriteActive && !customOverwrite.isEmpty()) { + throw MyException(tr("The designated write target \"%1\" is not enabled.") + .arg(customOverwrite)); + } if (m_CurrentProfile->localSavesEnabled()) { LocalSavegames *localSaves = game->feature(); @@ -1808,6 +1860,13 @@ std::vector OrganizerCore::fileMapping() } } + result.insert(result.end(), { + QDir::toNativeSeparators(m_Settings.getOverwriteDirectory()), + QDir::toNativeSeparators(game->dataDirectory().absolutePath()), + true, + customOverwrite.isEmpty() + }); + for (MOBase::IPluginFileMapper *mapper : m_PluginContainer->plugins()) { IPlugin *plugin = dynamic_cast(mapper); diff --git a/src/organizercore.h b/src/organizercore.h index ea37d72c..cd7af1b8 100644 --- a/src/organizercore.h +++ b/src/organizercore.h @@ -129,8 +129,8 @@ public: void doAfterLogin(const std::function &function) { m_PostLoginTasks.append(function); } - void spawnBinary(const QFileInfo &binary, const QString &arguments = "", const QDir ¤tDirectory = QDir(), const QString &steamAppID = ""); - HANDLE spawnBinaryDirect(const QFileInfo &binary, const QString &arguments, const QString &profileName, const QDir ¤tDirectory, const QString &steamAppID); + void spawnBinary(const Executable &exe); + HANDLE spawnBinaryDirect(const QFileInfo &binary, const QString &arguments, const QString &profileName, const QDir ¤tDirectory, const QString &steamAppID, const QString &customOverwrite); void loginSuccessfulUpdate(bool necessary); void loginFailedUpdate(const QString &message); @@ -173,11 +173,6 @@ public: void refreshModList(bool saveChanges = true); QStringList modsSortedByProfilePriority() const; - /** - * @brief return a descriptor of the mappings real file->virtual file - */ - std::vector fileMapping(); - public: // IPluginDiagnose interface virtual std::vector activeProblems() const; @@ -231,12 +226,23 @@ private: bool testForSteam(); + /** + * @brief return a descriptor of the mappings real file->virtual file + */ + std::vector fileMapping(const QString &profile, + const QString &customOverwrite); + std::vector fileMapping(const QString &dataPath, const QString &relPath, const MOShared::DirectoryEntry *base, const MOShared::DirectoryEntry *directoryEntry, int createDestination); + void spawnBinary(const QFileInfo &binary, const QString &arguments = "", + const QDir ¤tDirectory = QDir(), + const QString &steamAppID = "", + const QString &customOverwrite = ""); + private slots: void directory_refreshed(); diff --git a/src/usvfsconnector.cpp b/src/usvfsconnector.cpp index bc818685..2af28522 100644 --- a/src/usvfsconnector.cpp +++ b/src/usvfsconnector.cpp @@ -136,6 +136,7 @@ UsvfsConnector::~UsvfsConnector() m_WorkerThread.wait(); } + void UsvfsConnector::updateMapping(const MappingType &mapping) { QProgressDialog progress; -- cgit v1.3.1