diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/installationmanager.cpp | 96 | ||||
| -rw-r--r-- | src/installationmanager.h | 77 | ||||
| -rw-r--r-- | src/organizercore.cpp | 14 |
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))); } |
