From f442f5da51931d6c02476f6b77345e5be11698e9 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Mon, 1 Jun 2020 13:58:46 +0200 Subject: Modification following 'archive' update. --- src/installationmanager.cpp | 81 +++++++++++++++++++++++++++------------------ 1 file changed, 48 insertions(+), 33 deletions(-) (limited to 'src/installationmanager.cpp') diff --git a/src/installationmanager.cpp b/src/installationmanager.cpp index 79f5ee91..201868de 100644 --- a/src/installationmanager.cpp +++ b/src/installationmanager.cpp @@ -85,20 +85,26 @@ InstallationManager::InstallationManager() : m_ParentWidget(nullptr), m_SupportedExtensions({"zip", "rar", "7z", "fomod", "001"}), m_IsRunning(false) { - QLibrary archiveLib(QCoreApplication::applicationDirPath() + - "\\dlls\\archive.dll"); - if (!archiveLib.load()) { - throw MyException(QObject::tr("archive.dll not loaded: \"%1\"") - .arg(archiveLib.errorString())); - } - - CreateArchiveType CreateArchiveFunc - = resolveFunction(archiveLib, "CreateArchive"); - - m_ArchiveHandler = CreateArchiveFunc(); + m_ArchiveHandler = CreateArchive(); if (!m_ArchiveHandler->isValid()) { throw MyException(getErrorString(m_ArchiveHandler->getLastError())); } + m_ArchiveHandler->setLogCallback([](auto level, auto const& message) { + switch (level) { + case NArchive::LogLevel::Debug: + log::debug("{}", message); + break; + case NArchive::LogLevel::Info: + log::info("{}", message); + break; + case NArchive::LogLevel::Warning: + log::warn("{}", message); + break; + case NArchive::LogLevel::Error: + log::error("{}", message); + break; + } + }); } InstallationManager::~InstallationManager() @@ -147,10 +153,10 @@ bool InstallationManager::extractFiles(QDir extractPath) // unpack only the files we need for the installer QFuture future = QtConcurrent::run([&]() -> bool { return m_ArchiveHandler->extract( - QDir::tempPath(), - new MethodCallback(this, &InstallationManager::updateProgress), + QDir::tempPath().toStdWString(), + [this](float f) { updateProgress(f); }, nullptr, - new MethodCallback(this, &InstallationManager::report7ZipError) + [this](std::wstring const& error) { report7ZipError(QString::fromStdWString(error)); } ); }); do { @@ -159,7 +165,7 @@ bool InstallationManager::extractFiles(QDir extractPath) QCoreApplication::processEvents(); } while (!future.isFinished()); if (!future.result()) { - if (m_ArchiveHandler->getLastError() == Archive::ERROR_EXTRACT_CANCELLED) { + if (m_ArchiveHandler->getLastError() == Archive::Error::ERROR_EXTRACT_CANCELLED) { if (!m_ErrorMessage.isEmpty()) { throw MyException(tr("Extraction failed: %1").arg(m_ErrorMessage)); } @@ -168,7 +174,7 @@ bool InstallationManager::extractFiles(QDir extractPath) } } else { - throw MyException(tr("Extraction failed: %1").arg(m_ArchiveHandler->getLastError())); + throw MyException(tr("Extraction failed: %1").arg(static_cast(m_ArchiveHandler->getLastError()))); } } @@ -448,12 +454,15 @@ IPluginInstaller::EInstallResult InstallationManager::doInstall(GuessedValuesetWindowModality(Qt::WindowModal); m_InstallationProgress->setFixedSize(600, 100); m_InstallationProgress->show(); + + auto start = std::chrono::system_clock::now(); + QFuture future = QtConcurrent::run([&]() -> bool { return m_ArchiveHandler->extract( - targetDirectory, - new MethodCallback(this, &InstallationManager::updateProgress), - new MethodCallback(this, &InstallationManager::updateProgressFile), - new MethodCallback(this, &InstallationManager::report7ZipError) + targetDirectory.toStdWString(), + [this](float progress) { updateProgress(progress); }, + [this](std::wstring const& filename) { updateProgressFile(QString::fromStdWString(filename)); }, + [this](std::wstring const& error) { report7ZipError(QString::fromStdWString(error)); } ); }); do { @@ -463,15 +472,17 @@ IPluginInstaller::EInstallResult InstallationManager::doInstall(GuessedValuesetLabelText(m_ProgressFile); QCoreApplication::processEvents(); } while (!future.isFinished()); + log::debug("Extraction took {:.3f}s.", std::chrono::duration(std::chrono::system_clock::now() - start).count()); + if (!future.result()) { - if (m_ArchiveHandler->getLastError() == Archive::ERROR_EXTRACT_CANCELLED) { + if (m_ArchiveHandler->getLastError() == Archive::Error::ERROR_EXTRACT_CANCELLED) { if (!m_ErrorMessage.isEmpty()) { throw MyException(tr("Extraction failed: %1").arg(m_ErrorMessage)); } else { return IPluginInstaller::RESULT_CANCELED; } } else { - throw MyException(tr("Extraction failed: %1").arg(m_ArchiveHandler->getLastError())); + throw MyException(tr("Extraction failed: %1").arg(static_cast(m_ArchiveHandler->getLastError()))); } } @@ -539,7 +550,7 @@ IPluginInstaller::EInstallResult InstallationManager::doInstall(GuessedValuegetLastError() == Archive::ERROR_EXTRACT_CANCELLED; + return m_ArchiveHandler->getLastError() == Archive::Error::ERROR_EXTRACT_CANCELLED; } bool InstallationManager::isRunning() const @@ -662,8 +673,12 @@ IPluginInstaller::EInstallResult InstallationManager::install(const QString &fil m_ArchiveHandler->close(); // open the archive and construct the directory tree the installers work on - bool archiveOpen = m_ArchiveHandler->open(fileName, - new MethodCallback(this, &InstallationManager::queryPassword)); + bool archiveOpen = m_ArchiveHandler->open( + fileName.toStdWString(), [this]() { + QString password; + queryPassword(&password); + return password.toStdWString(); + }); if (!archiveOpen) { log::debug("integrated archiver can't open {}: {} ({})", fileName, @@ -793,28 +808,28 @@ IPluginInstaller::EInstallResult InstallationManager::install(const QString &fil QString InstallationManager::getErrorString(Archive::Error errorCode) { switch (errorCode) { - case Archive::ERROR_NONE: { + case Archive::Error::ERROR_NONE: { return tr("no error"); } break; - case Archive::ERROR_LIBRARY_NOT_FOUND: { + case Archive::Error::ERROR_LIBRARY_NOT_FOUND: { return tr("7z.dll not found"); } break; - case Archive::ERROR_LIBRARY_INVALID: { + case Archive::Error::ERROR_LIBRARY_INVALID: { return tr("7z.dll isn't valid"); } break; - case Archive::ERROR_ARCHIVE_NOT_FOUND: { + case Archive::Error::ERROR_ARCHIVE_NOT_FOUND: { return tr("archive not found"); } break; - case Archive::ERROR_FAILED_TO_OPEN_ARCHIVE: { + case Archive::Error::ERROR_FAILED_TO_OPEN_ARCHIVE: { return tr("failed to open archive"); } break; - case Archive::ERROR_INVALID_ARCHIVE_FORMAT: { + case Archive::Error::ERROR_INVALID_ARCHIVE_FORMAT: { return tr("unsupported archive type"); } break; - case Archive::ERROR_LIBRARY_ERROR: { + case Archive::Error::ERROR_LIBRARY_ERROR: { return tr("internal library error"); } break; - case Archive::ERROR_ARCHIVE_INVALID: { + case Archive::Error::ERROR_ARCHIVE_INVALID: { return tr("archive invalid"); } break; default: { -- cgit v1.3.1 From 4b1b897a140589426c9325f040feee741798c167 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Mon, 1 Jun 2020 17:12:20 +0200 Subject: Use a single extractFiles() method and avoir member variables when not needed. --- src/installationmanager.cpp | 171 ++++++++++++++++---------------------------- src/installationmanager.h | 8 +-- 2 files changed, 64 insertions(+), 115 deletions(-) (limited to 'src/installationmanager.cpp') diff --git a/src/installationmanager.cpp b/src/installationmanager.cpp index 201868de..00f06af1 100644 --- a/src/installationmanager.cpp +++ b/src/installationmanager.cpp @@ -131,43 +131,77 @@ void InstallationManager::queryPassword(QString *password) tr("Password"), QLineEdit::Password); } - -bool InstallationManager::extractFiles(QDir extractPath) +bool InstallationManager::extractFiles(QString extractPath, QString title, bool showFilenames) { - m_InstallationProgress = new QProgressDialog(m_ParentWidget); - ON_BLOCK_EXIT([this]() { - m_InstallationProgress->cancel(); - m_InstallationProgress->hide(); - m_InstallationProgress->deleteLater(); - m_InstallationProgress = nullptr; - m_Progress = 0; - }); - m_InstallationProgress->setWindowFlags( - m_InstallationProgress->windowFlags() & (~Qt::WindowContextHelpButtonHint)); - m_InstallationProgress->setWindowTitle(tr("Extracting files")); - m_InstallationProgress->setWindowModality(Qt::WindowModal); - m_InstallationProgress->setFixedSize(600, 100); - m_InstallationProgress->setAutoReset(false); - m_InstallationProgress->show(); + QProgressDialog *installationProgress = new QProgressDialog(m_ParentWidget); + ON_BLOCK_EXIT([=]() { + installationProgress->cancel(); + installationProgress->hide(); + installationProgress->deleteLater(); + }); + installationProgress->setWindowFlags( + installationProgress->windowFlags() & (~Qt::WindowContextHelpButtonHint)); + if (!title.isEmpty()) { + installationProgress->setWindowTitle(title); + } + installationProgress->setWindowModality(Qt::WindowModal); + installationProgress->setFixedSize(600, 100); + + // 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... + installationProgress->setAutoReset(false); + + installationProgress->show(); + + auto start = std::chrono::system_clock::now(); + + // Variable updated by the callbacks: + int progress = 0; + QString progressFile; + QString errorMessage; + + // The callbacks: + ProgressCallback progressCallback = [&progress, installationProgress, this](float p) { + progress = static_cast(p * 100.0); + + if (installationProgress->wasCanceled()) { + m_ArchiveHandler->cancel(); + installationProgress->reset(); + } + }; + FileChangeCallback fileChangeCallback = [&progressFile](std::wstring const& file) { + progressFile = QString::fromStdWString(file); + }; + ErrorCallback errorCallback = [&errorMessage, this](std::wstring const& message) { + errorMessage = QString::fromStdWString(message); + }; // unpack only the files we need for the installer QFuture future = QtConcurrent::run([&]() -> bool { return m_ArchiveHandler->extract( - QDir::tempPath().toStdWString(), - [this](float f) { updateProgress(f); }, - nullptr, - [this](std::wstring const& error) { report7ZipError(QString::fromStdWString(error)); } + extractPath.toStdWString(), + progressCallback, + showFilenames ? fileChangeCallback : nullptr, + errorCallback ); - }); + }); do { - if (m_Progress != m_InstallationProgress->value()) - m_InstallationProgress->setValue(m_Progress); + if (progress != installationProgress->value()) { + installationProgress->setValue(progress); + } + if (showFilenames && progressFile != installationProgress->labelText()) { + installationProgress->setLabelText(progressFile); + } QCoreApplication::processEvents(); } while (!future.isFinished()); + log::debug("Extraction took {:.3f}s.", std::chrono::duration(std::chrono::system_clock::now() - start).count()); if (!future.result()) { if (m_ArchiveHandler->getLastError() == Archive::Error::ERROR_EXTRACT_CANCELLED) { - if (!m_ErrorMessage.isEmpty()) { - throw MyException(tr("Extraction failed: %1").arg(m_ErrorMessage)); + if (!errorMessage.isEmpty()) { + throw MyException(tr("Extraction failed: %1").arg(errorMessage)); } else { return false; @@ -181,14 +215,12 @@ bool InstallationManager::extractFiles(QDir extractPath) return true; } - QString InstallationManager::extractFile(std::shared_ptr entry) { QStringList result = this->extractFiles({ entry }); return result.isEmpty() ? QString() : result[0]; } - QStringList InstallationManager::extractFiles(std::vector> const& entries) { // Remove the directory since mapToArchive would add them: @@ -208,7 +240,7 @@ QStringList InstallationManager::extractFiles(std::vector(percentage * 100.0); - - if (m_InstallationProgress->wasCanceled()) { - m_ArchiveHandler->cancel(); - m_InstallationProgress->reset(); - } - } -} - - -void InstallationManager::updateProgressFile(QString const &fileName) -{ - m_ProgressFile = fileName; -} - - -void InstallationManager::report7ZipError(QString const &errorMessage) -{ - m_ErrorMessage = errorMessage; - m_ArchiveHandler->cancel(); -} - QString InstallationManager::generateBackupName(const QString &directoryName) const { @@ -430,60 +437,8 @@ IPluginInstaller::EInstallResult InstallationManager::doInstall(GuessedValuecancel(); - m_InstallationProgress->hide(); - m_InstallationProgress->deleteLater(); - m_InstallationProgress = nullptr; - m_Progress = 0; - 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); - m_InstallationProgress->setFixedSize(600, 100); - m_InstallationProgress->show(); - - auto start = std::chrono::system_clock::now(); - - QFuture future = QtConcurrent::run([&]() -> bool { - return m_ArchiveHandler->extract( - targetDirectory.toStdWString(), - [this](float progress) { updateProgress(progress); }, - [this](std::wstring const& filename) { updateProgressFile(QString::fromStdWString(filename)); }, - [this](std::wstring const& error) { report7ZipError(QString::fromStdWString(error)); } - ); - }); - do { - if (m_Progress != m_InstallationProgress->value()) - m_InstallationProgress->setValue(m_Progress); - if (m_ProgressFile != m_InstallationProgress->labelText()) - m_InstallationProgress->setLabelText(m_ProgressFile); - QCoreApplication::processEvents(); - } while (!future.isFinished()); - log::debug("Extraction took {:.3f}s.", std::chrono::duration(std::chrono::system_clock::now() - start).count()); - - if (!future.result()) { - if (m_ArchiveHandler->getLastError() == Archive::Error::ERROR_EXTRACT_CANCELLED) { - if (!m_ErrorMessage.isEmpty()) { - throw MyException(tr("Extraction failed: %1").arg(m_ErrorMessage)); - } else { - return IPluginInstaller::RESULT_CANCELED; - } - } else { - throw MyException(tr("Extraction failed: %1").arg(static_cast(m_ArchiveHandler->getLastError()))); - } + if (!extractFiles(targetDirectory, "", true)) { + return IPluginInstaller::RESULT_CANCELED; } // Copy the created files: diff --git a/src/installationmanager.h b/src/installationmanager.h index cb355641..3d0dd35f 100644 --- a/src/installationmanager.h +++ b/src/installationmanager.h @@ -244,7 +244,7 @@ private: * @return true if the extraction was successful, false if the extraciton was * cancelled. If an error occured, an exception is thrown. */ - bool extractFiles(QDir extractPath); + bool extractFiles(QString extractPath, QString title, bool showFilenames); private: @@ -260,16 +260,10 @@ private: Archive *m_ArchiveHandler; 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, QString> m_CreatedFiles; - - QProgressDialog *m_InstallationProgress { nullptr }; - int m_Progress; - QString m_ProgressFile; - std::set m_TempFilesToDelete; QString m_URL; -- cgit v1.3.1 From 046af63dd55ee4b04d79cb2188f169bd8292aa19 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Mon, 1 Jun 2020 17:14:30 +0200 Subject: Remove non-used declarations. Move queryPassword to lambda. --- src/installationmanager.cpp | 10 ++-------- src/installationmanager.h | 8 -------- 2 files changed, 2 insertions(+), 16 deletions(-) (limited to 'src/installationmanager.cpp') diff --git a/src/installationmanager.cpp b/src/installationmanager.cpp index 00f06af1..a60e60eb 100644 --- a/src/installationmanager.cpp +++ b/src/installationmanager.cpp @@ -125,12 +125,6 @@ void InstallationManager::setURL(QString const &url) m_URL = url; } -void InstallationManager::queryPassword(QString *password) -{ - *password = QInputDialog::getText(nullptr, tr("Password required"), - tr("Password"), QLineEdit::Password); -} - bool InstallationManager::extractFiles(QString extractPath, QString title, bool showFilenames) { QProgressDialog *installationProgress = new QProgressDialog(m_ParentWidget); @@ -630,8 +624,8 @@ IPluginInstaller::EInstallResult InstallationManager::install(const QString &fil // open the archive and construct the directory tree the installers work on bool archiveOpen = m_ArchiveHandler->open( fileName.toStdWString(), [this]() { - QString password; - queryPassword(&password); + QString password = QInputDialog::getText(m_ParentWidget, tr("Password required"), + tr("Password"), QLineEdit::Password); return password.toStdWString(); }); if (!archiveOpen) { diff --git a/src/installationmanager.h b/src/installationmanager.h index 3d0dd35f..67e58356 100644 --- a/src/installationmanager.h +++ b/src/installationmanager.h @@ -189,14 +189,6 @@ public: private: - void queryPassword(QString *password); - void updateProgress(float percentage); - void updateProgressFile(const QString &fileName); - void report7ZipError(const QString &errorMessage); - - // Recursive worker function for mapToArchive (takes raw reference for "speed"). - bool unpackSingleFile(const QString &fileName); - MOBase::IPluginInstaller::EInstallResult doInstall(MOBase::GuessedValue &modName, QString gameName, int modID, const QString &version, const QString &newestVersion, int categoryID, int fileCategoryID, const QString &repository); -- cgit v1.3.1 From af0c0dc33e639f116aaa5c0ac4f2541a80106099 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Mon, 1 Jun 2020 17:40:15 +0200 Subject: Correct handling of 7z errors. --- src/installationmanager.cpp | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) (limited to 'src/installationmanager.cpp') diff --git a/src/installationmanager.cpp b/src/installationmanager.cpp index a60e60eb..639dc88a 100644 --- a/src/installationmanager.cpp +++ b/src/installationmanager.cpp @@ -158,18 +158,12 @@ bool InstallationManager::extractFiles(QString extractPath, QString title, bool QString errorMessage; // The callbacks: - ProgressCallback progressCallback = [&progress, installationProgress, this](float p) { - progress = static_cast(p * 100.0); - - if (installationProgress->wasCanceled()) { - m_ArchiveHandler->cancel(); - installationProgress->reset(); - } - }; + ProgressCallback progressCallback = [&progress](float p) { progress = static_cast(p * 100.0); }; FileChangeCallback fileChangeCallback = [&progressFile](std::wstring const& file) { progressFile = QString::fromStdWString(file); }; ErrorCallback errorCallback = [&errorMessage, this](std::wstring const& message) { + m_ArchiveHandler->cancel(); errorMessage = QString::fromStdWString(message); }; @@ -182,7 +176,13 @@ bool InstallationManager::extractFiles(QString extractPath, QString title, bool errorCallback ); }); + + // Wait for the extraction to complete (or be cancelled): do { + if (installationProgress->wasCanceled()) { + m_ArchiveHandler->cancel(); + installationProgress->reset(); + } if (progress != installationProgress->value()) { installationProgress->setValue(progress); } @@ -191,7 +191,8 @@ bool InstallationManager::extractFiles(QString extractPath, QString title, bool } QCoreApplication::processEvents(); } while (!future.isFinished()); - log::debug("Extraction took {:.3f}s.", std::chrono::duration(std::chrono::system_clock::now() - start).count()); + + // Check the result: if (!future.result()) { if (m_ArchiveHandler->getLastError() == Archive::Error::ERROR_EXTRACT_CANCELLED) { if (!errorMessage.isEmpty()) { @@ -206,6 +207,8 @@ bool InstallationManager::extractFiles(QString extractPath, QString title, bool } } + log::debug("Extraction took {:.3f}s.", std::chrono::duration(std::chrono::system_clock::now() - start).count()); + return true; } -- cgit v1.3.1 From 2c20a4115d0fdc4ce81787d8d78bd06fbf838b25 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Mon, 1 Jun 2020 21:06:52 +0200 Subject: Fix opening and extraction of archives with passwords and/or encrypted filenames. --- src/installationmanager.cpp | 33 ++++++++++++++++++++++++++------- src/installationmanager.h | 13 +++++++++++-- 2 files changed, 37 insertions(+), 9 deletions(-) (limited to 'src/installationmanager.cpp') diff --git a/src/installationmanager.cpp b/src/installationmanager.cpp index 639dc88a..d2262d13 100644 --- a/src/installationmanager.cpp +++ b/src/installationmanager.cpp @@ -105,6 +105,12 @@ InstallationManager::InstallationManager() break; } }); + + // Connect the query password slot - This is the only way I found to be able to query user + // from a separate thread. We use a BlockingQueuedConnection so that calling passwordRequested() + // will block until the end of the slot. + connect(this, &InstallationManager::passwordRequested, + this, &InstallationManager::queryPassword, Qt::BlockingQueuedConnection); } InstallationManager::~InstallationManager() @@ -125,6 +131,11 @@ void InstallationManager::setURL(QString const &url) m_URL = url; } +void InstallationManager::queryPassword() { + m_Password = QInputDialog::getText(m_ParentWidget, tr("Password required"), + tr("Password"), QLineEdit::Password); +} + bool InstallationManager::extractFiles(QString extractPath, QString title, bool showFilenames) { QProgressDialog *installationProgress = new QProgressDialog(m_ParentWidget); @@ -625,11 +636,22 @@ IPluginInstaller::EInstallResult InstallationManager::install(const QString &fil m_ArchiveHandler->close(); // open the archive and construct the directory tree the installers work on + bool archiveOpen = m_ArchiveHandler->open( - fileName.toStdWString(), [this]() { - QString password = QInputDialog::getText(m_ParentWidget, tr("Password required"), - tr("Password"), QLineEdit::Password); - return password.toStdWString(); + fileName.toStdWString(), [this]() -> std::wstring { + m_Password = QString(); + + // Note: If we are not in the Qt event thread, we cannot use queryPassword() directly, + // so we emit passwordRequested() that is connected to queryPassword(). The connection is + // made using Qt::BlockingQueuedConnection, so the emit "call" is actually blocking. We + // cannot use emit if we are in the even thread, otherwize we have a deadlock. + if (QThread::currentThread() != QApplication::instance()->thread()) { + emit passwordRequested(); + } + else { + queryPassword(); + } + return m_Password.toStdWString(); }); if (!archiveOpen) { log::debug("integrated archiver can't open {}: {} ({})", @@ -755,8 +777,6 @@ IPluginInstaller::EInstallResult InstallationManager::install(const QString &fil return installResult; } - - QString InstallationManager::getErrorString(Archive::Error errorCode) { switch (errorCode) { @@ -791,7 +811,6 @@ QString InstallationManager::getErrorString(Archive::Error errorCode) } } - void InstallationManager::registerInstaller(IPluginInstaller *installer) { m_Installers.push_back(installer); diff --git a/src/installationmanager.h b/src/installationmanager.h index 67e58356..328272e1 100644 --- a/src/installationmanager.h +++ b/src/installationmanager.h @@ -204,10 +204,16 @@ private: void postInstallCleanup(); +private slots: + + /** + * @brief Query user for password and update the m_Password field. + */ + void queryPassword(); + signals: - void progressUpdate(float percentage); - void progressUpdate(QString const fileName); + void passwordRequested(); private: @@ -253,6 +259,9 @@ private: Archive *m_ArchiveHandler; QString m_CurrentFile; + // Current password: + QString m_Password; + // Map from entries in the tree that is used by the installer and absolute // paths to temporary files: std::map, QString> m_CreatedFiles; -- cgit v1.3.1 From 30361186c14d43b9fa72b7dc4d6ceaf1c3f57868 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Mon, 1 Jun 2020 21:48:52 +0200 Subject: Update installation manager after namespace changes in archive. --- src/installationmanager.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'src/installationmanager.cpp') diff --git a/src/installationmanager.cpp b/src/installationmanager.cpp index d2262d13..0a97225e 100644 --- a/src/installationmanager.cpp +++ b/src/installationmanager.cpp @@ -90,17 +90,18 @@ InstallationManager::InstallationManager() throw MyException(getErrorString(m_ArchiveHandler->getLastError())); } m_ArchiveHandler->setLogCallback([](auto level, auto const& message) { + using LogLevel = ArchiveCallbacks::LogLevel; switch (level) { - case NArchive::LogLevel::Debug: + case LogLevel::Debug: log::debug("{}", message); break; - case NArchive::LogLevel::Info: + case LogLevel::Info: log::info("{}", message); break; - case NArchive::LogLevel::Warning: + case LogLevel::Warning: log::warn("{}", message); break; - case NArchive::LogLevel::Error: + case LogLevel::Error: log::error("{}", message); break; } @@ -169,11 +170,11 @@ bool InstallationManager::extractFiles(QString extractPath, QString title, bool QString errorMessage; // The callbacks: - ProgressCallback progressCallback = [&progress](float p) { progress = static_cast(p * 100.0); }; - FileChangeCallback fileChangeCallback = [&progressFile](std::wstring const& file) { + ArchiveCallbacks::ProgressCallback progressCallback = [&progress](float p) { progress = static_cast(p * 100.0); }; + ArchiveCallbacks::FileChangeCallback fileChangeCallback = [&progressFile](std::wstring const& file) { progressFile = QString::fromStdWString(file); }; - ErrorCallback errorCallback = [&errorMessage, this](std::wstring const& message) { + ArchiveCallbacks::ErrorCallback errorCallback = [&errorMessage, this](std::wstring const& message) { m_ArchiveHandler->cancel(); errorMessage = QString::fromStdWString(message); }; -- cgit v1.3.1 From b8a1365dacd7ffa8705bbb3dcaf2e2dc6613e6fb Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Tue, 2 Jun 2020 12:40:46 +0200 Subject: Update after change to CreateArchive. Remove unused archive handler from self-updater. --- src/archivefiletree.cpp | 12 ++++++------ src/archivefiletree.h | 6 +++--- src/installationmanager.cpp | 11 +++-------- src/installationmanager.h | 5 ++--- src/selfupdater.cpp | 27 --------------------------- src/selfupdater.h | 2 -- 6 files changed, 14 insertions(+), 49 deletions(-) (limited to 'src/installationmanager.cpp') diff --git a/src/archivefiletree.cpp b/src/archivefiletree.cpp index 1f410b34..4bedeb41 100644 --- a/src/archivefiletree.cpp +++ b/src/archivefiletree.cpp @@ -120,8 +120,8 @@ public: // Overrides: /** * */ - void mapToArchive(Archive* archive) const override { - mapToArchive(*this, "", archive->getFileList()); + void mapToArchive(Archive &archive) const override { + mapToArchive(*this, "", archive.getFileList()); } protected: @@ -214,9 +214,9 @@ private: mutable std::vector m_Files; }; -std::shared_ptr ArchiveFileTree::makeTree(Archive* archive) +std::shared_ptr ArchiveFileTree::makeTree(Archive const& archive) { - auto const& data = archive->getFileList(); + auto const& data = archive.getFileList(); std::vector files; files.reserve(data.size()); @@ -255,6 +255,6 @@ void mapToArchive(std::vector const& data, It begin, It end) { } } -void ArchiveFileTree::mapToArchive(Archive* archive, std::vector> const& entries) { - ::mapToArchive(archive->getFileList(), entries.cbegin(), entries.cend()); +void ArchiveFileTree::mapToArchive(Archive &archive, std::vector> const& entries) { + ::mapToArchive(archive.getFileList(), entries.cbegin(), entries.cend()); } diff --git a/src/archivefiletree.h b/src/archivefiletree.h index 0041acd6..e4a5cbdd 100644 --- a/src/archivefiletree.h +++ b/src/archivefiletree.h @@ -37,7 +37,7 @@ public: * * @return a file tree representing the given archive. */ - static std::shared_ptr makeTree(Archive* archive); + static std::shared_ptr makeTree(Archive const& archive); /** * @brief Update the given archive to reflect change in this tree. @@ -48,7 +48,7 @@ public: * @param archive The archive to update. Must be the one used to * create the tree. */ - virtual void mapToArchive(Archive *archive) const = 0; + virtual void mapToArchive(Archive &archive) const = 0; /** * @brief Update the given archive to prepare for the extraction @@ -61,7 +61,7 @@ public: * @param entries List of entries to mark for extraction. All the entries must * come from a tree created with the given archive. */ - static void mapToArchive(Archive* archive, std::vector> const& entries); + static void mapToArchive(Archive &archive, std::vector> const& entries); protected: diff --git a/src/installationmanager.cpp b/src/installationmanager.cpp index 0a97225e..dad5b436 100644 --- a/src/installationmanager.cpp +++ b/src/installationmanager.cpp @@ -64,10 +64,6 @@ using namespace MOBase; using namespace MOShared; -typedef Archive* (*CreateArchiveType)(); - - - template static T resolveFunction(QLibrary &lib, const char *name) { @@ -116,7 +112,6 @@ InstallationManager::InstallationManager() InstallationManager::~InstallationManager() { - delete m_ArchiveHandler; } void InstallationManager::setParentWidget(QWidget *widget) @@ -238,7 +233,7 @@ QStringList InstallationManager::extractFiles(std::vectorisFile(); }); // Update the archive: - ArchiveFileTree::mapToArchive(m_ArchiveHandler, files); + ArchiveFileTree::mapToArchive(*m_ArchiveHandler, files); // Retrieve the file path: QStringList result; @@ -663,7 +658,7 @@ IPluginInstaller::EInstallResult InstallationManager::install(const QString &fil ON_BLOCK_EXIT(std::bind(&InstallationManager::postInstallCleanup, this)); std::shared_ptr filesTree = - archiveOpen ? ArchiveFileTree::makeTree(m_ArchiveHandler) : nullptr; + archiveOpen ? ArchiveFileTree::makeTree(*m_ArchiveHandler) : nullptr; IPluginInstaller::EInstallResult installResult = IPluginInstaller::RESULT_NOTATTEMPTED; std::sort(m_Installers.begin(), m_Installers.end(), [] (IPluginInstaller *LHS, IPluginInstaller *RHS) { @@ -707,7 +702,7 @@ IPluginInstaller::EInstallResult InstallationManager::install(const QString &fil // stops at this root): p->detach(); - p->mapToArchive(m_ArchiveHandler); + p->mapToArchive(*m_ArchiveHandler); // Clean the created files: cleanCreatedFiles(filesTree); diff --git a/src/installationmanager.h b/src/installationmanager.h index 328272e1..1b0c6d73 100644 --- a/src/installationmanager.h +++ b/src/installationmanager.h @@ -256,10 +256,9 @@ private: std::vector m_Installers; std::set m_SupportedExtensions; - Archive *m_ArchiveHandler; + // Archive management: + std::unique_ptr m_ArchiveHandler; QString m_CurrentFile; - - // Current password: QString m_Password; // Map from entries in the tree that is used by the installer and absolute diff --git a/src/selfupdater.cpp b/src/selfupdater.cpp index 501c7ed1..dfe11880 100644 --- a/src/selfupdater.cpp +++ b/src/selfupdater.cpp @@ -71,45 +71,18 @@ along with Mod Organizer. If not, see . using namespace MOBase; using namespace MOShared; - -typedef Archive* (*CreateArchiveType)(); - - -template static T resolveFunction(QLibrary &lib, const char *name) -{ - T temp = reinterpret_cast(lib.resolve(name)); - if (temp == nullptr) { - throw std::runtime_error(QObject::tr("invalid 7-zip32.dll: %1").arg(lib.errorString()).toLatin1().constData()); - } - return temp; -} - - SelfUpdater::SelfUpdater(NexusInterface *nexusInterface) : m_Parent(nullptr) , m_Interface(nexusInterface) , m_Reply(nullptr) , m_Attempts(3) { - QLibrary archiveLib(QCoreApplication::applicationDirPath() + "\\dlls\\archive.dll"); - if (!archiveLib.load()) { - throw MyException(tr("archive.dll not loaded: \"%1\"").arg(archiveLib.errorString())); - } - - CreateArchiveType CreateArchiveFunc = resolveFunction(archiveLib, "CreateArchive"); - - m_ArchiveHandler = CreateArchiveFunc(); - if (!m_ArchiveHandler->isValid()) { - throw MyException(InstallationManager::getErrorString(m_ArchiveHandler->getLastError())); - } - m_MOVersion = createVersionInfo(); } SelfUpdater::~SelfUpdater() { - delete m_ArchiveHandler; } void SelfUpdater::setUserInterface(QWidget *widget) diff --git a/src/selfupdater.h b/src/selfupdater.h index 0c81efc5..8ff14326 100644 --- a/src/selfupdater.h +++ b/src/selfupdater.h @@ -140,8 +140,6 @@ private: bool m_Canceled; int m_Attempts; - Archive *m_ArchiveHandler; - GitHub m_GitHub; QJsonObject m_UpdateCandidate; -- cgit v1.3.1 From b7a559c75c2f00616690714b5b55c4a9edac7d58 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Tue, 2 Jun 2020 23:30:03 +0200 Subject: Update following archive callback changes. --- src/installationmanager.cpp | 16 +++++++++++----- src/selfupdater.cpp | 3 --- 2 files changed, 11 insertions(+), 8 deletions(-) (limited to 'src/installationmanager.cpp') diff --git a/src/installationmanager.cpp b/src/installationmanager.cpp index dad5b436..f4b19c71 100644 --- a/src/installationmanager.cpp +++ b/src/installationmanager.cpp @@ -86,7 +86,7 @@ InstallationManager::InstallationManager() throw MyException(getErrorString(m_ArchiveHandler->getLastError())); } m_ArchiveHandler->setLogCallback([](auto level, auto const& message) { - using LogLevel = ArchiveCallbacks::LogLevel; + using LogLevel = Archive::LogLevel; switch (level) { case LogLevel::Debug: log::debug("{}", message); @@ -165,11 +165,17 @@ bool InstallationManager::extractFiles(QString extractPath, QString title, bool QString errorMessage; // The callbacks: - ArchiveCallbacks::ProgressCallback progressCallback = [&progress](float p) { progress = static_cast(p * 100.0); }; - ArchiveCallbacks::FileChangeCallback fileChangeCallback = [&progressFile](std::wstring const& file) { - progressFile = QString::fromStdWString(file); + auto progressCallback = [&progress](auto progressType, uint64_t current, uint64_t total) { + if (progressType == Archive::ProgressType::EXTRACTION) { + progress = static_cast(100 * current / total); + } + }; + Archive::FileChangeCallback fileChangeCallback = [&progressFile](auto changeType, std::wstring const& file) { + if (changeType == Archive::FileChangeType::EXTRACTION_START) { + progressFile = QString::fromStdWString(file); + } }; - ArchiveCallbacks::ErrorCallback errorCallback = [&errorMessage, this](std::wstring const& message) { + auto errorCallback = [&errorMessage, this](std::wstring const& message) { m_ArchiveHandler->cancel(); errorMessage = QString::fromStdWString(message); }; diff --git a/src/selfupdater.cpp b/src/selfupdater.cpp index dfe11880..1f56fd62 100644 --- a/src/selfupdater.cpp +++ b/src/selfupdater.cpp @@ -19,10 +19,7 @@ along with Mod Organizer. If not, see . #include "selfupdater.h" -#include "archive.h" -#include "callback.h" #include "utility.h" -#include "installationmanager.h" #include "iplugingame.h" #include "messagedialog.h" #include "downloadmanager.h" -- cgit v1.3.1 From e49ce3ed21b10b9af21caaabd8c2f1339fe59076 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Fri, 5 Jun 2020 20:36:35 +0200 Subject: Use QEventLoop to process events when extracting instead of manual loop with processEvents. --- src/installationmanager.cpp | 45 +++++++++++++++++++++++---------------------- src/installationmanager.h | 13 +++++++++++++ 2 files changed, 36 insertions(+), 22 deletions(-) (limited to 'src/installationmanager.cpp') diff --git a/src/installationmanager.cpp b/src/installationmanager.cpp index f4b19c71..7f4ad864 100644 --- a/src/installationmanager.cpp +++ b/src/installationmanager.cpp @@ -155,24 +155,28 @@ bool InstallationManager::extractFiles(QString extractPath, QString title, bool // extracted... installationProgress->setAutoReset(false); - installationProgress->show(); + // Connect signals emitted by the extraction callback to the progress dialog slots: + connect(this, &InstallationManager::progressUpdate, installationProgress, &QProgressDialog::setValue); + connect(this, &InstallationManager::progressFileChange, installationProgress, &QProgressDialog::setLabelText); - auto start = std::chrono::system_clock::now(); + // Cancelling progress only cancel the extraction, we do not force exiting the event-loop: + connect(installationProgress, &QProgressDialog::canceled, [this]() { m_ArchiveHandler->cancel(); }); + + installationProgress->show(); // Variable updated by the callbacks: - int progress = 0; - QString progressFile; QString errorMessage; // The callbacks: - auto progressCallback = [&progress](auto progressType, uint64_t current, uint64_t total) { + auto progressCallback = [this](auto progressType, uint64_t current, uint64_t total) { if (progressType == Archive::ProgressType::EXTRACTION) { - progress = static_cast(100 * current / total); + int progress = static_cast(100 * current / total); + emit progressUpdate(progress); } }; - Archive::FileChangeCallback fileChangeCallback = [&progressFile](auto changeType, std::wstring const& file) { + Archive::FileChangeCallback fileChangeCallback = [this](auto changeType, std::wstring const& file) { if (changeType == Archive::FileChangeType::EXTRACTION_START) { - progressFile = QString::fromStdWString(file); + emit progressFileChange(QString::fromStdWString(file)); } }; auto errorCallback = [&errorMessage, this](std::wstring const& message) { @@ -180,6 +184,8 @@ bool InstallationManager::extractFiles(QString extractPath, QString title, bool errorMessage = QString::fromStdWString(message); }; + auto start = std::chrono::system_clock::now(); + // unpack only the files we need for the installer QFuture future = QtConcurrent::run([&]() -> bool { return m_ArchiveHandler->extract( @@ -190,20 +196,15 @@ bool InstallationManager::extractFiles(QString extractPath, QString title, bool ); }); - // Wait for the extraction to complete (or be cancelled): - do { - if (installationProgress->wasCanceled()) { - m_ArchiveHandler->cancel(); - installationProgress->reset(); - } - if (progress != installationProgress->value()) { - installationProgress->setValue(progress); - } - if (showFilenames && progressFile != installationProgress->labelText()) { - installationProgress->setLabelText(progressFile); - } - QCoreApplication::processEvents(); - } while (!future.isFinished()); + // Future watcher to exit the loop: + QFutureWatcher futureWatcher; + futureWatcher.setFuture(future); + + QEventLoop loop(this); + connect(&futureWatcher, &QFutureWatcher::finished, &loop, &QEventLoop::quit); + + // Wait for future to complete: + loop.exec(); // Check the result: if (!future.result()) { diff --git a/src/installationmanager.h b/src/installationmanager.h index 1b0c6d73..1d94151f 100644 --- a/src/installationmanager.h +++ b/src/installationmanager.h @@ -213,8 +213,21 @@ private slots: signals: + /** + * @brief Emitted when a password is requested from the archive wrapper. + */ void passwordRequested(); + /** + * @brief Progress update from the extraction. + */ + void progressUpdate(int percentage); + + /** + * @brief File change update from the extraction. + */ + void progressFileChange(QString const& value); + private: struct ByPriority { -- cgit v1.3.1 From 0ad56591fd9581aa9195f854ac3fc1b40d5b1802 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Sat, 6 Jun 2020 13:40:38 +0200 Subject: Fix order of operations to avoid deadlock with event loop. --- src/installationmanager.cpp | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) (limited to 'src/installationmanager.cpp') diff --git a/src/installationmanager.cpp b/src/installationmanager.cpp index 7f4ad864..9d64a6f3 100644 --- a/src/installationmanager.cpp +++ b/src/installationmanager.cpp @@ -186,25 +186,28 @@ bool InstallationManager::extractFiles(QString extractPath, QString title, bool auto start = std::chrono::system_clock::now(); + // Future watcher to exit the loop: + QFutureWatcher futureWatcher; + + QEventLoop loop(this); + connect( + &futureWatcher, &QFutureWatcher::finished, + &loop, &QEventLoop::quit, + Qt::QueuedConnection); + // unpack only the files we need for the installer - QFuture future = QtConcurrent::run([&]() -> bool { + futureWatcher.setFuture(QtConcurrent::run([&]() -> bool { return m_ArchiveHandler->extract( extractPath.toStdWString(), progressCallback, showFilenames ? fileChangeCallback : nullptr, errorCallback ); - }); - - // Future watcher to exit the loop: - QFutureWatcher futureWatcher; - futureWatcher.setFuture(future); - - QEventLoop loop(this); - connect(&futureWatcher, &QFutureWatcher::finished, &loop, &QEventLoop::quit); + })); // Wait for future to complete: loop.exec(); + auto future = futureWatcher.future(); // Check the result: if (!future.result()) { -- cgit v1.3.1