Browse Source

begin tools impl; verify integrity + fw install

Signed-off-by: crueter <crueter@eden-emu.dev>
pull/3016/head
crueter 2 weeks ago
parent
commit
181736c22c
No known key found for this signature in database GPG Key ID: 425ACD2D4830EBC6
  1. 11
      src/Eden/Interface/CMakeLists.txt
  2. 16
      src/Eden/Interface/MainWindowInterface.cpp
  3. 13
      src/Eden/Interface/MainWindowInterface.h
  4. 16
      src/Eden/Main/Main.qml
  5. 5
      src/Eden/Native/EdenApplication.cpp
  6. 31
      src/Eden/Native/LibQtCommon.cpp
  7. 2
      src/qt_common/CMakeLists.txt
  8. 21
      src/qt_common/abstract/frontend.cpp
  9. 26
      src/qt_common/abstract/frontend.h
  10. 4
      src/qt_common/externals/cpmfile.json
  11. 65
      src/qt_common/util/content.cpp
  12. 5
      src/qt_common/util/content.h
  13. 23
      src/yuzu/libqt_common.cpp
  14. 55
      src/yuzu/main_window.cpp
  15. 2
      src/yuzu/main_window.h

11
src/Eden/Interface/CMakeLists.txt

@ -4,6 +4,8 @@
# SPDX-FileCopyrightText: Copyright 2025 crueter # SPDX-FileCopyrightText: Copyright 2025 crueter
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
find_package(Qt6 REQUIRED COMPONENTS Core)
EdenModule( EdenModule(
NAME Interface NAME Interface
URI Eden.Interface URI Eden.Interface
@ -17,6 +19,15 @@ EdenModule(
LIBRARIES LIBRARIES
Qt6::Quick Qt6::Quick
Qt6::Core Qt6::Core
Qt6::Widgets
Qt6::Concurrent
Vulkan::UtilityHeaders Vulkan::UtilityHeaders
frontend_common frontend_common
) )
target_link_libraries(EdenInterface PRIVATE Qt6::Core)
target_sources(EdenInterface
PRIVATE
MainWindowInterface.h MainWindowInterface.cpp
)

16
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();
}

13
src/Eden/Interface/MainWindowInterface.h

@ -0,0 +1,13 @@
#pragma once
#include <QObject>
class MainWindowInterface : public QObject {
Q_OBJECT
public:
explicit MainWindowInterface(QObject* parent = nullptr);
Q_INVOKABLE void installFirmware();
Q_INVOKABLE void installFirmwareZip();
Q_INVOKABLE void verifyIntegrity();
};

16
src/Eden/Main/Main.qml

@ -1,6 +1,5 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
@ -161,12 +160,23 @@ ApplicationWindow {
text: qsTr("Install &Decryption Keys") 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 { Action {
text: qsTr("&Verify Installed Contents") text: qsTr("&Verify Installed Contents")
onTriggered: MainWindowInterface.verifyIntegrity()
} }
MenuSeparator {} MenuSeparator {}

5
src/Eden/Native/EdenApplication.cpp

@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#include "Eden/Interface/MainWindowInterface.h"
#include "EdenApplication.h" #include "EdenApplication.h"
#include <QQmlApplicationEngine> #include <QQmlApplicationEngine>
@ -101,6 +102,10 @@ int EdenApplication::run() {
TitleManager *title = new TitleManager(&engine); TitleManager *title = new TitleManager(&engine);
ctx->setContextProperty(QStringLiteral("TitleManager"), title); ctx->setContextProperty(QStringLiteral("TitleManager"), title);
// MainWindow interface
MainWindowInterface* mwint = new MainWindowInterface(&engine);
ctx->setContextProperty(QStringLiteral("MainWindowInterface"), mwint);
// :) // :)
ctx->setContextProperty(QStringLiteral("EdenApplication"), this); ctx->setContextProperty(QStringLiteral("EdenApplication"), this);

31
src/Eden/Native/LibQtCommon.cpp

@ -18,33 +18,12 @@ StandardButton ShowMessage(Icon icon, const QString& title, const QString& text,
return StandardButton(res); 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), : 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 { bool QuickProgressDialog::wasCanceled() const {
return m_dialog->wasCanceled(); return m_dialog->wasCanceled();

2
src/qt_common/CMakeLists.txt

@ -26,6 +26,8 @@ add_library(qt_common STATIC
util/compress.h util/compress.cpp util/compress.h util/compress.cpp
abstract/frontend.h abstract/frontend.h
abstract/frontend.cpp
abstract/qt_progress_dialog.h abstract/qt_progress_dialog.h
abstract/qt_progress_dialog.cpp abstract/qt_progress_dialog.cpp

21
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

26
src/qt_common/abstract/frontend.h

@ -4,6 +4,7 @@
#ifndef FRONTEND_H #ifndef FRONTEND_H
#define FRONTEND_H #define FRONTEND_H
#include <QFileDialog>
#include <QGuiApplication> #include <QGuiApplication>
#include "qt_common/qt_common.h" #include "qt_common/qt_common.h"
@ -14,19 +15,6 @@ namespace QtCommon::Frontend {
Q_NAMESPACE 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 { enum StandardButton {
// keep this in sync with QDialogButtonBox::StandardButton and QPlatformDialogHelper::StandardButton // keep this in sync with QDialogButtonBox::StandardButton and QPlatformDialogHelper::StandardButton
NoButton = 0x00000000, NoButton = 0x00000000,
@ -118,21 +106,21 @@ UTIL_OVERRIDES(Warning)
UTIL_OVERRIDES(Critical) UTIL_OVERRIDES(Critical)
UTIL_OVERRIDES(Question) UTIL_OVERRIDES(Question)
const QString GetOpenFileName(const QString &title,
QString GetOpenFileName(const QString &title,
const QString &dir, const QString &dir,
const QString &filter, const QString &filter,
QString *selectedFilter = nullptr, 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 &dir,
const QString &filter, const QString &filter,
QString *selectedFilter = nullptr, 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(), const QString &dir = QString(),
Options options = Option::ShowDirsOnly);
QFileDialog::Options options = QFileDialog::ShowDirsOnly);
} // namespace QtCommon::Frontend } // namespace QtCommon::Frontend
#endif // FRONTEND_H #endif // FRONTEND_H

4
src/qt_common/externals/cpmfile.json

@ -19,8 +19,8 @@
"package": "Carboxyl", "package": "Carboxyl",
"repo": "crueter/Carboxyl", "repo": "crueter/Carboxyl",
"git_host": "git.crueter.xyz", "git_host": "git.crueter.xyz",
"sha": "ad4a5743dc",
"hash": "68f1da395c3628f8e82a8f160f5cd0e1d6503606f68d9fc51e0d8fde91514b178afb722fb498394b3ab17a2725f39af6cc48b5bd076d546bb9cfb6718f47345a",
"sha": "6c37572331",
"hash": "e2a7f14d6b6bc9285e1b525fee653a47bc0f971343addb1223873c11f683f0c9e360dedfa496c5074895f0788092ea401d42e7e61e98a244c38373de49a0f638",
"bundled": "true", "bundled": "true",
"options": [ "options": [
"CARBOXYL_DEMO OFF" "CARBOXYL_DEMO OFF"

65
src/qt_common/util/content.cpp

@ -19,6 +19,7 @@
#include <QFutureWatcher> #include <QFutureWatcher>
#include <QtConcurrentRun> #include <QtConcurrentRun>
#include <JlCompress.h> #include <JlCompress.h>
#include <qguiapplication.h>
namespace QtCommon::Content { namespace QtCommon::Content {
@ -46,8 +47,11 @@ void InstallFirmware(const QString& location, bool recursive)
tr("Cancel"), 0, 100); tr("Cancel"), 0, 100);
progress->show(); progress->show();
QGuiApplication::processEvents();
// Declare progress callback. // Declare progress callback.
auto callback = [&](size_t total_size, size_t processed_size) { auto callback = [&](size_t total_size, size_t processed_size) {
QGuiApplication::processEvents();
progress->setValue(static_cast<int>((processed_size * 100) / total_size)); progress->setValue(static_cast<int>((processed_size * 100) / total_size));
return progress->wasCanceled(); return progress->wasCanceled();
}; };
@ -219,7 +223,10 @@ void VerifyGameContents(const std::string& game_path)
QtCommon::Frontend::newProgressDialog(tr("Verifying integrity..."), tr("Cancel"), 0, 100); QtCommon::Frontend::newProgressDialog(tr("Verifying integrity..."), tr("Cancel"), 0, 100);
progress->show(); progress->show();
QGuiApplication::processEvents();
const auto callback = [&](size_t total_size, size_t processed_size) { const auto callback = [&](size_t total_size, size_t processed_size) {
QGuiApplication::processEvents();
progress->setValue(static_cast<int>((processed_size * 100) / total_size)); progress->setValue(static_cast<int>((processed_size * 100) / total_size));
return progress->wasCanceled(); return progress->wasCanceled();
}; };
@ -253,7 +260,7 @@ void InstallKeys()
{}, {},
QStringLiteral("Decryption Keys (*.keys)"), QStringLiteral("Decryption Keys (*.keys)"),
{}, {},
QtCommon::Frontend::Option::ReadOnly);
QFileDialog::ReadOnly);
if (key_source_location.isEmpty()) { if (key_source_location.isEmpty()) {
return; return;
@ -282,8 +289,11 @@ void VerifyInstalledContents()
tr("Cancel"), 0, 100); tr("Cancel"), 0, 100);
progress->show(); progress->show();
QGuiApplication::processEvents();
// Declare progress callback. // Declare progress callback.
auto QtProgressCallback = [&](size_t total_size, size_t processed_size) { auto QtProgressCallback = [&](size_t total_size, size_t processed_size) {
QGuiApplication::processEvents();
progress->setValue(static_cast<int>((processed_size * 100) / total_size)); progress->setValue(static_cast<int>((processed_size * 100) / total_size));
return progress->wasCanceled(); 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 } // namespace QtCommon::Content

5
src/qt_common/util/content.h

@ -39,9 +39,12 @@ inline const QString GetKeyInstallResultString(FirmwareManager::KeyInstallResult
} }
void InstallFirmware(const QString &location, bool recursive); void InstallFirmware(const QString &location, bool recursive);
QString UnzipFirmwareToTmp(const QString &location); QString UnzipFirmwareToTmp(const QString &location);
bool CheckKeys();
void InstallFirmware();
void InstallFirmwareZip();
// Keys // // Keys //
void InstallKeys(); void InstallKeys();

23
src/yuzu/libqt_common.cpp

@ -20,29 +20,6 @@ StandardButton ShowMessage(
return StandardButton(box->exec()); 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, WidgetsProgressDialog::WidgetsProgressDialog(const QString& labelText,
const QString& cancelButtonText, int minimum, const QString& cancelButtonText, int minimum,
int maximum, QWidget* parent, Qt::WindowFlags f) int maximum, QWidget* parent, Qt::WindowFlags f)

55
src/yuzu/main_window.cpp

@ -3663,32 +3663,14 @@ void MainWindow::OnVerifyInstalledContents() {
QtCommon::Content::VerifyInstalledContents(); QtCommon::Content::VerifyInstalledContents();
} }
void MainWindow::InstallFirmware(const QString& location, bool recursive) {
QtCommon::Content::InstallFirmware(location, recursive);
OnCheckFirmwareDecryption();
}
void MainWindow::OnInstallFirmware() { void MainWindow::OnInstallFirmware() {
// Don't do this while emulation is running, that'd probably be a bad idea. // Don't do this while emulation is running, that'd probably be a bad idea.
if (emu_thread != nullptr && emu_thread->IsRunning()) { if (emu_thread != nullptr && emu_thread->IsRunning()) {
return; 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() { void MainWindow::OnInstallFirmwareFromZIP() {
@ -3697,37 +3679,8 @@ void MainWindow::OnInstallFirmwareFromZIP() {
return; 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() { void MainWindow::OnInstallDecryptionKeys() {

2
src/yuzu/main_window.h

@ -579,8 +579,6 @@ private:
std::string arguments, std::string arguments,
const bool needs_title); const bool needs_title);
void InstallFirmware(const QString& location, bool recursive = false);
protected: protected:
void dropEvent(QDropEvent* event) override; void dropEvent(QDropEvent* event) override;
void dragEnterEvent(QDragEnterEvent* event) override; void dragEnterEvent(QDragEnterEvent* event) override;

Loading…
Cancel
Save