From 181736c22c26edc427f335291dea6c18f08e925e Mon Sep 17 00:00:00 2001 From: crueter Date: Tue, 9 Dec 2025 00:48:00 -0500 Subject: [PATCH] begin tools impl; verify integrity + fw install Signed-off-by: crueter --- src/Eden/Interface/CMakeLists.txt | 11 ++++ src/Eden/Interface/MainWindowInterface.cpp | 16 ++++++ src/Eden/Interface/MainWindowInterface.h | 13 +++++ src/Eden/Main/Main.qml | 16 +++++- src/Eden/Native/EdenApplication.cpp | 5 ++ src/Eden/Native/LibQtCommon.cpp | 31 ++--------- src/qt_common/CMakeLists.txt | 2 + src/qt_common/abstract/frontend.cpp | 21 +++++++ src/qt_common/abstract/frontend.h | 26 +++------ src/qt_common/externals/cpmfile.json | 4 +- src/qt_common/util/content.cpp | 65 +++++++++++++++++++++- src/qt_common/util/content.h | 5 +- src/yuzu/libqt_common.cpp | 23 -------- src/yuzu/main_window.cpp | 55 ++---------------- src/yuzu/main_window.h | 2 - 15 files changed, 167 insertions(+), 128 deletions(-) create mode 100644 src/Eden/Interface/MainWindowInterface.cpp create mode 100644 src/Eden/Interface/MainWindowInterface.h create mode 100644 src/qt_common/abstract/frontend.cpp diff --git a/src/Eden/Interface/CMakeLists.txt b/src/Eden/Interface/CMakeLists.txt index 4c4c6b3659..ad6e98f118 100644 --- a/src/Eden/Interface/CMakeLists.txt +++ b/src/Eden/Interface/CMakeLists.txt @@ -4,6 +4,8 @@ # SPDX-FileCopyrightText: Copyright 2025 crueter # SPDX-License-Identifier: GPL-3.0-or-later +find_package(Qt6 REQUIRED COMPONENTS Core) + EdenModule( NAME Interface URI Eden.Interface @@ -17,6 +19,15 @@ EdenModule( LIBRARIES Qt6::Quick Qt6::Core + Qt6::Widgets + Qt6::Concurrent Vulkan::UtilityHeaders frontend_common ) + +target_link_libraries(EdenInterface PRIVATE Qt6::Core) + +target_sources(EdenInterface + PRIVATE + MainWindowInterface.h MainWindowInterface.cpp +) diff --git a/src/Eden/Interface/MainWindowInterface.cpp b/src/Eden/Interface/MainWindowInterface.cpp new file mode 100644 index 0000000000..acf04d3080 --- /dev/null +++ b/src/Eden/Interface/MainWindowInterface.cpp @@ -0,0 +1,16 @@ +#include "MainWindowInterface.h" +#include "qt_common/util/content.h" + +MainWindowInterface::MainWindowInterface(QObject* parent) : QObject{parent} {} + +void MainWindowInterface::installFirmware() { + QtCommon::Content::InstallFirmware(); +} + +void MainWindowInterface::installFirmwareZip() { + QtCommon::Content::InstallFirmwareZip(); +} + +void MainWindowInterface::verifyIntegrity() { + QtCommon::Content::VerifyInstalledContents(); +} diff --git a/src/Eden/Interface/MainWindowInterface.h b/src/Eden/Interface/MainWindowInterface.h new file mode 100644 index 0000000000..f64c716f16 --- /dev/null +++ b/src/Eden/Interface/MainWindowInterface.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +class MainWindowInterface : public QObject { + Q_OBJECT +public: + explicit MainWindowInterface(QObject* parent = nullptr); + + Q_INVOKABLE void installFirmware(); + Q_INVOKABLE void installFirmwareZip(); + Q_INVOKABLE void verifyIntegrity(); +}; diff --git a/src/Eden/Main/Main.qml b/src/Eden/Main/Main.qml index a8e1702396..fdd57276e3 100644 --- a/src/Eden/Main/Main.qml +++ b/src/Eden/Main/Main.qml @@ -1,6 +1,5 @@ // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later - import QtQuick import QtQuick.Controls @@ -161,12 +160,23 @@ ApplicationWindow { text: qsTr("Install &Decryption Keys") } - Action { - text: qsTr("Install &Firmware") + Menu { + title: qsTr("Install &Firmware") + + Action { + text: qsTr("From &Folder") + onTriggered: MainWindowInterface.installFirmware() + } + + Action { + text: qsTr("From &ZIP") + onTriggered: MainWindowInterface.installFirmwareZip() + } } Action { text: qsTr("&Verify Installed Contents") + onTriggered: MainWindowInterface.verifyIntegrity() } MenuSeparator {} diff --git a/src/Eden/Native/EdenApplication.cpp b/src/Eden/Native/EdenApplication.cpp index cbabd01d63..6541a508cf 100644 --- a/src/Eden/Native/EdenApplication.cpp +++ b/src/Eden/Native/EdenApplication.cpp @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later +#include "Eden/Interface/MainWindowInterface.h" #include "EdenApplication.h" #include @@ -101,6 +102,10 @@ int EdenApplication::run() { TitleManager *title = new TitleManager(&engine); ctx->setContextProperty(QStringLiteral("TitleManager"), title); + // MainWindow interface + MainWindowInterface* mwint = new MainWindowInterface(&engine); + ctx->setContextProperty(QStringLiteral("MainWindowInterface"), mwint); + // :) ctx->setContextProperty(QStringLiteral("EdenApplication"), this); diff --git a/src/Eden/Native/LibQtCommon.cpp b/src/Eden/Native/LibQtCommon.cpp index 62f609cbf5..f164341ed3 100644 --- a/src/Eden/Native/LibQtCommon.cpp +++ b/src/Eden/Native/LibQtCommon.cpp @@ -18,33 +18,12 @@ StandardButton ShowMessage(Icon icon, const QString& title, const QString& text, return StandardButton(res); } -const QString GetOpenFileName(const QString& title, const QString& dir, const QString& filter, - QString* selectedFilter, Options options) { - // TODO - // return QFileDialog::getOpenFileName((QWidget *) rootObject, title, dir, filter, - // selectedFilter, QFileDialog::Options(int(options))); - return QString(); -} - -const QString GetSaveFileName(const QString& title, const QString& dir, const QString& filter, - QString* selectedFilter, Options options) { - // return QFileDialog::getSaveFileName((QWidget *) rootObject, title, dir, filter, - // selectedFilter, QFileDialog::Options(int(options))); - return QString(); -} - - -const QString GetExistingDirectory(const QString& caption, const QString& dir, Options options) { - // return QFileDialog::getExistingDirectory((QWidget *) rootObject, caption, dir, - // QFileDialog::Options(int(options))); - return QString(); -} - -QuickProgressDialog::QuickProgressDialog(const QString& labelText, - const QString& cancelButtonText, int minimum, - int maximum, QObject* parent, Qt::WindowFlags f) +QuickProgressDialog::QuickProgressDialog(const QString& labelText, const QString& cancelButtonText, + int minimum, int maximum, QObject* parent, + Qt::WindowFlags f) : QtProgressDialog(labelText, cancelButtonText, minimum, maximum, parent, f), - m_dialog(new CarboxylProgressDialog(labelText, cancelButtonText, minimum, maximum, parent)) {} + m_dialog(new CarboxylProgressDialog(labelText, cancelButtonText, minimum, maximum, parent)) { +} bool QuickProgressDialog::wasCanceled() const { return m_dialog->wasCanceled(); diff --git a/src/qt_common/CMakeLists.txt b/src/qt_common/CMakeLists.txt index 4c7b86fc0b..3d77b756b6 100644 --- a/src/qt_common/CMakeLists.txt +++ b/src/qt_common/CMakeLists.txt @@ -26,6 +26,8 @@ add_library(qt_common STATIC util/compress.h util/compress.cpp abstract/frontend.h + abstract/frontend.cpp + abstract/qt_progress_dialog.h abstract/qt_progress_dialog.cpp diff --git a/src/qt_common/abstract/frontend.cpp b/src/qt_common/abstract/frontend.cpp new file mode 100644 index 0000000000..5d23e41865 --- /dev/null +++ b/src/qt_common/abstract/frontend.cpp @@ -0,0 +1,21 @@ +#include "frontend.h" + +namespace QtCommon::Frontend { +QString GetOpenFileName(const QString& title, const QString& dir, const QString& filter, + QString* selectedFilter, QFileDialog::Options options) { + return QFileDialog::getOpenFileName((QWidget*)rootObject, title, dir, filter, selectedFilter, + QFileDialog::Options(int(options))); +} + +QString GetSaveFileName(const QString& title, const QString& dir, const QString& filter, + QString* selectedFilter, QFileDialog::Options options) { + return QFileDialog::getSaveFileName((QWidget*)rootObject, title, dir, filter, selectedFilter, + QFileDialog::Options(int(options))); +} + +QString GetExistingDirectory(const QString& caption, const QString& dir, + QFileDialog::Options options) { + return QFileDialog::getExistingDirectory((QWidget*)rootObject, caption, dir, + QFileDialog::Options(int(options))); +} +} // namespace QtCommon::Frontend diff --git a/src/qt_common/abstract/frontend.h b/src/qt_common/abstract/frontend.h index d09433af84..1db1788fb7 100644 --- a/src/qt_common/abstract/frontend.h +++ b/src/qt_common/abstract/frontend.h @@ -4,6 +4,7 @@ #ifndef FRONTEND_H #define FRONTEND_H +#include #include #include "qt_common/qt_common.h" @@ -14,19 +15,6 @@ namespace QtCommon::Frontend { Q_NAMESPACE -enum Option { - ShowDirsOnly = 0x00000001, - DontResolveSymlinks = 0x00000002, - DontConfirmOverwrite = 0x00000004, - DontUseNativeDialog = 0x00000008, - ReadOnly = 0x00000010, - HideNameFilterDetails = 0x00000020, - DontUseCustomDirectoryIcons = 0x00000040 -}; -Q_ENUM_NS(Option) -Q_DECLARE_FLAGS(Options, Option) -Q_FLAG_NS(Options) - enum StandardButton { // keep this in sync with QDialogButtonBox::StandardButton and QPlatformDialogHelper::StandardButton NoButton = 0x00000000, @@ -118,21 +106,21 @@ UTIL_OVERRIDES(Warning) UTIL_OVERRIDES(Critical) UTIL_OVERRIDES(Question) -const QString GetOpenFileName(const QString &title, +QString GetOpenFileName(const QString &title, const QString &dir, const QString &filter, QString *selectedFilter = nullptr, - Options options = Options()); + QFileDialog::Options options = QFileDialog::Options()); -const QString GetSaveFileName(const QString &title, +QString GetSaveFileName(const QString &title, const QString &dir, const QString &filter, QString *selectedFilter = nullptr, - Options options = Options()); + QFileDialog::Options options = QFileDialog::Options()); -const QString GetExistingDirectory(const QString &caption = QString(), +QString GetExistingDirectory(const QString &caption = QString(), const QString &dir = QString(), - Options options = Option::ShowDirsOnly); + QFileDialog::Options options = QFileDialog::ShowDirsOnly); } // namespace QtCommon::Frontend #endif // FRONTEND_H diff --git a/src/qt_common/externals/cpmfile.json b/src/qt_common/externals/cpmfile.json index 820ec3fb46..11fb5b9620 100644 --- a/src/qt_common/externals/cpmfile.json +++ b/src/qt_common/externals/cpmfile.json @@ -19,8 +19,8 @@ "package": "Carboxyl", "repo": "crueter/Carboxyl", "git_host": "git.crueter.xyz", - "sha": "ad4a5743dc", - "hash": "68f1da395c3628f8e82a8f160f5cd0e1d6503606f68d9fc51e0d8fde91514b178afb722fb498394b3ab17a2725f39af6cc48b5bd076d546bb9cfb6718f47345a", + "sha": "6c37572331", + "hash": "e2a7f14d6b6bc9285e1b525fee653a47bc0f971343addb1223873c11f683f0c9e360dedfa496c5074895f0788092ea401d42e7e61e98a244c38373de49a0f638", "bundled": "true", "options": [ "CARBOXYL_DEMO OFF" diff --git a/src/qt_common/util/content.cpp b/src/qt_common/util/content.cpp index 2abd0d5c6b..5f764f52a2 100644 --- a/src/qt_common/util/content.cpp +++ b/src/qt_common/util/content.cpp @@ -19,6 +19,7 @@ #include #include #include +#include namespace QtCommon::Content { @@ -46,8 +47,11 @@ void InstallFirmware(const QString& location, bool recursive) tr("Cancel"), 0, 100); progress->show(); + QGuiApplication::processEvents(); + // Declare progress callback. auto callback = [&](size_t total_size, size_t processed_size) { + QGuiApplication::processEvents(); progress->setValue(static_cast((processed_size * 100) / total_size)); return progress->wasCanceled(); }; @@ -219,7 +223,10 @@ void VerifyGameContents(const std::string& game_path) QtCommon::Frontend::newProgressDialog(tr("Verifying integrity..."), tr("Cancel"), 0, 100); progress->show(); + QGuiApplication::processEvents(); + const auto callback = [&](size_t total_size, size_t processed_size) { + QGuiApplication::processEvents(); progress->setValue(static_cast((processed_size * 100) / total_size)); return progress->wasCanceled(); }; @@ -253,7 +260,7 @@ void InstallKeys() {}, QStringLiteral("Decryption Keys (*.keys)"), {}, - QtCommon::Frontend::Option::ReadOnly); + QFileDialog::ReadOnly); if (key_source_location.isEmpty()) { return; @@ -282,8 +289,11 @@ void VerifyInstalledContents() tr("Cancel"), 0, 100); progress->show(); + QGuiApplication::processEvents(); + // Declare progress callback. auto QtProgressCallback = [&](size_t total_size, size_t processed_size) { + QGuiApplication::processEvents(); progress->setValue(static_cast((processed_size * 100) / total_size)); return progress->wasCanceled(); }; @@ -524,4 +534,57 @@ void ImportDataDir(FrontendCommon::DataManager::DataDir data_dir, }); } +bool CheckKeys() { + if (!ContentManager::AreKeysPresent()) { + QtCommon::Frontend::Information( + tr("Keys not installed"), + tr("Install decryption keys and restart Eden before attempting to install firmware.")); + return false; + } + + return true; +} + +void InstallFirmware() { + if (!CheckKeys()) + return; + + const QString firmware_source_location = QtCommon::Frontend::GetExistingDirectory( + tr("Select Dumped Firmware Source Location"), {}, QFileDialog::ShowDirsOnly); + + if (!firmware_source_location.isEmpty()) + QtCommon::Content::InstallFirmware(firmware_source_location, false); +} + +void InstallFirmwareZip() { + if (!CheckKeys()) + return; + + const QString firmware_zip_location = QtCommon::Frontend::GetOpenFileName( + tr("Select Dumped Firmware ZIP"), {}, tr("Zipped Archives (*.zip)")); + + if (firmware_zip_location.isEmpty()) + return; + + const QString qCacheDir = QtCommon::Content::UnzipFirmwareToTmp(firmware_zip_location); + + // In this case, it has to be done recursively, since sometimes people + // will pack it into a subdirectory after dumping + if (!qCacheDir.isEmpty()) { + QtCommon::Content::InstallFirmware(qCacheDir, true); + std::error_code ec; + std::filesystem::remove_all(std::filesystem::temp_directory_path() / "eden" / "firmware", + ec); + + if (ec) { + QtCommon::Frontend::Warning( + tr("Firmware cleanup failed"), + tr("Failed to clean up extracted firmware cache.\n" + "Check write permissions in the system temp directory and try " + "again.\nOS reported error: %1") + .arg(QString::fromStdString(ec.message()))); + } + } +} + } // namespace QtCommon::Content diff --git a/src/qt_common/util/content.h b/src/qt_common/util/content.h index 6e8642083f..d09de6af65 100644 --- a/src/qt_common/util/content.h +++ b/src/qt_common/util/content.h @@ -39,9 +39,12 @@ inline const QString GetKeyInstallResultString(FirmwareManager::KeyInstallResult } void InstallFirmware(const QString &location, bool recursive); - QString UnzipFirmwareToTmp(const QString &location); +bool CheckKeys(); +void InstallFirmware(); +void InstallFirmwareZip(); + // Keys // void InstallKeys(); diff --git a/src/yuzu/libqt_common.cpp b/src/yuzu/libqt_common.cpp index 825084d915..81d4d81855 100644 --- a/src/yuzu/libqt_common.cpp +++ b/src/yuzu/libqt_common.cpp @@ -20,29 +20,6 @@ StandardButton ShowMessage( return StandardButton(box->exec()); } -const QString GetOpenFileName(const QString &title, - const QString &dir, - const QString &filter, - QString *selectedFilter, - Options options) -{ - return QFileDialog::getOpenFileName((QWidget *) rootObject, title, dir, filter, selectedFilter, QFileDialog::Options(int(options))); -} - -const QString GetSaveFileName(const QString &title, - const QString &dir, - const QString &filter, - QString *selectedFilter, - Options options) -{ - return QFileDialog::getSaveFileName((QWidget *) rootObject, title, dir, filter, selectedFilter, QFileDialog::Options(int(options))); -} - -const QString GetExistingDirectory(const QString& caption, const QString& dir, - Options options) { - return QFileDialog::getExistingDirectory((QWidget *) rootObject, caption, dir, QFileDialog::Options(int(options))); -} - WidgetsProgressDialog::WidgetsProgressDialog(const QString& labelText, const QString& cancelButtonText, int minimum, int maximum, QWidget* parent, Qt::WindowFlags f) diff --git a/src/yuzu/main_window.cpp b/src/yuzu/main_window.cpp index 01cf1ff854..9d5b6e8ce0 100644 --- a/src/yuzu/main_window.cpp +++ b/src/yuzu/main_window.cpp @@ -3663,32 +3663,14 @@ void MainWindow::OnVerifyInstalledContents() { QtCommon::Content::VerifyInstalledContents(); } -void MainWindow::InstallFirmware(const QString& location, bool recursive) { - QtCommon::Content::InstallFirmware(location, recursive); - OnCheckFirmwareDecryption(); -} - void MainWindow::OnInstallFirmware() { // Don't do this while emulation is running, that'd probably be a bad idea. if (emu_thread != nullptr && emu_thread->IsRunning()) { return; } - // Check for installed keys, error out, suggest restart? - if (!ContentManager::AreKeysPresent()) { - QMessageBox::information( - this, tr("Keys not installed"), - tr("Install decryption keys and restart Eden before attempting to install firmware.")); - return; - } - - const QString firmware_source_location = QFileDialog::getExistingDirectory( - this, tr("Select Dumped Firmware Source Location"), {}, QFileDialog::ShowDirsOnly); - if (firmware_source_location.isEmpty()) { - return; - } - - InstallFirmware(firmware_source_location); + QtCommon::Content::InstallFirmware(); + OnCheckFirmwareDecryption(); } void MainWindow::OnInstallFirmwareFromZIP() { @@ -3697,37 +3679,8 @@ void MainWindow::OnInstallFirmwareFromZIP() { return; } - // Check for installed keys, error out, suggest restart? - if (!ContentManager::AreKeysPresent()) { - QMessageBox::information( - this, tr("Keys not installed"), - tr("Install decryption keys and restart Eden before attempting to install firmware.")); - return; - } - - const QString firmware_zip_location = QFileDialog::getOpenFileName( - this, tr("Select Dumped Firmware ZIP"), {}, tr("Zipped Archives (*.zip)")); - if (firmware_zip_location.isEmpty()) { - return; - } - - const QString qCacheDir = QtCommon::Content::UnzipFirmwareToTmp(firmware_zip_location); - - // In this case, it has to be done recursively, since sometimes people - // will pack it into a subdirectory after dumping - if (!qCacheDir.isEmpty()) { - InstallFirmware(qCacheDir, true); - std::error_code ec; - std::filesystem::remove_all(std::filesystem::temp_directory_path() / "eden" / "firmware", ec); - - if (ec) { - QMessageBox::warning(this, tr("Firmware cleanup failed"), - tr("Failed to clean up extracted firmware cache.\n" - "Check write permissions in the system temp directory and try " - "again.\nOS reported error: %1") - .arg(QString::fromStdString(ec.message()))); - } - } + QtCommon::Content::InstallFirmwareZip(); + OnCheckFirmwareDecryption(); } void MainWindow::OnInstallDecryptionKeys() { diff --git a/src/yuzu/main_window.h b/src/yuzu/main_window.h index bbd76e847f..f7f860533a 100644 --- a/src/yuzu/main_window.h +++ b/src/yuzu/main_window.h @@ -579,8 +579,6 @@ private: std::string arguments, const bool needs_title); - void InstallFirmware(const QString& location, bool recursive = false); - protected: void dropEvent(QDropEvent* event) override; void dragEnterEvent(QDragEnterEvent* event) override;