summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/installationmanager.cpp96
-rw-r--r--src/installationmanager.h77
-rw-r--r--src/organizercore.cpp14
3 files changed, 118 insertions, 69 deletions
diff --git a/src/installationmanager.cpp b/src/installationmanager.cpp
index 413f567a..ad7cdcd5 100644
--- a/src/installationmanager.cpp
+++ b/src/installationmanager.cpp
@@ -64,6 +64,12 @@ using namespace MOBase;
using namespace MOShared;
+InstallationResult::InstallationResult(IPluginInstaller::EInstallResult result) :
+ m_result(result), m_name(), m_iniTweaks(false), m_backup(false), m_merged(false), m_replaced(false)
+{
+}
+
+
template <typename T>
static T resolveFunction(QLibrary &lib, const char *name)
{
@@ -353,8 +359,7 @@ IPluginInstaller::EInstallResult InstallationManager::installArchive(GuessedValu
// in earlier versions the modName was copied here and the copy passed to install. I don't know why I did this and it causes
// a problem if this is called by the bundle installer and the bundled installer adds additional names that then end up being used,
// because the caller will then not have the right name.
- bool iniTweaks;
- return install(archiveName, modName, iniTweaks, modId);
+ return install(archiveName, modName, modId).result();
}
@@ -374,10 +379,13 @@ QString InstallationManager::generateBackupName(const QString &directoryName) co
}
-IPluginInstaller::EInstallResult InstallationManager::testOverwrite(GuessedValue<QString> &modName, bool *merge)
+InstallationResult InstallationManager::testOverwrite(GuessedValue<QString> &modName)
{
QString targetDirectory = QDir::fromNativeSeparators(m_ModsDirectory + "\\" + modName);
+ // this is only returned on success
+ InstallationResult result{ IPluginInstaller::RESULT_SUCCESS };
+
while (QDir(targetDirectory).exists()) {
Settings &settings(Settings::instance());
@@ -393,12 +401,14 @@ IPluginInstaller::EInstallResult InstallationManager::testOverwrite(GuessedValue
QString backupDirectory = generateBackupName(targetDirectory);
if (!copyDir(targetDirectory, backupDirectory, false)) {
reportError(tr("Failed to create backup"));
- return IPluginInstaller::RESULT_FAILED;
+ return { IPluginInstaller::RESULT_FAILED };
}
}
- if (merge != nullptr) {
- *merge = (overwriteDialog.action() == QueryOverwriteDialog::ACT_MERGE);
- }
+
+ result.m_merged = overwriteDialog.action() == QueryOverwriteDialog::ACT_MERGE;
+ result.m_replaced = overwriteDialog.action() == QueryOverwriteDialog::ACT_REPLACE;
+ result.m_backup = overwriteDialog.backup();
+
if (overwriteDialog.action() == QueryOverwriteDialog::ACT_RENAME) {
bool ok = false;
QString name = QInputDialog::getText(m_ParentWidget, tr("Mod Name"), tr("Name"),
@@ -406,7 +416,7 @@ IPluginInstaller::EInstallResult InstallationManager::testOverwrite(GuessedValue
if (ok && !name.isEmpty()) {
modName.update(name, GUESS_USER);
if (!ensureValidModName(modName)) {
- return IPluginInstaller::RESULT_FAILED;
+ return { IPluginInstaller::RESULT_FAILED };
}
targetDirectory = QDir::fromNativeSeparators(m_ModsDirectory) + "/" + modName;
}
@@ -441,20 +451,20 @@ IPluginInstaller::EInstallResult InstallationManager::testOverwrite(GuessedValue
} else {
log::error("failed to restore original settings: {}", metaFilename);
}
- return IPluginInstaller::RESULT_SUCCESS;
+ return result;
} else if (overwriteDialog.action() == QueryOverwriteDialog::ACT_MERGE) {
- return IPluginInstaller::RESULT_SUCCESS;
+ return result;
} else /* if (overwriteDialog.action() == QueryOverwriteDialog::ACT_NONE) */ {
- return IPluginInstaller::RESULT_CANCELED;
+ return { IPluginInstaller::RESULT_CANCELED };
}
} else {
- return IPluginInstaller::RESULT_CANCELED;
+ return { IPluginInstaller::RESULT_CANCELED };
}
}
QDir().mkdir(targetDirectory);
- return IPluginInstaller::RESULT_SUCCESS;;
+ return result;
}
@@ -473,27 +483,30 @@ bool InstallationManager::ensureValidModName(GuessedValue<QString> &name) const
return true;
}
-IPluginInstaller::EInstallResult InstallationManager::doInstall(GuessedValue<QString> &modName, QString gameName, int modID,
- const QString &version, const QString &newestVersion,
- int categoryID, int fileCategoryID, const QString &repository)
+InstallationResult InstallationManager::doInstall(
+ GuessedValue<QString> &modName, QString gameName, int modID,
+ const QString &version, const QString &newestVersion,
+ int categoryID, int fileCategoryID, const QString &repository)
{
if (!ensureValidModName(modName)) {
- return IPluginInstaller::RESULT_FAILED;
+ return { IPluginInstaller::RESULT_FAILED };
}
bool merge = false;
// determine target directory
- IPluginInstaller::EInstallResult result = testOverwrite(modName, &merge);
- if (result != IPluginInstaller::RESULT_SUCCESS) {
+ InstallationResult result = testOverwrite(modName);
+ if (!result) {
return result;
}
+ result.m_name = modName;
+
QString targetDirectory = QDir(m_ModsDirectory + "/" + modName).canonicalPath();
QString targetDirectoryNative = QDir::toNativeSeparators(targetDirectory);
log::debug("installing to \"{}\"", targetDirectoryNative);
if (!extractFiles(targetDirectory, "", true, false)) {
- return IPluginInstaller::RESULT_CANCELED;
+ return { IPluginInstaller::RESULT_CANCELED };
}
// Copy the created files:
@@ -547,7 +560,7 @@ IPluginInstaller::EInstallResult InstallationManager::doInstall(GuessedValue<QSt
settingsFile.endGroup();
}
- return IPluginInstaller::RESULT_SUCCESS;
+ return result;
}
@@ -601,10 +614,8 @@ void InstallationManager::postInstallCleanup()
}
}
-IPluginInstaller::EInstallResult InstallationManager::install(const QString &fileName,
- GuessedValue<QString> &modName,
- bool &hasIniTweaks,
- int modID)
+InstallationResult InstallationManager::install(
+ const QString &fileName, GuessedValue<QString> &modName, int modID)
{
m_IsRunning = true;
ON_BLOCK_EXIT([this]() { m_IsRunning = false; });
@@ -612,7 +623,7 @@ IPluginInstaller::EInstallResult InstallationManager::install(const QString &fil
QFileInfo fileInfo(fileName);
if (!getSupportedExtensions().contains(fileInfo.suffix(), Qt::CaseInsensitive)) {
reportError(tr("File format \"%1\" not supported").arg(fileInfo.suffix()));
- return IPluginInstaller::RESULT_FAILED;
+ return InstallationResult(IPluginInstaller::RESULT_FAILED);
}
modName.setFilter(&fixDirectoryName);
@@ -703,7 +714,6 @@ IPluginInstaller::EInstallResult InstallationManager::install(const QString &fil
std::shared_ptr<IFileTree> filesTree =
archiveOpen ? ArchiveFileTree::makeTree(*m_ArchiveHandler) : nullptr;
- IPluginInstaller::EInstallResult installResult = IPluginInstaller::RESULT_NOTATTEMPTED;
auto installers = m_PluginContainer->plugins<IPluginInstaller>();
@@ -711,6 +721,8 @@ IPluginInstaller::EInstallResult InstallationManager::install(const QString &fil
return lhs->priority() > rhs->priority();
});
+ InstallationResult installResult(IPluginInstaller::RESULT_NOTATTEMPTED);
+
for (IPluginInstaller *installer : installers) {
// don't use inactive installers (installer can't be null here but vc static code analysis thinks it could)
if ((installer == nullptr) || !m_PluginContainer->isEnabled(installer)) {
@@ -718,11 +730,11 @@ IPluginInstaller::EInstallResult InstallationManager::install(const QString &fil
}
// try only manual installers if that was requested
- if (installResult == IPluginInstaller::RESULT_MANUALREQUESTED) {
+ if (installResult.result() == IPluginInstaller::RESULT_MANUALREQUESTED) {
if (!installer->isManualInstaller()) {
continue;
}
- } else if (installResult != IPluginInstaller::RESULT_NOTATTEMPTED) {
+ } else if (installResult.result() != IPluginInstaller::RESULT_NOTATTEMPTED) {
break;
}
@@ -732,9 +744,9 @@ IPluginInstaller::EInstallResult InstallationManager::install(const QString &fil
= dynamic_cast<IPluginInstallerSimple *>(installer);
if ((installerSimple != nullptr) && (filesTree != nullptr)
&& (installer->isArchiveSupported(filesTree))) {
- installResult
+ installResult.m_result
= installerSimple->install(modName, filesTree, version, modID);
- if (installResult == IPluginInstaller::RESULT_SUCCESS) {
+ if (installResult) {
// Downcast to an actual ArchiveFileTree and map to the archive. Test if
// the tree is still an ArchiveFileTree, otherwize it means the installer
@@ -755,12 +767,13 @@ IPluginInstaller::EInstallResult InstallationManager::install(const QString &fil
// the simple installer only prepares the installation, the rest
// works the same for all installers
- installResult = doInstall(modName, gameName, modID, version, newestVersion, categoryID, fileCategoryID, repository);
+ installResult = doInstall(modName, gameName, modID, version,
+ newestVersion, categoryID, fileCategoryID, repository);
}
}
}
- if (installResult != IPluginInstaller::RESULT_CANCELED) { // custom case
+ if (installResult.result() != IPluginInstaller::RESULT_CANCELED) { // custom case
IPluginInstallerCustom *installerCustom
= dynamic_cast<IPluginInstallerCustom *>(installer);
if ((installerCustom != nullptr)
@@ -771,7 +784,7 @@ IPluginInstaller::EInstallResult InstallationManager::install(const QString &fil
std::set<QString> installerExt
= installerCustom->supportedExtensions();
if (installerExt.find(fileInfo.suffix()) != installerExt.end()) {
- installResult
+ installResult.m_result
= installerCustom->install(modName, gameName, fileName, version, modID);
unsigned int idx = ModInfo::getIndex(modName);
if (idx != UINT_MAX) {
@@ -787,7 +800,7 @@ IPluginInstaller::EInstallResult InstallationManager::install(const QString &fil
// act upon the installation result. at this point the files have already been
// extracted to the correct location
- switch (installResult) {
+ switch (installResult.result()) {
case IPluginInstaller::RESULT_FAILED: {
QMessageBox::information(qApp->activeWindow(), tr("Installation failed"),
tr("Something went wrong while installing this mod."),
@@ -798,10 +811,11 @@ IPluginInstaller::EInstallResult InstallationManager::install(const QString &fil
case IPluginInstaller::RESULT_SUCCESSCANCEL: {
if (filesTree != nullptr) {
auto iniTweakEntry = filesTree->find("INI Tweaks", FileTreeEntry::DIRECTORY);
- hasIniTweaks = iniTweakEntry != nullptr
+ installResult.m_iniTweaks = iniTweakEntry != nullptr
&& !iniTweakEntry->astree()->empty();
}
- return IPluginInstaller::RESULT_SUCCESS;
+ installResult.m_result = IPluginInstaller::RESULT_SUCCESS;
+ return installResult;
} break;
case IPluginInstaller::RESULT_NOTATTEMPTED:
case IPluginInstaller::RESULT_MANUALREQUESTED: {
@@ -811,7 +825,7 @@ IPluginInstaller::EInstallResult InstallationManager::install(const QString &fil
return installResult;
}
}
- if (installResult == IPluginInstaller::RESULT_NOTATTEMPTED) {
+ if (installResult.result() == IPluginInstaller::RESULT_NOTATTEMPTED) {
reportError(tr("None of the available installer plugins were able to handle that archive.\n"
"This is likely due to a corrupted or incompatible download or unrecognized archive format."));
}
@@ -877,14 +891,12 @@ void InstallationManager::notifyInstallationStart(QString const& archive, bool r
}
}
-void InstallationManager::notifyInstallationEnd(
- MOBase::IPluginInstaller::EInstallResult result,
- ModInfo::Ptr newMod)
+void InstallationManager::notifyInstallationEnd(const InstallationResult& result, ModInfo::Ptr newMod)
{
auto& installers = m_PluginContainer->plugins<IPluginInstaller>();
for (auto* installer : installers) {
if (m_PluginContainer->isEnabled(installer)) {
- installer->onInstallationEnd(result, newMod.get());
+ installer->onInstallationEnd(result.result(), newMod.get());
}
}
}
diff --git a/src/installationmanager.h b/src/installationmanager.h
index 277c276a..a5ec11d9 100644
--- a/src/installationmanager.h
+++ b/src/installationmanager.h
@@ -37,15 +37,48 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "modinfo.h"
#include "plugincontainer.h"
+// contains installation result from the manager, internal class
+// for MO2 that is not forwarded to plugin
+class InstallationResult {
+public:
+
+ // result status of the installation
+ //
+ auto result() const { return m_result; }
+
+ // information about the installation, only valid for successful
+ // installation
+ //
+ QString name() const { return m_name; }
+ bool backupCreated() const { return m_backup; }
+ bool merged() const { return m_merged; }
+ bool replaced() const { return m_replaced; }
+ bool hasIniTweaks() const { return m_iniTweaks; }
+ bool mergedOrReplaced() const { return merged() || replaced(); }
+
+ // check if the installation was a success
+ //
+ explicit operator bool() const { return result() == MOBase::IPluginInstaller::EInstallResult::RESULT_SUCCESS; }
+
+private:
+
+ friend class InstallationManager;
+
+ // create a failed result
+ InstallationResult(MOBase::IPluginInstaller::EInstallResult result = MOBase::IPluginInstaller::EInstallResult::RESULT_FAILED);
+
+ MOBase::IPluginInstaller::EInstallResult m_result;
+
+ QString m_name;
+
+ bool m_iniTweaks;
+ bool m_backup;
+ bool m_merged;
+ bool m_replaced;
+
+};
+
-/**
- * @brief manages the installation of mod archives
- * This currently supports two special kind of archives:
- * - "simple" archives: properly packaged archives without options, so they can be extracted to the (virtual) data directory directly
- * - "complex" bain archives: archives with options for the bain system.
- * All other archives are managed through the manual "InstallDialog"
- * @todo this may be a good place to support plugins
- **/
class InstallationManager : public QObject, public MOBase::IInstallationManager
{
Q_OBJECT
@@ -79,7 +112,7 @@ public:
* @param result The result of the installation process.
* @param currentMod The newly install mod, if result is SUCCESS, a null pointer otherwise.
*/
- void notifyInstallationEnd(MOBase::IPluginInstaller::EInstallResult result, ModInfo::Ptr newMod);
+ void notifyInstallationEnd(const InstallationResult& result, ModInfo::Ptr newMod);
/**
* @brief update the directory where mods are to be installed
@@ -107,7 +140,7 @@ public:
* @return true if the archive was installed, false if installation failed or was refused
* @exception std::exception an exception may be thrown if the archive can't be opened (maybe the format is invalid or the file is damaged)
**/
- MOBase::IPluginInstaller::EInstallResult install(const QString &fileName, MOBase::GuessedValue<QString> &modName, bool &hasIniTweaks, int modID = 0);
+ InstallationResult install(const QString &fileName, MOBase::GuessedValue<QString> &modName, int modID = 0);
/**
* @return true if the installation was canceled
@@ -148,7 +181,7 @@ public:
* @note The temporary file is automatically cleaned up after the installation.
* @note This call can be very slow if the archive is large and "solid".
*/
- virtual QString extractFile(std::shared_ptr<const MOBase::FileTreeEntry> entry, bool silent = false) override;
+ QString extractFile(std::shared_ptr<const MOBase::FileTreeEntry> entry, bool silent = false) override;
/**
* @brief Extract the specified files from the currently opened archive to a temporary location.
@@ -171,7 +204,7 @@ public:
* IFileTree and thus to given a list of entries flattened (this was not possible with the
* QStringList version since these were based on the name of the file inside the archive).
*/
- virtual QStringList extractFiles(std::vector<std::shared_ptr<const MOBase::FileTreeEntry>> const& entries, bool silent = false) override;
+ QStringList extractFiles(std::vector<std::shared_ptr<const MOBase::FileTreeEntry>> const& entries, bool silent = false) override;
/**
* @brief Create a new file on the disk corresponding to the given entry.
@@ -184,7 +217,7 @@ public:
*
* @return the path to the created file.
*/
- virtual QString createFile(std::shared_ptr<const MOBase::FileTreeEntry> entry) override;
+ QString createFile(std::shared_ptr<const MOBase::FileTreeEntry> entry) override;
/**
* @brief Installs the given archive.
@@ -195,22 +228,26 @@ public:
*
* @return the installation result.
*/
- virtual MOBase::IPluginInstaller::EInstallResult installArchive(MOBase::GuessedValue<QString> &modName, const QString &archiveName, int modId = 0) override;
+ MOBase::IPluginInstaller::EInstallResult installArchive(MOBase::GuessedValue<QString> &modName, const QString &archiveName, int modId = 0) override;
/**
- * @brief test if the specified mod name is free. If not, query the user how to proceed
* @param modName current possible names for the mod
- * @param merge if this value is not null, the value will be set to whether the use chose to merge or replace
- * @return true if we can proceed with the installation, false if the user canceled or in case of an unrecoverable error
+ *
+ * @return an installation result containing information from the user.
*/
- virtual MOBase::IPluginInstaller::EInstallResult testOverwrite(MOBase::GuessedValue<QString> &modName, bool *merge = nullptr);
+ InstallationResult testOverwrite(MOBase::GuessedValue<QString>& modName);
QString generateBackupName(const QString &directoryName) const;
private:
- MOBase::IPluginInstaller::EInstallResult doInstall(MOBase::GuessedValue<QString> &modName, QString gameName,
- int modID, const QString &version, const QString &newestVersion, int categoryID, int fileCategoryID, const QString &repository);
+ // actually perform the installation (write files to the disk, etc.), returns the
+ // installation result
+ //
+ InstallationResult doInstall(
+ MOBase::GuessedValue<QString> &modName, QString gameName,
+ int modID, const QString &version, const QString &newestVersion,
+ int categoryID, int fileCategoryID, const QString &repository);
/**
* @brief Clean the list of created files by removing all entries that are not
diff --git a/src/organizercore.cpp b/src/organizercore.cpp
index 159296b7..ecd4b34e 100644
--- a/src/organizercore.cpp
+++ b/src/organizercore.cpp
@@ -692,8 +692,8 @@ MOBase::IPluginGame *OrganizerCore::getGame(const QString &name) const
MOBase::IModInterface *OrganizerCore::createMod(GuessedValue<QString> &name)
{
- bool merge = false;
- if (m_InstallationManager.testOverwrite(name, &merge) != IPluginInstaller::EInstallResult::RESULT_SUCCESS) {
+ auto result = m_InstallationManager.testOverwrite(name);
+ if (!result) {
return nullptr;
}
@@ -706,7 +706,7 @@ MOBase::IModInterface *OrganizerCore::createMod(GuessedValue<QString> &name)
QSettings settingsFile(targetDirectory + "/meta.ini", QSettings::IniFormat);
- if (!merge) {
+ if (!result.merged()) {
settingsFile.setValue("modid", 0);
settingsFile.setValue("version", "");
settingsFile.setValue("newestVersion", "");
@@ -783,7 +783,7 @@ MOBase::IModInterface *OrganizerCore::installMod(const QString &fileName,
m_InstallationManager.setModsDirectory(m_Settings.paths().mods());
m_InstallationManager.notifyInstallationStart(fileName, reinstallation, currentMod);
auto result = m_InstallationManager.install(fileName, modName, hasIniTweaks);
- if (result == IPluginInstaller::RESULT_SUCCESS) {
+ if (result) {
MessageDialog::showMessage(tr("Installation successful"),
qApp->activeWindow());
refresh();
@@ -865,7 +865,7 @@ ModInfo::Ptr OrganizerCore::installDownload(int index, int priority)
m_InstallationManager.setModsDirectory(m_Settings.paths().mods());
m_InstallationManager.notifyInstallationStart(fileName, false, currentMod);
auto result = m_InstallationManager.install(fileName, modName, hasIniTweaks);
- if (result == IPluginInstaller::RESULT_SUCCESS) {
+ if (result) {
MessageDialog::showMessage(tr("Installation successful"),
qApp->activeWindow());
refresh();
@@ -876,7 +876,7 @@ ModInfo::Ptr OrganizerCore::installDownload(int index, int priority)
modInfo = ModInfo::getByIndex(modIndex);
modInfo->addInstalledFile(modID, fileID);
- if (priority != -1) {
+ if (priority != -1 && !result.mergedOrReplaced()) {
m_ModList.changeModPriority(modIndex, priority);
}
@@ -891,7 +891,7 @@ ModInfo::Ptr OrganizerCore::installDownload(int index, int priority)
}
m_ModList.notifyModInstalled(modInfo.get());
- m_InstallationManager.notifyInstallationEnd(IPluginInstaller::RESULT_SUCCESS, modInfo);
+ m_InstallationManager.notifyInstallationEnd(result, modInfo);
} else {
reportError(tr("mod not found: %1").arg(qUtf8Printable(modName)));
}