diff options
| author | Al <26797547+Al12rs@users.noreply.github.com> | 2020-06-01 13:56:26 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-06-01 13:56:26 -0700 |
| commit | 226b2b1843bbf41581757f44f8a2f4728f0a4b19 (patch) | |
| tree | ef1daf7ea519dd2bc7c9ff4ccb64b5b3a734b50b /src | |
| parent | a92fdeb40064738df18d006285dfd05463d64223 (diff) | |
| parent | 926267faa059082865fc3a1d62d3090645267e37 (diff) | |
Merge pull request #1098 from Holt59/install-manager-improvements
Installation manager improvements
Diffstat (limited to 'src')
| -rw-r--r-- | src/archivefiletree.cpp | 24 | ||||
| -rw-r--r-- | src/installationmanager.cpp | 113 | ||||
| -rw-r--r-- | src/installationmanager.h | 34 |
3 files changed, 141 insertions, 30 deletions
diff --git a/src/archivefiletree.cpp b/src/archivefiletree.cpp index 6ba06924..37d80537 100644 --- a/src/archivefiletree.cpp +++ b/src/archivefiletree.cpp @@ -84,14 +84,6 @@ protected: public: // Overrides: /** - * @override - */ - std::shared_ptr<FileTreeEntry> addFile(QString path) override { - // Cannot add file to an archive. - throw UnsupportedOperationException(QObject::tr("Cannot create file within an archive.")); - } - - /** * */ static void mapToArchive(IFileTree const& tree, QString path, FileData* const* data) @@ -118,7 +110,9 @@ public: // Overrides: } else { const ArchiveFileEntry& archiveFileEntry = dynamic_cast<const ArchiveFileEntry&>(*entry); - data[archiveFileEntry.m_Index]->addOutputFileName(path + archiveFileEntry.name()); + if (archiveFileEntry.m_Index != -1) { + data[archiveFileEntry.m_Index]->addOutputFileName(path + archiveFileEntry.name()); + } } } } @@ -135,18 +129,20 @@ public: // Overrides: protected: - /* - * Overriding this to create custom FileTreeEntry with index set to -1. No need to - * override makeFile() since we addFile is overriden. Note that this will not be - * used to create existing tree since we do this manually in doPopulate. + /** + * Overriding makeDirectory and makeFile to create file tree or file entry with index -1. * - * @override */ virtual std::shared_ptr<IFileTree> makeDirectory( std::shared_ptr<const IFileTree> parent, QString name) const override { return std::make_shared<ArchiveFileTreeImpl>(parent, name, -1, std::vector<File>{}); } + virtual std::shared_ptr<FileTreeEntry> makeFile( + std::shared_ptr<const IFileTree> parent, QString name) const override { + return std::make_shared<ArchiveFileEntry>(parent, name, -1); + } + virtual bool doPopulate(std::shared_ptr<const IFileTree> parent, std::vector<std::shared_ptr<FileTreeEntry>>& entries) const override { // Sort by name: diff --git a/src/installationmanager.cpp b/src/installationmanager.cpp index 62a97591..79f5ee91 100644 --- a/src/installationmanager.cpp +++ b/src/installationmanager.cpp @@ -108,6 +108,7 @@ InstallationManager::~InstallationManager() void InstallationManager::setParentWidget(QWidget *widget) { + m_ParentWidget = widget; for (IPluginInstaller *installer : m_Installers) { installer->setParentWidget(widget); } @@ -129,6 +130,7 @@ bool InstallationManager::extractFiles(QDir extractPath) { m_InstallationProgress = new QProgressDialog(m_ParentWidget); ON_BLOCK_EXIT([this]() { + m_InstallationProgress->cancel(); m_InstallationProgress->hide(); m_InstallationProgress->deleteLater(); m_InstallationProgress = nullptr; @@ -139,6 +141,7 @@ bool InstallationManager::extractFiles(QDir extractPath) m_InstallationProgress->setWindowTitle(tr("Extracting files")); m_InstallationProgress->setWindowModality(Qt::WindowModal); m_InstallationProgress->setFixedSize(600, 100); + m_InstallationProgress->setAutoReset(false); m_InstallationProgress->show(); // unpack only the files we need for the installer @@ -154,7 +157,7 @@ bool InstallationManager::extractFiles(QDir extractPath) if (m_Progress != m_InstallationProgress->value()) m_InstallationProgress->setValue(m_Progress); QCoreApplication::processEvents(); - } while (!future.isFinished() || m_InstallationProgress->isVisible()); + } while (!future.isFinished()); if (!future.result()) { if (m_ArchiveHandler->getLastError() == Archive::ERROR_EXTRACT_CANCELLED) { if (!m_ErrorMessage.isEmpty()) { @@ -207,6 +210,60 @@ QStringList InstallationManager::extractFiles(std::vector<std::shared_ptr<const } +QString InstallationManager::createFile(std::shared_ptr<const MOBase::FileTreeEntry> entry) +{ + // Use QTemporaryFile to create the temporary file with the given template: + QTemporaryFile tempFile(QDir::cleanPath(QDir::tempPath() + QDir::separator() + "mo2-install")); + + // Turn-off autoRemove otherwise the file is deleted when destructor is called: + tempFile.setAutoRemove(false); + + // Open/Close the file so that installer can use it properly: + if (!tempFile.open()) { + return QString(); + } + tempFile.close(); + + // fileName() returns the full path since we provide a full path in the constructor: + const QString absPath = tempFile.fileName(); + + m_CreatedFiles[entry] = absPath; + m_TempFilesToDelete.insert(QDir::temp().relativeFilePath(absPath)); + + // Returns the path with native separators: + return QDir::toNativeSeparators(absPath); +} + +void InstallationManager::cleanCreatedFiles(std::shared_ptr<const MOBase::IFileTree> fileTree) +{ + // We simply have to check if all the entries have fileTree as a parent: + for (auto it = std::begin(m_CreatedFiles); it != std::end(m_CreatedFiles); ) { + + // Find the parent - Could this be in FileTreeEntry? + bool found = false; + { + auto parent = it->first->parent(); + while (parent && !found) { + if (parent == fileTree) { + found = true; + } + else { + parent = parent->parent(); + } + } + } + + // If the parent was not found, we remove the entry, otherwize we move to the next one: + if (!found) { + it = m_CreatedFiles.erase(it); + } + else { + ++it; + } + } +} + + IPluginInstaller::EInstallResult InstallationManager::installArchive(GuessedValue<QString> &modName, const QString &archiveName, int modId) { // 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 @@ -368,6 +425,7 @@ IPluginInstaller::EInstallResult InstallationManager::doInstall(GuessedValue<QSt log::debug("installing to \"{}\"", targetDirectoryNative); + // Extract the archive: m_InstallationProgress = new QProgressDialog(m_ParentWidget); ON_BLOCK_EXIT([this] () { m_InstallationProgress->cancel(); @@ -378,6 +436,13 @@ IPluginInstaller::EInstallResult InstallationManager::doInstall(GuessedValue<QSt m_ProgressFile = QString(); }); + // Turn off auto-reset otherwize the progress dialog is reset before the end. This + // is kind of annoying because updateProgress consider percentage of progression + // through the archive (pack), while we are waiting for extracting archive entries, so + // the percentage of in updateProgress is not really related to the percentage of files + // extracted... + m_InstallationProgress->setAutoReset(false); + m_InstallationProgress->setWindowFlags( m_InstallationProgress->windowFlags() & (~Qt::WindowContextHelpButtonHint)); m_InstallationProgress->setWindowModality(Qt::WindowModal); @@ -410,6 +475,24 @@ IPluginInstaller::EInstallResult InstallationManager::doInstall(GuessedValue<QSt } } + // Copy the created files: + for (auto& p : m_CreatedFiles) { + QString destPath = QDir::cleanPath(targetDirectory + QDir::separator() + p.first->path()); + log::debug("Moving {} to {}.", p.second, destPath); + + // We need to remove the path if it exists: + if (QFile::exists(destPath)) { + QFile::remove(destPath); + } + + QDir dir = QFileInfo(destPath).absoluteDir(); + if (!dir.exists()) { + dir.mkpath("."); + } + + QFile::copy(p.second, destPath); + } + QSettings settingsFile(targetDirectory + "/meta.ini", QSettings::IniFormat); // overwrite settings only if they are actually are available or haven't been set before @@ -454,12 +537,12 @@ IPluginInstaller::EInstallResult InstallationManager::doInstall(GuessedValue<QSt } -bool InstallationManager::wasCancelled() +bool InstallationManager::wasCancelled() const { return m_ArchiveHandler->getLastError() == Archive::ERROR_EXTRACT_CANCELLED; } -bool InstallationManager::isRunning() +bool InstallationManager::isRunning() const { return m_IsRunning; } @@ -467,6 +550,10 @@ bool InstallationManager::isRunning() void InstallationManager::postInstallCleanup() { + // Clear the list of created files: + m_CreatedFiles.clear(); + + // Close the archive: m_ArchiveHandler->close(); // directories we may want to remove. sorted from longest to shortest to ensure we remove subdirectories first. @@ -618,15 +705,23 @@ IPluginInstaller::EInstallResult InstallationManager::install(const QString &fil = installerSimple->install(modName, filesTree, version, modID); if (installResult == IPluginInstaller::RESULT_SUCCESS) { - // Note: Have to maintain a pointer to IFileTree because install() takes a - // reference. This could be an issue if ever a plugin creates a new tree that - // is not a ArchiveFileTree. + // Downcast to an actual ArchiveFileTree and map to the archive. Test if + // the tree is still an ArchiveFileTree, otherwize it means the installer + // did some bad stuff. ArchiveFileTree* p = dynamic_cast<ArchiveFileTree*>(filesTree.get()); if (p == nullptr) { throw IncompatibilityException(tr("Invalid file tree returned by plugin.")); } + + // Detach the file tree (this ensure the parent is null and call to path() + // stops at this root): + p->detach(); + p->mapToArchive(m_ArchiveHandler); + // Clean the created files: + cleanCreatedFiles(filesTree); + // 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); @@ -743,9 +838,5 @@ void InstallationManager::registerInstaller(IPluginInstaller *installer) QStringList InstallationManager::getSupportedExtensions() const { - QStringList result; - foreach (const QString &extension, m_SupportedExtensions) { - result.append(extension); - } - return result; + return QStringList(std::begin(m_SupportedExtensions), std::end(m_SupportedExtensions)); } diff --git a/src/installationmanager.h b/src/installationmanager.h index 008a0916..cb355641 100644 --- a/src/installationmanager.h +++ b/src/installationmanager.h @@ -31,6 +31,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>. #include <archive.h> #include <QProgressDialog> #include <set> +#include <map> #include <errorcodes.h> @@ -87,12 +88,12 @@ public: /** * @return true if the installation was canceled **/ - bool wasCancelled(); + bool wasCancelled() const; /** * @return true if an installation is currently in progress **/ - bool isRunning(); + bool isRunning() const; /** * @brief retrieve a string describing the specified error code @@ -110,9 +111,9 @@ public: void registerInstaller(MOBase::IPluginInstaller *installer); /** - * @return list of file extensions we can install + * @return the extensions of archives supported by this installation manager. */ - QStringList getSupportedExtensions() const; + QStringList getSupportedExtensions() const override; /** * @brief Extract the specified file from the currently opened archive to a temporary location. @@ -153,6 +154,19 @@ public: virtual QStringList extractFiles(std::vector<std::shared_ptr<const MOBase::FileTreeEntry>> const& entries) override; /** + * @brief Create a new file on the disk corresponding to the given entry. + * + * This method can be used by installer that needs to create files that are not in the original + * archive. At the end of the installation, if there are entries in the final tree that were used + * to create files, the corresponding files will be moved to the mod folder. + * + * @param entry The entry for which a temporary file should be created. + * + * @return the path to the created file. + */ + virtual QString createFile(std::shared_ptr<const MOBase::FileTreeEntry> entry) override; + + /** * @brief Installs the given archive. * * @param modName Suggested name of the mod. @@ -186,7 +200,13 @@ 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); - //QString generateBackupName(const QString &directoryName) const; + /** + * @brief Clean the list of created files by removing all entries that are not + * in the given tree. + * + * @param tree The parent tree. Usually the tree returned by the installer. + */ + void cleanCreatedFiles(std::shared_ptr<const MOBase::IFileTree> fileTree); bool ensureValidModName(MOBase::GuessedValue<QString> &name) const; @@ -242,6 +262,10 @@ private: QString m_CurrentFile; QString m_ErrorMessage; + // Map from entries in the tree that is used by the installer and absolute + // paths to temporary files: + std::map<std::shared_ptr<const MOBase::FileTreeEntry>, QString> m_CreatedFiles; + QProgressDialog *m_InstallationProgress { nullptr }; int m_Progress; QString m_ProgressFile; |
