summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAl <26797547+Al12rs@users.noreply.github.com>2020-06-01 13:56:26 -0700
committerGitHub <noreply@github.com>2020-06-01 13:56:26 -0700
commit226b2b1843bbf41581757f44f8a2f4728f0a4b19 (patch)
treeef1daf7ea519dd2bc7c9ff4ccb64b5b3a734b50b /src
parenta92fdeb40064738df18d006285dfd05463d64223 (diff)
parent926267faa059082865fc3a1d62d3090645267e37 (diff)
Merge pull request #1098 from Holt59/install-manager-improvements
Installation manager improvements
Diffstat (limited to 'src')
-rw-r--r--src/archivefiletree.cpp24
-rw-r--r--src/installationmanager.cpp113
-rw-r--r--src/installationmanager.h34
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;