QProgressDialog abstractor, more moving
All checks were successful
eden-license / license-header (pull_request) Successful in 34s

Signed-off-by: crueter <crueter@eden-emu.dev>
This commit is contained in:
crueter 2025-09-13 11:39:10 -04:00
parent 2d94767f96
commit 8974aed013
Signed by: crueter
GPG key ID: 425ACD2D4830EBC6
27 changed files with 580 additions and 526 deletions

View file

@ -15,7 +15,6 @@ FILES=`git diff --name-only $BASE`
#FILES=$(git diff --name-only master) #FILES=$(git diff --name-only master)
echo $FILES
echo "Done" echo "Done"
check_header() { check_header() {

View file

@ -232,8 +232,6 @@ class GameAdapter(private val activity: AppCompatActivity) :
binding.root.findNavController().navigate(action) binding.root.findNavController().navigate(action)
} }
val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
if (NativeLibrary.gameRequiresFirmware(game.programId) && !NativeLibrary.isFirmwareAvailable()) { if (NativeLibrary.gameRequiresFirmware(game.programId) && !NativeLibrary.isFirmwareAvailable()) {
MaterialAlertDialogBuilder(activity) MaterialAlertDialogBuilder(activity)
.setTitle(R.string.loader_requires_firmware) .setTitle(R.string.loader_requires_firmware)
@ -248,23 +246,6 @@ class GameAdapter(private val activity: AppCompatActivity) :
} }
.setNegativeButton(android.R.string.cancel) { _, _ -> } .setNegativeButton(android.R.string.cancel) { _, _ -> }
.show() .show()
} else if (BooleanSetting.DISABLE_NCA_VERIFICATION.getBoolean(false) && !preferences.getBoolean(
Settings.PREF_HIDE_NCA_POPUP, false)) {
MaterialAlertDialogBuilder(activity)
.setTitle(R.string.nca_verification_disabled)
.setMessage(activity.getString(R.string.nca_verification_disabled_description))
.setPositiveButton(android.R.string.ok) { _, _ ->
launch()
}
.setNeutralButton(R.string.dont_show_again) { _, _ ->
preferences.edit {
putBoolean(Settings.PREF_HIDE_NCA_POPUP, true)
}
launch()
}
.setNegativeButton(android.R.string.cancel) { _, _ -> }
.show()
} else { } else {
launch() launch()
} }

View file

@ -35,7 +35,6 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
RENDERER_SAMPLE_SHADING("sample_shading"), RENDERER_SAMPLE_SHADING("sample_shading"),
PICTURE_IN_PICTURE("picture_in_picture"), PICTURE_IN_PICTURE("picture_in_picture"),
USE_CUSTOM_RTC("custom_rtc_enabled"), USE_CUSTOM_RTC("custom_rtc_enabled"),
DISABLE_NCA_VERIFICATION("disable_nca_verification"),
BLACK_BACKGROUNDS("black_backgrounds"), BLACK_BACKGROUNDS("black_backgrounds"),
JOYSTICK_REL_CENTER("joystick_rel_center"), JOYSTICK_REL_CENTER("joystick_rel_center"),
DPAD_SLIDE("dpad_slide"), DPAD_SLIDE("dpad_slide"),

View file

@ -297,13 +297,6 @@ abstract class SettingsItem(
descriptionId = R.string.use_custom_rtc_description descriptionId = R.string.use_custom_rtc_description
) )
) )
put(
SwitchSetting(
BooleanSetting.DISABLE_NCA_VERIFICATION,
titleId = R.string.disable_nca_verification,
descriptionId = R.string.disable_nca_verification_description
)
)
put( put(
StringInputSetting( StringInputSetting(
StringSetting.WEB_TOKEN, StringSetting.WEB_TOKEN,

View file

@ -210,7 +210,6 @@ class SettingsFragmentPresenter(
add(IntSetting.LANGUAGE_INDEX.key) add(IntSetting.LANGUAGE_INDEX.key)
add(BooleanSetting.USE_CUSTOM_RTC.key) add(BooleanSetting.USE_CUSTOM_RTC.key)
add(LongSetting.CUSTOM_RTC.key) add(LongSetting.CUSTOM_RTC.key)
add(BooleanSetting.DISABLE_NCA_VERIFICATION.key)
add(HeaderSetting(R.string.network)) add(HeaderSetting(R.string.network))
add(StringSetting.WEB_TOKEN.key) add(StringSetting.WEB_TOKEN.key)

View file

@ -784,9 +784,6 @@
<string name="loader_requires_firmware">Game Requires Firmware</string> <string name="loader_requires_firmware">Game Requires Firmware</string>
<string name="loader_requires_firmware_description"><![CDATA[The game you are trying to launch requires firmware to boot or to get past the opening menu. Please <a href="https://yuzu-mirror.github.io/help/quickstart"> dump and install firmware</a>, or press "OK" to launch anyways.]]></string> <string name="loader_requires_firmware_description"><![CDATA[The game you are trying to launch requires firmware to boot or to get past the opening menu. Please <a href="https://yuzu-mirror.github.io/help/quickstart"> dump and install firmware</a>, or press "OK" to launch anyways.]]></string>
<string name="nca_verification_disabled">NCA Verification Disabled</string>
<string name="nca_verification_disabled_description">This is required to run new games and updates, but may cause instability or crashes if NCA files are corrupt, modified, or tampered with. If unsure, re-enable verification in Advanced Settings -> System, and use firmware versions of 19.0.1 or below.</string>
<!-- Intent Launch strings --> <!-- Intent Launch strings -->
<string name="searching_for_game">Searching for game...</string> <string name="searching_for_game">Searching for game...</string>
<string name="game_not_found_for_title_id">Game not found for Title ID: %1$s</string> <string name="game_not_found_for_title_id">Game not found for Title ID: %1$s</string>

View file

@ -626,10 +626,6 @@ struct Values {
true, true, &rng_seed_enabled}; true, true, &rng_seed_enabled};
Setting<std::string> device_name{ Setting<std::string> device_name{
linkage, "Eden", "device_name", Category::System, Specialization::Default, true, true}; linkage, "Eden", "device_name", Category::System, Specialization::Default, true, true};
SwitchableSetting<bool> disable_nca_verification{linkage, true, "disable_nca_verification",
Category::System, Specialization::Default};
Setting<bool> hide_nca_verification_popup{
linkage, false, "hide_nca_verification_popup", Category::System, Specialization::Default};
Setting<s32> current_user{linkage, 0, "current_user", Category::System}; Setting<s32> current_user{linkage, 0, "current_user", Category::System};

View file

@ -238,9 +238,7 @@ void BucketTree::Initialize(size_t node_size, s64 end_offset) {
ASSERT(NodeSizeMin <= node_size && node_size <= NodeSizeMax); ASSERT(NodeSizeMin <= node_size && node_size <= NodeSizeMax);
ASSERT(Common::IsPowerOfTwo(node_size)); ASSERT(Common::IsPowerOfTwo(node_size));
if (!Settings::values.disable_nca_verification.GetValue()) { ASSERT(end_offset > 0);
ASSERT(end_offset > 0);
}
ASSERT(!this->IsInitialized()); ASSERT(!this->IsInitialized());
m_node_size = node_size; m_node_size = node_size;

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// 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
@ -1296,91 +1299,65 @@ Result NcaFileSystemDriver::CreateIntegrityVerificationStorageImpl(
ASSERT(base_storage != nullptr); ASSERT(base_storage != nullptr);
ASSERT(layer_info_offset >= 0); ASSERT(layer_info_offset >= 0);
if (!Settings::values.disable_nca_verification.GetValue()) { // Define storage types.
// Define storage types. using VerificationStorage = HierarchicalIntegrityVerificationStorage;
using VerificationStorage = HierarchicalIntegrityVerificationStorage; using StorageInfo = VerificationStorage::HierarchicalStorageInformation;
using StorageInfo = VerificationStorage::HierarchicalStorageInformation;
// Validate the meta info. // Validate the meta info.
HierarchicalIntegrityVerificationInformation level_hash_info; HierarchicalIntegrityVerificationInformation level_hash_info;
std::memcpy(std::addressof(level_hash_info), std::addressof(meta_info.level_hash_info), std::memcpy(std::addressof(level_hash_info),
sizeof(level_hash_info)); std::addressof(meta_info.level_hash_info),
sizeof(level_hash_info));
R_UNLESS(IntegrityMinLayerCount <= level_hash_info.max_layers, R_UNLESS(IntegrityMinLayerCount <= level_hash_info.max_layers,
ResultInvalidNcaHierarchicalIntegrityVerificationLayerCount); ResultInvalidNcaHierarchicalIntegrityVerificationLayerCount);
R_UNLESS(level_hash_info.max_layers <= IntegrityMaxLayerCount, R_UNLESS(level_hash_info.max_layers <= IntegrityMaxLayerCount,
ResultInvalidNcaHierarchicalIntegrityVerificationLayerCount); ResultInvalidNcaHierarchicalIntegrityVerificationLayerCount);
// Get the base storage size. // Get the base storage size.
s64 base_storage_size = base_storage->GetSize(); s64 base_storage_size = base_storage->GetSize();
// Create storage info. // Create storage info.
StorageInfo storage_info; StorageInfo storage_info;
for (s32 i = 0; i < static_cast<s32>(level_hash_info.max_layers - 2); ++i) { for (s32 i = 0; i < static_cast<s32>(level_hash_info.max_layers - 2); ++i) {
const auto& layer_info = level_hash_info.info[i]; const auto& layer_info = level_hash_info.info[i];
R_UNLESS(layer_info_offset + layer_info.offset + layer_info.size <= base_storage_size, R_UNLESS(layer_info_offset + layer_info.offset + layer_info.size <= base_storage_size,
ResultNcaBaseStorageOutOfRangeD);
storage_info[i + 1] = std::make_shared<OffsetVfsFile>(
base_storage, layer_info.size, layer_info_offset + layer_info.offset);
}
// Set the last layer info.
const auto& layer_info = level_hash_info.info[level_hash_info.max_layers - 2];
const s64 last_layer_info_offset = layer_info_offset > 0 ? 0LL : layer_info.offset.Get();
R_UNLESS(last_layer_info_offset + layer_info.size <= base_storage_size,
ResultNcaBaseStorageOutOfRangeD); ResultNcaBaseStorageOutOfRangeD);
if (layer_info_offset > 0) {
R_UNLESS(last_layer_info_offset + layer_info.size <= layer_info_offset,
ResultRomNcaInvalidIntegrityLayerInfoOffset);
}
storage_info[level_hash_info.max_layers - 1] = std::make_shared<OffsetVfsFile>(
std::move(base_storage), layer_info.size, last_layer_info_offset);
// Make the integrity romfs storage. storage_info[i + 1] = std::make_shared<OffsetVfsFile>(base_storage,
auto integrity_storage = std::make_shared<IntegrityRomFsStorage>(); layer_info.size,
R_UNLESS(integrity_storage != nullptr, ResultAllocationMemoryFailedAllocateShared); layer_info_offset + layer_info.offset);
// Initialize the integrity storage.
R_TRY(integrity_storage->Initialize(level_hash_info, meta_info.master_hash, storage_info,
max_data_cache_entries, max_hash_cache_entries,
buffer_level));
// Set the output.
*out = std::move(integrity_storage);
R_SUCCEED();
} else {
// Read IVFC layout
HierarchicalIntegrityVerificationInformation lhi{};
std::memcpy(std::addressof(lhi), std::addressof(meta_info.level_hash_info), sizeof(lhi));
R_UNLESS(IntegrityMinLayerCount <= lhi.max_layers,
ResultInvalidNcaHierarchicalIntegrityVerificationLayerCount);
R_UNLESS(lhi.max_layers <= IntegrityMaxLayerCount,
ResultInvalidNcaHierarchicalIntegrityVerificationLayerCount);
const auto& data_li = lhi.info[lhi.max_layers - 2];
const s64 base_size = base_storage->GetSize();
// Compute the data layer window
const s64 data_off = (layer_info_offset > 0) ? 0LL : data_li.offset.Get();
R_UNLESS(data_off + data_li.size <= base_size, ResultNcaBaseStorageOutOfRangeD);
if (layer_info_offset > 0) {
R_UNLESS(data_off + data_li.size <= layer_info_offset,
ResultRomNcaInvalidIntegrityLayerInfoOffset);
}
// TODO: Passthrough (temporary compatibility: integrity disabled)
auto data_view = std::make_shared<OffsetVfsFile>(base_storage, data_li.size, data_off);
R_UNLESS(data_view != nullptr, ResultAllocationMemoryFailedAllocateShared);
auto passthrough = std::make_shared<PassthroughStorage>(std::move(data_view));
R_UNLESS(passthrough != nullptr, ResultAllocationMemoryFailedAllocateShared);
*out = std::move(passthrough);
R_SUCCEED();
} }
// Set the last layer info.
const auto& layer_info = level_hash_info.info[level_hash_info.max_layers - 2];
const s64 last_layer_info_offset = layer_info_offset > 0 ? 0LL : layer_info.offset.Get();
R_UNLESS(last_layer_info_offset + layer_info.size <= base_storage_size,
ResultNcaBaseStorageOutOfRangeD);
if (layer_info_offset > 0) {
R_UNLESS(last_layer_info_offset + layer_info.size <= layer_info_offset,
ResultRomNcaInvalidIntegrityLayerInfoOffset);
}
storage_info[level_hash_info.max_layers - 1]
= std::make_shared<OffsetVfsFile>(std::move(base_storage),
layer_info.size,
last_layer_info_offset);
// Make the integrity romfs storage.
auto integrity_storage = std::make_shared<IntegrityRomFsStorage>();
R_UNLESS(integrity_storage != nullptr, ResultAllocationMemoryFailedAllocateShared);
// Initialize the integrity storage.
R_TRY(integrity_storage->Initialize(level_hash_info,
meta_info.master_hash,
storage_info,
max_data_cache_entries,
max_hash_cache_entries,
buffer_level));
// Set the output.
*out = std::move(integrity_storage);
R_SUCCEED();
} }
Result NcaFileSystemDriver::CreateRegionSwitchStorage(VirtualFile* out, Result NcaFileSystemDriver::CreateRegionSwitchStorage(VirtualFile* out,

View file

@ -1,6 +1,10 @@
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2023 yuzu Emulator Project # SPDX-FileCopyrightText: 2023 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later # SPDX-License-Identifier: GPL-2.0-or-later
find_package(Qt6 REQUIRED COMPONENTS Core)
find_package(Qt6 REQUIRED COMPONENTS Core) find_package(Qt6 REQUIRED COMPONENTS Core)
add_library(qt_common STATIC add_library(qt_common STATIC
@ -22,6 +26,8 @@ add_library(qt_common STATIC
qt_content_util.h qt_content_util.cpp qt_content_util.h qt_content_util.cpp
qt_rom_util.h qt_rom_util.cpp qt_rom_util.h qt_rom_util.cpp
qt_applet_util.h qt_applet_util.cpp qt_applet_util.h qt_applet_util.cpp
qt_progress_dialog.h qt_progress_dialog.cpp
) )
create_target_directory_groups(qt_common) create_target_directory_groups(qt_common)
@ -33,7 +39,8 @@ endif()
add_subdirectory(externals) add_subdirectory(externals)
target_link_libraries(qt_common PRIVATE core Qt6::Core SimpleIni::SimpleIni QuaZip::QuaZip) target_link_libraries(qt_common PRIVATE core Qt6::Core SimpleIni::SimpleIni QuaZip::QuaZip frozen::frozen)
target_link_libraries(qt_common PRIVATE Qt6::Core)
if (NOT WIN32) if (NOT WIN32)
target_include_directories(qt_common PRIVATE ${Qt6Gui_PRIVATE_INCLUDE_DIRS}) target_include_directories(qt_common PRIVATE ${Qt6Gui_PRIVATE_INCLUDE_DIRS})

View file

@ -14,3 +14,7 @@ set_directory_properties(PROPERTIES EXCLUDE_FROM_ALL ON)
# QuaZip # QuaZip
AddJsonPackage(quazip) AddJsonPackage(quazip)
# frozen
# TODO(crueter): Qt String Lookup
AddJsonPackage(frozen)

View file

@ -8,5 +8,12 @@
"options": [ "options": [
"QUAZIP_INSTALL OFF" "QUAZIP_INSTALL OFF"
] ]
},
"frozen": {
"package": "frozen",
"repo": "serge-sans-paille/frozen",
"sha": "61dce5ae18",
"hash": "1ae3d073e659c1f24b2cdd76379c90d6af9e06bc707d285a4fafce05f7a4c9e592ff208c94a9ae0f0d07620b3c6cec191f126b03d70ad4dfa496a86ed5658a6d",
"bundled": true
} }
} }

View file

@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#include "qt_common.h" #include "qt_common.h"
#include "common/fs/fs.h"
#include <QGuiApplication> #include <QGuiApplication>
#include <QStringLiteral> #include <QStringLiteral>
@ -22,9 +23,15 @@
namespace QtCommon { namespace QtCommon {
QObject *rootObject = nullptr; #ifdef YUZU_QT_WIDGETS
QWidget* rootObject = nullptr;
#else
QObject* rootObject = nullptr;
#endif
std::unique_ptr<Core::System> system = nullptr; std::unique_ptr<Core::System> system = nullptr;
std::shared_ptr<FileSys::RealVfsFilesystem> vfs = nullptr; std::shared_ptr<FileSys::RealVfsFilesystem> vfs = nullptr;
std::unique_ptr<FileSys::ManualContentProvider> provider = nullptr;
Core::Frontend::WindowSystemType GetWindowSystemType() Core::Frontend::WindowSystemType GetWindowSystemType()
{ {
@ -81,11 +88,35 @@ const QString tr(const std::string& str)
return QGuiApplication::tr(str.c_str()); return QGuiApplication::tr(str.c_str());
} }
#ifdef YUZU_QT_WIDGETS
void Init(QWidget* root)
#else
void Init(QObject* root) void Init(QObject* root)
#endif
{ {
system = std::make_unique<Core::System>(); system = std::make_unique<Core::System>();
rootObject = root; rootObject = root;
vfs = std::make_unique<FileSys::RealVfsFilesystem>(); vfs = std::make_unique<FileSys::RealVfsFilesystem>();
provider = std::make_unique<FileSys::ManualContentProvider>();
}
std::filesystem::path GetEdenCommand() {
std::filesystem::path command;
QString appimage = QString::fromLocal8Bit(getenv("APPIMAGE"));
if (!appimage.isEmpty()) {
command = std::filesystem::path{appimage.toStdString()};
} else {
const QStringList args = QGuiApplication::arguments();
command = args[0].toStdString();
}
// If relative path, make it an absolute path
if (command.c_str()[0] == '.') {
command = Common::FS::GetCurrentDir() / command;
}
return command;
} }
} // namespace QtCommon } // namespace QtCommon

View file

@ -4,18 +4,25 @@
#ifndef QT_COMMON_H #ifndef QT_COMMON_H
#define QT_COMMON_H #define QT_COMMON_H
#include <memory>
#include <QWindow> #include <QWindow>
#include "core/core.h" #include "core/core.h"
#include "core/file_sys/registered_cache.h"
#include <core/frontend/emu_window.h> #include <core/frontend/emu_window.h>
#include <memory>
#include <core/file_sys/vfs/vfs_real.h> #include <core/file_sys/vfs/vfs_real.h>
namespace QtCommon { namespace QtCommon {
#ifdef YUZU_QT_WIDGETS
extern QWidget *rootObject;
#else
extern QObject *rootObject; extern QObject *rootObject;
#endif
extern std::unique_ptr<Core::System> system; extern std::unique_ptr<Core::System> system;
extern std::shared_ptr<FileSys::RealVfsFilesystem> vfs; extern std::shared_ptr<FileSys::RealVfsFilesystem> vfs;
extern std::unique_ptr<FileSys::ManualContentProvider> provider;
typedef std::function<bool(std::size_t, std::size_t)> QtProgressCallback; typedef std::function<bool(std::size_t, std::size_t)> QtProgressCallback;
@ -23,10 +30,15 @@ Core::Frontend::WindowSystemType GetWindowSystemType();
Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow *window); Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow *window);
#ifdef YUZU_QT_WIDGETS
void Init(QWidget *root);
#else
void Init(QObject *root); void Init(QObject *root);
#endif
const QString tr(const char *str); const QString tr(const char *str);
const QString tr(const std::string &str); const QString tr(const std::string &str);
std::filesystem::path GetEdenCommand();
} // namespace QtCommon } // namespace QtCommon
#endif #endif

View file

@ -6,6 +6,7 @@
#include "frontend_common/content_manager.h" #include "frontend_common/content_manager.h"
#include "frontend_common/firmware_manager.h" #include "frontend_common/firmware_manager.h"
#include "qt_common/qt_common.h" #include "qt_common/qt_common.h"
#include "qt_common/qt_progress_dialog.h"
#include "qt_frontend_util.h" #include "qt_frontend_util.h"
#include <JlCompress.h> #include <JlCompress.h>
@ -31,24 +32,34 @@ bool CheckGameFirmware(u64 program_id, QObject* parent)
return true; return true;
} }
FirmwareInstallResult InstallFirmware(const QString& location, void InstallFirmware(const QString& location, bool recursive)
bool recursive,
QtProgressCallback callback,
FileSys::VfsFilesystem* vfs)
{ {
static constexpr const char* failedTitle = "Firmware Install Failed"; QtCommon::Frontend::QtProgressDialog progress(tr("Installing Firmware..."),
static constexpr const char* successTitle = "Firmware Install Failed"; tr("Cancel"),
static constexpr QMessageBox::StandardButtons buttons = QMessageBox::Ok; 0,
100,
rootObject);
progress.setWindowModality(Qt::WindowModal);
progress.setMinimumDuration(100);
progress.setAutoClose(false);
progress.setAutoReset(false);
progress.show();
// Declare progress callback.
auto callback = [&](size_t total_size, size_t processed_size) {
progress.setValue(static_cast<int>((processed_size * 100) / total_size));
return progress.wasCanceled();
};
static constexpr const char* failedTitle = "Firmware Install Failed";
static constexpr const char* successTitle = "Firmware Install Succeeded";
QMessageBox::Icon icon; QMessageBox::Icon icon;
FirmwareInstallResult result; FirmwareInstallResult result;
const auto ShowMessage = [&]() { const auto ShowMessage = [&]() {
QtCommon::Frontend::ShowMessage(icon, QtCommon::Frontend::ShowMessage(icon,
failedTitle, failedTitle,
GetFirmwareInstallResultString(result), GetFirmwareInstallResultString(result));
buttons,
rootObject);
}; };
LOG_INFO(Frontend, "Installing firmware from {}", location.toStdString()); LOG_INFO(Frontend, "Installing firmware from {}", location.toStdString());
@ -57,7 +68,7 @@ FirmwareInstallResult InstallFirmware(const QString& location,
// there.) // there.)
std::filesystem::path firmware_source_path = location.toStdString(); std::filesystem::path firmware_source_path = location.toStdString();
if (!Common::FS::IsDir(firmware_source_path)) { if (!Common::FS::IsDir(firmware_source_path)) {
return FirmwareInstallResult::NoOp; return;
} }
std::vector<std::filesystem::path> out; std::vector<std::filesystem::path> out;
@ -86,7 +97,7 @@ FirmwareInstallResult InstallFirmware(const QString& location,
result = FirmwareInstallResult::NoNCAs; result = FirmwareInstallResult::NoNCAs;
icon = QMessageBox::Warning; icon = QMessageBox::Warning;
ShowMessage(); ShowMessage();
return result; return;
} }
// Locate and erase the content of nand/system/Content/registered/*.nca, if any. // Locate and erase the content of nand/system/Content/registered/*.nca, if any.
@ -96,7 +107,7 @@ FirmwareInstallResult InstallFirmware(const QString& location,
result = FirmwareInstallResult::FailedDelete; result = FirmwareInstallResult::FailedDelete;
icon = QMessageBox::Critical; icon = QMessageBox::Critical;
ShowMessage(); ShowMessage();
return result; return;
} }
LOG_INFO(Frontend, LOG_INFO(Frontend,
@ -127,7 +138,7 @@ FirmwareInstallResult InstallFirmware(const QString& location,
result = FirmwareInstallResult::FailedCorrupted; result = FirmwareInstallResult::FailedCorrupted;
icon = QMessageBox::Warning; icon = QMessageBox::Warning;
ShowMessage(); ShowMessage();
return result; return;
} }
} }
@ -135,12 +146,34 @@ FirmwareInstallResult InstallFirmware(const QString& location,
result = FirmwareInstallResult::FailedCopy; result = FirmwareInstallResult::FailedCopy;
icon = QMessageBox::Critical; icon = QMessageBox::Critical;
ShowMessage(); ShowMessage();
return result; return;
} }
// Re-scan VFS for the newly placed firmware files. // Re-scan VFS for the newly placed firmware files.
system->GetFileSystemController().CreateFactories(*vfs); system->GetFileSystemController().CreateFactories(*vfs);
auto VerifyFirmwareCallback = [&](size_t total_size, size_t processed_size) {
progress.setValue(90 + static_cast<int>((processed_size * 10) / total_size));
return progress.wasCanceled();
};
auto results = ContentManager::VerifyInstalledContents(*QtCommon::system,
*QtCommon::provider,
VerifyFirmwareCallback,
true);
if (results.size() > 0) {
const auto failed_names = QString::fromStdString(
fmt::format("{}", fmt::join(results, "\n")));
progress.close();
QtCommon::Frontend::Critical(tr("Firmware integrity verification failed!"),
tr("Verification failed for the following files:\n\n%1")
.arg(failed_names));
return;
}
progress.close();
const auto pair = FirmwareManager::GetFirmwareVersion(*system); const auto pair = FirmwareManager::GetFirmwareVersion(*system);
const auto firmware_data = pair.first; const auto firmware_data = pair.first;
const std::string display_version(firmware_data.display_version.data()); const std::string display_version(firmware_data.display_version.data());
@ -149,9 +182,7 @@ FirmwareInstallResult InstallFirmware(const QString& location,
QtCommon::Frontend::Information(rootObject, QtCommon::Frontend::Information(rootObject,
tr(successTitle), tr(successTitle),
tr(GetFirmwareInstallResultString(result)) tr(GetFirmwareInstallResultString(result))
.arg(QString::fromStdString(display_version)), .arg(QString::fromStdString(display_version)));
buttons);
return result;
} }
QString UnzipFirmwareToTmp(const QString& location) QString UnzipFirmwareToTmp(const QString& location)
@ -179,8 +210,23 @@ QString UnzipFirmwareToTmp(const QString& location)
} }
// Content // // Content //
void VerifyGameContents(const std::string& game_path, QtProgressCallback callback) void VerifyGameContents(const std::string& game_path)
{ {
QtCommon::Frontend::QtProgressDialog progress(tr("Verifying integrity..."),
tr("Cancel"),
0,
100,
rootObject);
progress.setWindowModality(Qt::WindowModal);
progress.setMinimumDuration(100);
progress.setAutoClose(false);
progress.setAutoReset(false);
const auto callback = [&](size_t total_size, size_t processed_size) {
progress.setValue(static_cast<int>((processed_size * 100) / total_size));
return progress.wasCanceled();
};
const auto result = ContentManager::VerifyGameContents(*system, game_path, callback); const auto result = ContentManager::VerifyGameContents(*system, game_path, callback);
switch (result) { switch (result) {
@ -234,4 +280,34 @@ void InstallKeys()
} }
} }
void VerifyInstalledContents() {
// Initialize a progress dialog.
QtCommon::Frontend::QtProgressDialog progress(tr("Verifying integrity..."), tr("Cancel"), 0, 100, QtCommon::rootObject);
progress.setWindowModality(Qt::WindowModal);
progress.setMinimumDuration(100);
progress.setAutoClose(false);
progress.setAutoReset(false);
// Declare progress callback.
auto QtProgressCallback = [&](size_t total_size, size_t processed_size) {
progress.setValue(static_cast<int>((processed_size * 100) / total_size));
return progress.wasCanceled();
};
const std::vector<std::string> result =
ContentManager::VerifyInstalledContents(*QtCommon::system, *QtCommon::provider, QtProgressCallback);
progress.close();
if (result.empty()) {
QtCommon::Frontend::Information(tr("Integrity verification succeeded!"),
tr("The operation completed successfully."));
} else {
const auto failed_names =
QString::fromStdString(fmt::format("{}", fmt::join(result, "\n")));
QtCommon::Frontend::Critical(
tr("Integrity verification failed!"),
tr("Verification failed for the following files:\n\n%1").arg(failed_names));
}
}
} // namespace QtCommon::Content } // namespace QtCommon::Content

View file

@ -6,8 +6,6 @@
#include <QObject> #include <QObject>
#include "common/common_types.h" #include "common/common_types.h"
#include "core/core.h"
#include "qt_common/qt_common.h"
namespace QtCommon::Content { namespace QtCommon::Content {
@ -37,11 +35,7 @@ inline constexpr const char *GetFirmwareInstallResultString(FirmwareInstallResul
return FIRMWARE_RESULTS.at(static_cast<std::size_t>(result)); return FIRMWARE_RESULTS.at(static_cast<std::size_t>(result));
} }
FirmwareInstallResult InstallFirmware( void InstallFirmware(const QString &location, bool recursive);
const QString &location,
bool recursive,
QtProgressCallback callback,
FileSys::VfsFilesystem *vfs);
QString UnzipFirmwareToTmp(const QString &location); QString UnzipFirmwareToTmp(const QString &location);
@ -49,6 +43,7 @@ QString UnzipFirmwareToTmp(const QString &location);
void InstallKeys(); void InstallKeys();
// Content // // Content //
void VerifyGameContents(const std::string &game_path, QtProgressCallback callback); void VerifyGameContents(const std::string &game_path);
void VerifyInstalledContents();
} }
#endif // QT_CONTENT_UTIL_H #endif // QT_CONTENT_UTIL_H

View file

@ -10,11 +10,8 @@
namespace QtCommon::Frontend { namespace QtCommon::Frontend {
QMessageBox::StandardButton ShowMessage(QMessageBox::Icon icon, StandardButton ShowMessage(
const QString &title, Icon icon, const QString &title, const QString &text, StandardButtons buttons, QObject *parent)
const QString &text,
QMessageBox::StandardButtons buttons,
QObject *parent)
{ {
#ifdef YUZU_QT_WIDGETS #ifdef YUZU_QT_WIDGETS
QMessageBox *box = new QMessageBox(icon, title, text, buttons, (QWidget *) parent); QMessageBox *box = new QMessageBox(icon, title, text, buttons, (QWidget *) parent);

View file

@ -5,12 +5,12 @@
#define QT_FRONTEND_UTIL_H #define QT_FRONTEND_UTIL_H
#include <QGuiApplication> #include <QGuiApplication>
#include <QMessageBox>
#include "qt_common/qt_common.h" #include "qt_common/qt_common.h"
#ifdef YUZU_QT_WIDGETS #ifdef YUZU_QT_WIDGETS
#include <QFileDialog> #include <QFileDialog>
#include <QWidget> #include <QWidget>
#include <QMessageBox>
#endif #endif
/** /**
@ -23,6 +23,11 @@ Q_NAMESPACE
#ifdef YUZU_QT_WIDGETS #ifdef YUZU_QT_WIDGETS
using Options = QFileDialog::Options; using Options = QFileDialog::Options;
using Option = QFileDialog::Option; using Option = QFileDialog::Option;
using StandardButton = QMessageBox::StandardButton;
using StandardButtons = QMessageBox::StandardButtons;
using Icon = QMessageBox::Icon;
#else #else
enum Option { enum Option {
ShowDirsOnly = 0x00000001, ShowDirsOnly = 0x00000001,
@ -36,44 +41,96 @@ enum Option {
Q_ENUM_NS(Option) Q_ENUM_NS(Option)
Q_DECLARE_FLAGS(Options, Option) Q_DECLARE_FLAGS(Options, Option)
Q_FLAG_NS(Options) Q_FLAG_NS(Options)
enum StandardButton {
// keep this in sync with QDialogButtonBox::StandardButton and QPlatformDialogHelper::StandardButton
NoButton = 0x00000000,
Ok = 0x00000400,
Save = 0x00000800,
SaveAll = 0x00001000,
Open = 0x00002000,
Yes = 0x00004000,
YesToAll = 0x00008000,
No = 0x00010000,
NoToAll = 0x00020000,
Abort = 0x00040000,
Retry = 0x00080000,
Ignore = 0x00100000,
Close = 0x00200000,
Cancel = 0x00400000,
Discard = 0x00800000,
Help = 0x01000000,
Apply = 0x02000000,
Reset = 0x04000000,
RestoreDefaults = 0x08000000,
FirstButton = Ok, // internal
LastButton = RestoreDefaults, // internal
YesAll = YesToAll, // obsolete
NoAll = NoToAll, // obsolete
Default = 0x00000100, // obsolete
Escape = 0x00000200, // obsolete
FlagMask = 0x00000300, // obsolete
ButtonMask = ~FlagMask // obsolete
};
Q_ENUM_NS(StandardButton)
#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
typedef StandardButton Button;
#endif
Q_DECLARE_FLAGS(StandardButtons, StandardButton)
Q_FLAG_NS(StandardButtons)
enum Icon {
// keep this in sync with QMessageDialogOptions::StandardIcon
NoIcon = 0,
Information = 1,
Warning = 2,
Critical = 3,
Question = 4
};
Q_ENUM_NS(Icon)
#endif #endif
// TODO(crueter) widgets-less impl, choices et al. // TODO(crueter) widgets-less impl, choices et al.
QMessageBox::StandardButton ShowMessage(QMessageBox::Icon icon, StandardButton ShowMessage(Icon icon,
const QString &title, const QString &title,
const QString &text, const QString &text,
QMessageBox::StandardButtons buttons = QMessageBox::NoButton, StandardButtons buttons = StandardButton::NoButton,
QObject *parent = nullptr); QObject *parent = nullptr);
#define UTIL_OVERRIDES(level) \ #define UTIL_OVERRIDES(level) \
inline QMessageBox::StandardButton level(QObject *parent, \ inline StandardButton level(QObject *parent, \
const QString &title, \ const QString &title, \
const QString &text, \ const QString &text, \
QMessageBox::StandardButtons buttons = QMessageBox::Ok) \ StandardButtons buttons = StandardButton::Ok) \
{ \ { \
return ShowMessage(QMessageBox::level, title, text, buttons, parent); \ return ShowMessage(Icon::level, title, text, buttons, parent); \
} \ } \
inline QMessageBox::StandardButton level(QObject *parent, \ inline StandardButton level(QObject *parent, \
const char *title, \ const char *title, \
const char *text, \ const char *text, \
QMessageBox::StandardButtons buttons \ StandardButtons buttons \
= QMessageBox::Ok) \ = StandardButton::Ok) \
{ \ { \
return ShowMessage(QMessageBox::level, tr(title), tr(text), buttons, parent); \ return ShowMessage(Icon::level, tr(title), tr(text), buttons, parent); \
} \ } \
inline QMessageBox::StandardButton level(const char *title, \ inline StandardButton level(const char *title, \
const char *text, \ const char *text, \
QMessageBox::StandardButtons buttons \ StandardButtons buttons \
= QMessageBox::Ok) \ = StandardButton::Ok) \
{ \ { \
return ShowMessage(QMessageBox::level, tr(title), tr(text), buttons, rootObject); \ return ShowMessage(Icon::level, tr(title), tr(text), buttons, rootObject); \
} \ } \
inline QMessageBox::StandardButton level(const QString title, \ inline StandardButton level(const QString title, \
const QString &text, \ const QString &text, \
QMessageBox::StandardButtons buttons \ StandardButtons buttons \
= QMessageBox::Ok) \ = StandardButton::Ok) \
{ \ { \
return ShowMessage(QMessageBox::level, title, text, buttons, rootObject); \ return ShowMessage(Icon::level, title, text, buttons, rootObject); \
} }
UTIL_OVERRIDES(Information) UTIL_OVERRIDES(Information)

View file

@ -5,12 +5,15 @@
#include "common/fs/fs.h" #include "common/fs/fs.h"
#include "common/fs/path_util.h" #include "common/fs/path_util.h"
#include "core/file_sys/savedata_factory.h" #include "core/file_sys/savedata_factory.h"
#include "core/hle/service/am/am_types.h"
#include "frontend_common/content_manager.h" #include "frontend_common/content_manager.h"
#include "qt_common.h" #include "qt_common.h"
#include "qt_common/uisettings.h" #include "qt_common/uisettings.h"
#include "qt_frontend_util.h" #include "qt_frontend_util.h"
#include "yuzu/util/util.h"
#include <QDesktopServices> #include <QDesktopServices>
#include <QStandardPaths>
#include <QUrl> #include <QUrl>
#ifdef _WIN32 #ifdef _WIN32
@ -397,4 +400,178 @@ void ResetMetadata()
} }
} }
// Uhhh //
// Messages in pre-defined message boxes for less code spaghetti
inline constexpr bool CreateShortcutMessagesGUI(ShortcutMessages imsg, const QString& game_title)
{
int result = 0;
QMessageBox::StandardButtons buttons;
switch (imsg) {
case ShortcutMessages::Fullscreen:
buttons = QMessageBox::Yes | QMessageBox::No;
result
= QtCommon::Frontend::Information(tr("Create Shortcut"),
tr("Do you want to launch the game in fullscreen?"),
buttons);
return result == QMessageBox::Yes;
case ShortcutMessages::Success:
QtCommon::Frontend::Information(tr("Shortcut Created"),
tr("Successfully created a shortcut to %1").arg(game_title));
return false;
case ShortcutMessages::Volatile:
buttons = QMessageBox::StandardButton::Ok | QMessageBox::StandardButton::Cancel;
result = QtCommon::Frontend::Warning(
tr("Shortcut may be Volatile!"),
tr("This will create a shortcut to the current AppImage. This may "
"not work well if you update. Continue?"),
buttons);
return result == QMessageBox::Ok;
default:
buttons = QMessageBox::Ok;
QtCommon::Frontend::Critical(tr("Failed to Create Shortcut"),
tr("Failed to create a shortcut to %1").arg(game_title),
buttons);
return false;
}
}
void CreateShortcut(const std::string& game_path,
const u64 program_id,
const std::string& game_title_,
const ShortcutTarget &target,
std::string arguments_,
const bool needs_title)
{
// Get path to Eden executable
std::filesystem::path command = GetEdenCommand();
// Shortcut path
std::filesystem::path shortcut_path = GetShortcutPath(target);
if (!std::filesystem::exists(shortcut_path)) {
CreateShortcutMessagesGUI(ShortcutMessages::Failed,
QString::fromStdString(shortcut_path.generic_string()));
LOG_ERROR(Frontend, "Invalid shortcut target {}", shortcut_path.generic_string());
return;
}
const FileSys::PatchManager pm{program_id, QtCommon::system->GetFileSystemController(),
QtCommon::system->GetContentProvider()};
const auto control = pm.GetControlMetadata();
const auto loader =
Loader::GetLoader(*QtCommon::system, QtCommon::vfs->OpenFile(game_path, FileSys::OpenMode::Read));
std::string game_title{game_title_};
// Delete illegal characters from title
if (needs_title) {
game_title = fmt::format("{:016X}", program_id);
if (control.first != nullptr) {
game_title = control.first->GetApplicationName();
} else {
loader->ReadTitle(game_title);
}
}
const std::string illegal_chars = "<>:\"/\\|?*.";
for (auto it = game_title.rbegin(); it != game_title.rend(); ++it) {
if (illegal_chars.find(*it) != std::string::npos) {
game_title.erase(it.base() - 1);
}
}
const QString qgame_title = QString::fromStdString(game_title);
// Get icon from game file
std::vector<u8> icon_image_file{};
if (control.second != nullptr) {
icon_image_file = control.second->ReadAllBytes();
} else if (loader->ReadIcon(icon_image_file) != Loader::ResultStatus::Success) {
LOG_WARNING(Frontend, "Could not read icon from {:s}", game_path);
}
QImage icon_data =
QImage::fromData(icon_image_file.data(), static_cast<int>(icon_image_file.size()));
std::filesystem::path out_icon_path;
if (QtCommon::Game::MakeShortcutIcoPath(program_id, game_title, out_icon_path)) {
if (!SaveIconToFile(out_icon_path, icon_data)) {
LOG_ERROR(Frontend, "Could not write icon to file");
}
} else {
QtCommon::Frontend::Critical(
tr("Create Icon"),
tr("Cannot create icon file. Path \"%1\" does not exist and cannot be created.")
.arg(QString::fromStdString(out_icon_path.string())));
}
#if defined(__unix__) && !defined(__APPLE__) && !defined(__ANDROID__)
// Special case for AppImages
// Warn once if we are making a shortcut to a volatile AppImage
if (command.string().ends_with(".AppImage") && !UISettings::values.shortcut_already_warned) {
if (!CreateShortcutMessagesGUI(ShortcutMessages::Volatile, qgame_title)) {
return;
}
UISettings::values.shortcut_already_warned = true;
}
#endif
// Create shortcut
std::string arguments{arguments_};
if (CreateShortcutMessagesGUI(ShortcutMessages::Fullscreen, qgame_title)) {
arguments = "-f " + arguments;
}
const std::string comment = fmt::format("Start {:s} with the Eden Emulator", game_title);
const std::string categories = "Game;Emulator;Qt;";
const std::string keywords = "Switch;Nintendo;";
if (QtCommon::Game::CreateShortcutLink(shortcut_path, comment, out_icon_path, command,
arguments, categories, keywords, game_title)) {
CreateShortcutMessagesGUI(ShortcutMessages::Success,
qgame_title);
return;
}
CreateShortcutMessagesGUI(ShortcutMessages::Failed,
qgame_title);
}
constexpr std::string GetShortcutPath(ShortcutTarget target) {
{
std::string shortcut_path{};
if (target == ShortcutTarget::Desktop) {
shortcut_path = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)
.toStdString();
} else if (target == ShortcutTarget::Applications) {
shortcut_path = QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation)
.toStdString();
}
return shortcut_path;
}
}
void CreateHomeMenuShortcut(ShortcutTarget target) {
constexpr u64 QLaunchId = static_cast<u64>(Service::AM::AppletProgramId::QLaunch);
auto bis_system = QtCommon::system->GetFileSystemController().GetSystemNANDContents();
if (!bis_system) {
QtCommon::Frontend::Warning(tr("No firmware available"),
tr("Please install firmware to use the home menu."));
return;
}
auto qlaunch_nca = bis_system->GetEntry(QLaunchId, FileSys::ContentRecordType::Program);
if (!qlaunch_nca) {
QtCommon::Frontend::Warning(tr("Home Menu Applet"),
tr("Home Menu is not available. Please reinstall firmware."));
return;
}
auto qlaunch_applet_nca = bis_system->GetEntry(QLaunchId, FileSys::ContentRecordType::Program);
const auto game_path = qlaunch_applet_nca->GetFullPath();
// TODO(crueter): Make this use the Eden icon
CreateShortcut(game_path, QLaunchId, "Switch Home Menu", target, "-qlaunch", false);
}
} // namespace QtCommon::Game } // namespace QtCommon::Game

View file

@ -5,6 +5,7 @@
#define QT_GAME_UTIL_H #define QT_GAME_UTIL_H
#include <QObject> #include <QObject>
#include <QStandardPaths>
#include "common/fs/path_util.h" #include "common/fs/path_util.h"
#include <core/file_sys/vfs/vfs.h> #include <core/file_sys/vfs/vfs.h>
@ -24,6 +25,18 @@ enum class GameListRemoveTarget {
CacheStorage, CacheStorage,
}; };
enum class ShortcutTarget {
Desktop,
Applications,
};
enum class ShortcutMessages{
Fullscreen = 0,
Success = 1,
Volatile = 2,
Failed = 3
};
bool CreateShortcutLink(const std::filesystem::path& shortcut_path, bool CreateShortcutLink(const std::filesystem::path& shortcut_path,
const std::string& comment, const std::string& comment,
const std::filesystem::path& icon_path, const std::filesystem::path& icon_path,
@ -57,6 +70,17 @@ void RemoveCacheStorage(u64 program_id, FileSys::VfsFilesystem* vfs);
// Metadata // // Metadata //
void ResetMetadata(); void ResetMetadata();
// Shortcuts //
void CreateShortcut(const std::string& game_path,
const u64 program_id,
const std::string& game_title_,
const ShortcutTarget& target,
std::string arguments_,
const bool needs_title);
constexpr std::string GetShortcutPath(ShortcutTarget target);
void CreateHomeMenuShortcut(ShortcutTarget target);
} }
#endif // QT_GAME_UTIL_H #endif // QT_GAME_UTIL_H

View file

@ -0,0 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "qt_progress_dialog.h"

View file

@ -0,0 +1,47 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#ifndef QT_PROGRESS_DIALOG_H
#define QT_PROGRESS_DIALOG_H
#include <QWindow>
#ifdef YUZU_QT_WIDGETS
#include <QProgressDialog>
#endif
namespace QtCommon::Frontend {
#ifdef YUZU_QT_WIDGETS
using QtProgressDialog = QProgressDialog;
// TODO(crueter): QML impl
#else
class QtProgressDialog
{
public:
QtProgressDialog(const QString &labelText,
const QString &cancelButtonText,
int minimum,
int maximum,
QObject *parent = nullptr,
Qt::WindowFlags f = Qt::WindowFlags());
bool wasCanceled() const;
void setWindowModality(Qt::WindowModality modality);
void setMinimumDuration(int durationMs);
void setAutoClose(bool autoClose);
void setAutoReset(bool autoReset);
public slots:
void setLabelText(QString &text);
void setRange(int min, int max);
void setValue(int progress);
bool close();
void show();
};
#endif // YUZU_QT_WIDGETS
}
#endif // QT_PROGRESS_DIALOG_H

View file

@ -29,10 +29,10 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent)
#define INSERT(SETTINGS, ID, NAME, TOOLTIP) \ #define INSERT(SETTINGS, ID, NAME, TOOLTIP) \
translations->insert(std::pair{SETTINGS::values.ID.Id(), std::pair{(NAME), (TOOLTIP)}}) translations->insert(std::pair{SETTINGS::values.ID.Id(), std::pair{(NAME), (TOOLTIP)}})
// A setting can be ignored by giving it a blank name // A setting can be ignored by giving it a blank name
// Applets // Applets
INSERT(Settings, cabinet_applet_mode, tr("Amiibo editor"), QString()); INSERT(Settings, cabinet_applet_mode, tr("Amiibo editor"), QString());
INSERT(Settings, controller_applet_mode, tr("Controller configuration"), QString()); INSERT(Settings, controller_applet_mode, tr("Controller configuration"), QString());
INSERT(Settings, data_erase_applet_mode, tr("Data erase"), QString()); INSERT(Settings, data_erase_applet_mode, tr("Data erase"), QString());
INSERT(Settings, error_applet_mode, tr("Error"), QString()); INSERT(Settings, error_applet_mode, tr("Error"), QString());
@ -407,12 +407,6 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent)
"their resolution, details and supported controllers and depending on this setting.\n" "their resolution, details and supported controllers and depending on this setting.\n"
"Setting to Handheld can help improve performance for low end systems.")); "Setting to Handheld can help improve performance for low end systems."));
INSERT(Settings, current_user, QString(), QString()); INSERT(Settings, current_user, QString(), QString());
INSERT(Settings, disable_nca_verification, tr("Disable NCA Verification"),
tr("Disables integrity verification of NCA content archives."
"\nThis may improve loading speed but risks data corruption or invalid files going "
"undetected.\n"
"Is necessary to make games and updates work that needs firmware 20+."));
INSERT(Settings, hide_nca_verification_popup, QString(), QString());
// Controls // Controls

View file

@ -650,10 +650,10 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri
// TODO: Implement shortcut creation for macOS // TODO: Implement shortcut creation for macOS
#if !defined(__APPLE__) #if !defined(__APPLE__)
connect(create_desktop_shortcut, &QAction::triggered, [this, program_id, path]() { connect(create_desktop_shortcut, &QAction::triggered, [this, program_id, path]() {
emit CreateShortcut(program_id, path, GameListShortcutTarget::Desktop); emit CreateShortcut(program_id, path, QtCommon::Game::ShortcutTarget::Desktop);
}); });
connect(create_applications_menu_shortcut, &QAction::triggered, [this, program_id, path]() { connect(create_applications_menu_shortcut, &QAction::triggered, [this, program_id, path]() {
emit CreateShortcut(program_id, path, GameListShortcutTarget::Applications); emit CreateShortcut(program_id, path, QtCommon::Game::ShortcutTarget::Applications);
}); });
#endif #endif
connect(properties, &QAction::triggered, connect(properties, &QAction::triggered,

View file

@ -52,11 +52,6 @@ enum class DumpRomFSTarget {
SDMC, SDMC,
}; };
enum class GameListShortcutTarget {
Desktop,
Applications,
};
class GameList : public QWidget { class GameList : public QWidget {
Q_OBJECT Q_OBJECT
@ -113,7 +108,7 @@ signals:
void VerifyIntegrityRequested(const std::string& game_path); void VerifyIntegrityRequested(const std::string& game_path);
void CopyTIDRequested(u64 program_id); void CopyTIDRequested(u64 program_id);
void CreateShortcut(u64 program_id, const std::string& game_path, void CreateShortcut(u64 program_id, const std::string& game_path,
GameListShortcutTarget target); const QtCommon::Game::ShortcutTarget target);
void NavigateToGamedbEntryRequested(u64 program_id, void NavigateToGamedbEntryRequested(u64 program_id,
const CompatibilityList& compatibility_list); const CompatibilityList& compatibility_list);
void OpenPerGameGeneralRequested(const std::string& file); void OpenPerGameGeneralRequested(const std::string& file);

View file

@ -1,21 +1,22 @@
// 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 "core/hle/service/am/applet_manager.h"
#include "core/loader/nca.h"
#include "core/tools/renderdoc.h"
#include "frontend_common/firmware_manager.h"
#include "qt_common/qt_common.h"
#include "qt_common/qt_content_util.h"
#include "qt_common/qt_game_util.h"
#include "qt_common/qt_meta.h"
#include "qt_common/qt_path_util.h"
#include "qt_common/qt_progress_dialog.h"
#include <clocale> #include <clocale>
#include <cmath> #include <cmath>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <memory> #include <memory>
#include <thread> #include <thread>
#include "core/hle/service/am/applet_manager.h"
#include "core/loader/nca.h"
#include "core/tools/renderdoc.h"
#include "frontend_common/firmware_manager.h"
#include "qt_common/qt_common.h"
#include "qt_common/qt_game_util.h"
#include "qt_common/qt_path_util.h"
#include "qt_common/qt_meta.h"
#include "qt_common/qt_content_util.h"
#ifdef __APPLE__ #ifdef __APPLE__
#include <unistd.h> // for chdir #include <unistd.h> // for chdir
@ -398,8 +399,7 @@ inline static bool isDarkMode() {
GMainWindow::GMainWindow(bool has_broken_vulkan) GMainWindow::GMainWindow(bool has_broken_vulkan)
: ui{std::make_unique<Ui::MainWindow>()}, : ui{std::make_unique<Ui::MainWindow>()},
input_subsystem{std::make_shared<InputCommon::InputSubsystem>()}, user_data_migrator{this}, input_subsystem{std::make_shared<InputCommon::InputSubsystem>()}, user_data_migrator{this} {
provider{std::make_unique<FileSys::ManualContentProvider>()} {
QtCommon::Init(this); QtCommon::Init(this);
Common::FS::CreateEdenPaths(); Common::FS::CreateEdenPaths();
@ -543,7 +543,7 @@ GMainWindow::GMainWindow(bool has_broken_vulkan)
QtCommon::system->SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); QtCommon::system->SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
QtCommon::system->RegisterContentProvider(FileSys::ContentProviderUnionSlot::FrontendManual, QtCommon::system->RegisterContentProvider(FileSys::ContentProviderUnionSlot::FrontendManual,
provider.get()); QtCommon::provider.get());
QtCommon::system->GetFileSystemController().CreateFactories(*QtCommon::vfs); QtCommon::system->GetFileSystemController().CreateFactories(*QtCommon::vfs);
// Remove cached contents generated during the previous session // Remove cached contents generated during the previous session
@ -1091,7 +1091,7 @@ void GMainWindow::InitializeWidgets() {
render_window = new GRenderWindow(this, emu_thread.get(), input_subsystem, *QtCommon::system); render_window = new GRenderWindow(this, emu_thread.get(), input_subsystem, *QtCommon::system);
render_window->hide(); render_window->hide();
game_list = new GameList(QtCommon::vfs, provider.get(), *play_time_manager, *QtCommon::system, this); game_list = new GameList(QtCommon::vfs, QtCommon::provider.get(), *play_time_manager, *QtCommon::system, this);
ui->horizontalLayout->addWidget(game_list); ui->horizontalLayout->addWidget(game_list);
game_list_placeholder = new GameListPlaceholder(this); game_list_placeholder = new GameListPlaceholder(this);
@ -1913,10 +1913,6 @@ bool GMainWindow::LoadROM(const QString& filename, Service::AM::FrontendAppletPa
return false; return false;
} }
if (!OnCheckNcaVerification()) {
return false;
}
/** Exec */ /** Exec */
const Core::SystemResultStatus result{ const Core::SystemResultStatus result{
QtCommon::system->Load(*render_window, filename.toStdString(), params)}; QtCommon::system->Load(*render_window, filename.toStdString(), params)};
@ -2023,7 +2019,7 @@ void GMainWindow::ConfigureFilesystemProvider(const std::string& filepath) {
u64 program_id = 0; u64 program_id = 0;
const auto res2 = loader->ReadProgramId(program_id); const auto res2 = loader->ReadProgramId(program_id);
if (res2 == Loader::ResultStatus::Success && file_type == Loader::FileType::NCA) { if (res2 == Loader::ResultStatus::Success && file_type == Loader::FileType::NCA) {
provider->AddEntry(FileSys::TitleType::Application, QtCommon::provider->AddEntry(FileSys::TitleType::Application,
FileSys::GetCRTypeFromNCAType(FileSys::NCA{file}.GetType()), program_id, FileSys::GetCRTypeFromNCAType(FileSys::NCA{file}.GetType()), program_id,
file); file);
} else if (res2 == Loader::ResultStatus::Success && } else if (res2 == Loader::ResultStatus::Success &&
@ -2033,7 +2029,7 @@ void GMainWindow::ConfigureFilesystemProvider(const std::string& filepath) {
: FileSys::XCI{file}.GetSecurePartitionNSP(); : FileSys::XCI{file}.GetSecurePartitionNSP();
for (const auto& title : nsp->GetNCAs()) { for (const auto& title : nsp->GetNCAs()) {
for (const auto& entry : title.second) { for (const auto& entry : title.second) {
provider->AddEntry(entry.first.first, entry.first.second, title.first, QtCommon::provider->AddEntry(entry.first.first, entry.first.second, title.first,
entry.second->GetBaseFile()); entry.second->GetBaseFile());
} }
} }
@ -2761,18 +2757,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
// END // END
void GMainWindow::OnGameListVerifyIntegrity(const std::string& game_path) { void GMainWindow::OnGameListVerifyIntegrity(const std::string& game_path) {
QProgressDialog progress(tr("Verifying integrity..."), tr("Cancel"), 0, 100, this); QtCommon::Content::VerifyGameContents(game_path);
progress.setWindowModality(Qt::WindowModal);
progress.setMinimumDuration(100);
progress.setAutoClose(false);
progress.setAutoReset(false);
const auto QtProgressCallback = [&](size_t total_size, size_t processed_size) {
progress.setValue(static_cast<int>((processed_size * 100) / total_size));
return progress.wasCanceled();
};
QtCommon::Content::VerifyGameContents(game_path, QtProgressCallback);
} }
void GMainWindow::OnGameListCopyTID(u64 program_id) { void GMainWindow::OnGameListCopyTID(u64 program_id) {
@ -2793,44 +2778,12 @@ void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id,
QUrl(QStringLiteral("https://eden-emulator.github.io/game/") + directory)); QUrl(QStringLiteral("https://eden-emulator.github.io/game/") + directory));
} }
// Messages in pre-defined message boxes for less code spaghetti
// TODO(crueter): Still need to decide what to do re: message boxes w/ qml
bool GMainWindow::CreateShortcutMessagesGUI(QWidget* parent, int imsg, const QString& game_title) {
int result = 0;
QMessageBox::StandardButtons buttons;
switch (imsg) {
case GMainWindow::CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES:
buttons = QMessageBox::Yes | QMessageBox::No;
result =
QMessageBox::information(parent, tr("Create Shortcut"),
tr("Do you want to launch the game in fullscreen?"), buttons);
return result == QMessageBox::Yes;
case GMainWindow::CREATE_SHORTCUT_MSGBOX_SUCCESS:
QMessageBox::information(parent, tr("Create Shortcut"),
tr("Successfully created a shortcut to %1").arg(game_title));
return false;
case GMainWindow::CREATE_SHORTCUT_MSGBOX_APPVOLATILE_WARNING:
buttons = QMessageBox::StandardButton::Ok | QMessageBox::StandardButton::Cancel;
result =
QMessageBox::warning(this, tr("Create Shortcut"),
tr("This will create a shortcut to the current AppImage. This may "
"not work well if you update. Continue?"),
buttons);
return result == QMessageBox::Ok;
default:
buttons = QMessageBox::Ok;
QMessageBox::critical(parent, tr("Create Shortcut"),
tr("Failed to create a shortcut to %1").arg(game_title), buttons);
return false;
}
}
void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& game_path, void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& game_path,
GameListShortcutTarget target) { const QtCommon::Game::ShortcutTarget target) {
// Create shortcut // Create shortcut
std::string arguments = fmt::format("-g \"{:s}\"", game_path); std::string arguments = fmt::format("-g \"{:s}\"", game_path);
CreateShortcut(game_path, program_id, "", target, arguments, true); QtCommon::Game::CreateShortcut(game_path, program_id, "", target, arguments, true);
} }
void GMainWindow::OnGameListOpenDirectory(const QString& directory) { void GMainWindow::OnGameListOpenDirectory(const QString& directory) {
@ -3884,74 +3837,11 @@ void GMainWindow::OnOpenLogFolder()
} }
void GMainWindow::OnVerifyInstalledContents() { void GMainWindow::OnVerifyInstalledContents() {
// Initialize a progress dialog. QtCommon::Content::VerifyInstalledContents();
QProgressDialog progress(tr("Verifying integrity..."), tr("Cancel"), 0, 100, this);
progress.setWindowModality(Qt::WindowModal);
progress.setMinimumDuration(100);
progress.setAutoClose(false);
progress.setAutoReset(false);
// Declare progress callback.
auto QtProgressCallback = [&](size_t total_size, size_t processed_size) {
progress.setValue(static_cast<int>((processed_size * 100) / total_size));
return progress.wasCanceled();
};
const std::vector<std::string> result =
ContentManager::VerifyInstalledContents(*QtCommon::system, *provider, QtProgressCallback);
progress.close();
if (result.empty()) {
QMessageBox::information(this, tr("Integrity verification succeeded!"),
tr("The operation completed successfully."));
} else {
const auto failed_names =
QString::fromStdString(fmt::format("{}", fmt::join(result, "\n")));
QMessageBox::critical(
this, tr("Integrity verification failed!"),
tr("Verification failed for the following files:\n\n%1").arg(failed_names));
}
} }
void GMainWindow::InstallFirmware(const QString& location, bool recursive) { void GMainWindow::InstallFirmware(const QString& location, bool recursive) {
QProgressDialog progress(tr("Installing Firmware..."), tr("Cancel"), 0, 100, this); QtCommon::Content::InstallFirmware(location, recursive);
progress.setWindowModality(Qt::WindowModal);
progress.setMinimumDuration(100);
progress.setAutoClose(false);
progress.setAutoReset(false);
progress.show();
// Declare progress callback.
auto QtProgressCallback = [&](size_t total_size, size_t processed_size) {
progress.setValue(static_cast<int>((processed_size * 100) / total_size));
return progress.wasCanceled();
};
auto result = QtCommon::Content::InstallFirmware(location, recursive, QtProgressCallback, QtCommon::vfs.get());
progress.close();
if (result != QtCommon::Content::FirmwareInstallResult::Success) return;
auto VerifyFirmwareCallback = [&](size_t total_size, size_t processed_size) {
progress.setValue(90 + static_cast<int>((processed_size * 10) / total_size));
return progress.wasCanceled();
};
auto results =
ContentManager::VerifyInstalledContents(*QtCommon::system, *provider, VerifyFirmwareCallback, true);
if (results.size() > 0) {
const auto failed_names =
QString::fromStdString(fmt::format("{}", fmt::join(results, "\n")));
progress.close();
QMessageBox::critical(
this, tr("Firmware integrity verification failed!"),
tr("Verification failed for the following files:\n\n%1").arg(failed_names));
return;
}
progress.close();
OnCheckFirmwareDecryption(); OnCheckFirmwareDecryption();
} }
@ -4190,8 +4080,6 @@ void GMainWindow::OnHomeMenu() {
break; break;
} }
// TODO(crueter): So much of this crap is common to qt that I should just move it all tbh
constexpr u64 QLaunchId = static_cast<u64>(Service::AM::AppletProgramId::QLaunch); constexpr u64 QLaunchId = static_cast<u64>(Service::AM::AppletProgramId::QLaunch);
auto bis_system = QtCommon::system->GetFileSystemController().GetSystemNANDContents(); auto bis_system = QtCommon::system->GetFileSystemController().GetSystemNANDContents();
@ -4233,164 +4121,11 @@ void GMainWindow::OnInitialSetup() {
} }
void GMainWindow::OnCreateHomeMenuDesktopShortcut() { void GMainWindow::OnCreateHomeMenuDesktopShortcut() {
OnCreateHomeMenuShortcut(GameListShortcutTarget::Desktop); QtCommon::Game::CreateHomeMenuShortcut(QtCommon::Game::ShortcutTarget::Desktop);
} }
void GMainWindow::OnCreateHomeMenuApplicationMenuShortcut() { void GMainWindow::OnCreateHomeMenuApplicationMenuShortcut() {
OnCreateHomeMenuShortcut(GameListShortcutTarget::Applications); QtCommon::Game::CreateHomeMenuShortcut(QtCommon::Game::ShortcutTarget::Applications);
}
std::filesystem::path GMainWindow::GetEdenCommand() {
std::filesystem::path command;
QString appimage = QString::fromLocal8Bit(getenv("APPIMAGE"));
if (!appimage.isEmpty()) {
command = std::filesystem::path{appimage.toStdString()};
} else {
const QStringList args = QApplication::arguments();
command = args[0].toStdString();
}
// If relative path, make it an absolute path
if (command.c_str()[0] == '.') {
command = Common::FS::GetCurrentDir() / command;
}
return command;
}
std::filesystem::path GMainWindow::GetShortcutPath(GameListShortcutTarget target) {
std::filesystem::path shortcut_path{};
if (target == GameListShortcutTarget::Desktop) {
shortcut_path =
QStandardPaths::writableLocation(QStandardPaths::DesktopLocation).toStdString();
} else if (target == GameListShortcutTarget::Applications) {
shortcut_path =
QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation).toStdString();
}
return shortcut_path;
}
// TODO(crueter): Migrate
void GMainWindow::CreateShortcut(const std::string &game_path, const u64 program_id, const std::string& game_title_, GameListShortcutTarget target, std::string arguments_, const bool needs_title) {
// Get path to Eden executable
std::filesystem::path command = GetEdenCommand();
// Shortcut path
std::filesystem::path shortcut_path = GetShortcutPath(target);
if (!std::filesystem::exists(shortcut_path)) {
GMainWindow::CreateShortcutMessagesGUI(
this, GMainWindow::CREATE_SHORTCUT_MSGBOX_ERROR,
QString::fromStdString(shortcut_path.generic_string()));
LOG_ERROR(Frontend, "Invalid shortcut target {}", shortcut_path.generic_string());
return;
}
const FileSys::PatchManager pm{program_id, QtCommon::system->GetFileSystemController(),
QtCommon::system->GetContentProvider()};
const auto control = pm.GetControlMetadata();
const auto loader =
Loader::GetLoader(*QtCommon::system, QtCommon::vfs->OpenFile(game_path, FileSys::OpenMode::Read));
std::string game_title{game_title_};
// Delete illegal characters from title
if (needs_title) {
game_title = fmt::format("{:016X}", program_id);
if (control.first != nullptr) {
game_title = control.first->GetApplicationName();
} else {
loader->ReadTitle(game_title);
}
}
const std::string illegal_chars = "<>:\"/\\|?*.";
for (auto it = game_title.rbegin(); it != game_title.rend(); ++it) {
if (illegal_chars.find(*it) != std::string::npos) {
game_title.erase(it.base() - 1);
}
}
const QString qgame_title = QString::fromStdString(game_title);
// Get icon from game file
std::vector<u8> icon_image_file{};
if (control.second != nullptr) {
icon_image_file = control.second->ReadAllBytes();
} else if (loader->ReadIcon(icon_image_file) != Loader::ResultStatus::Success) {
LOG_WARNING(Frontend, "Could not read icon from {:s}", game_path);
}
QImage icon_data =
QImage::fromData(icon_image_file.data(), static_cast<int>(icon_image_file.size()));
std::filesystem::path out_icon_path;
if (QtCommon::Game::MakeShortcutIcoPath(program_id, game_title, out_icon_path)) {
if (!SaveIconToFile(out_icon_path, icon_data)) {
LOG_ERROR(Frontend, "Could not write icon to file");
}
} else {
QMessageBox::critical(
this, tr("Create Icon"),
tr("Cannot create icon file. Path \"%1\" does not exist and cannot be created.")
.arg(QString::fromStdString(out_icon_path.string())),
QMessageBox::StandardButton::Ok);
}
#if defined(__unix__) && !defined(__APPLE__) && !defined(__ANDROID__)
// Special case for AppImages
// Warn once if we are making a shortcut to a volatile AppImage
if (command.string().ends_with(".AppImage") && !UISettings::values.shortcut_already_warned) {
if (!GMainWindow::CreateShortcutMessagesGUI(
this, GMainWindow::CREATE_SHORTCUT_MSGBOX_APPVOLATILE_WARNING, qgame_title)) {
return;
}
UISettings::values.shortcut_already_warned = true;
}
#endif
// Create shortcut
std::string arguments{arguments_};
if (GMainWindow::CreateShortcutMessagesGUI(
this, GMainWindow::CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES, qgame_title)) {
arguments = "-f " + arguments;
}
const std::string comment = fmt::format("Start {:s} with the Eden Emulator", game_title);
const std::string categories = "Game;Emulator;Qt;";
const std::string keywords = "Switch;Nintendo;";
if (QtCommon::Game::CreateShortcutLink(shortcut_path, comment, out_icon_path, command,
arguments, categories, keywords, game_title)) {
GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_SUCCESS,
qgame_title);
return;
}
GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_ERROR,
qgame_title);
}
void GMainWindow::OnCreateHomeMenuShortcut(GameListShortcutTarget target) {
constexpr u64 QLaunchId = static_cast<u64>(Service::AM::AppletProgramId::QLaunch);
auto bis_system = QtCommon::system->GetFileSystemController().GetSystemNANDContents();
if (!bis_system) {
QMessageBox::warning(this, tr("No firmware available"),
tr("Please install firmware to use the home menu."));
return;
}
auto qlaunch_nca = bis_system->GetEntry(QLaunchId, FileSys::ContentRecordType::Program);
if (!qlaunch_nca) {
QMessageBox::warning(this, tr("Home Menu Applet"),
tr("Home Menu is not available. Please reinstall firmware."));
return;
}
auto qlaunch_applet_nca = bis_system->GetEntry(QLaunchId, FileSys::ContentRecordType::Program);
const auto game_path = qlaunch_applet_nca->GetFullPath();
// TODO(crueter): Make this use the Eden icon
CreateShortcut(game_path, QLaunchId, "Switch Home Menu", target, "-qlaunch", false);
} }
void GMainWindow::OnCaptureScreenshot() { void GMainWindow::OnCaptureScreenshot() {
@ -4718,7 +4453,6 @@ void GMainWindow::OnMouseActivity() {
} }
void GMainWindow::OnCheckFirmwareDecryption() { void GMainWindow::OnCheckFirmwareDecryption() {
QtCommon::system->GetFileSystemController().CreateFactories(*QtCommon::vfs);
if (!ContentManager::AreKeysPresent()) { if (!ContentManager::AreKeysPresent()) {
QMessageBox::warning(this, tr("Derivation Components Missing"), QMessageBox::warning(this, tr("Derivation Components Missing"),
tr("Encryption keys are missing.")); tr("Encryption keys are missing."));
@ -4727,41 +4461,6 @@ void GMainWindow::OnCheckFirmwareDecryption() {
UpdateMenuState(); UpdateMenuState();
} }
bool GMainWindow::OnCheckNcaVerification() {
if (!Settings::values.disable_nca_verification.GetValue())
return true;
const bool currently_hidden = Settings::values.hide_nca_verification_popup.GetValue();
LOG_INFO(Frontend, "NCA Verification is disabled. Popup State={}", currently_hidden);
if (currently_hidden)
return true;
QMessageBox msgbox(this);
msgbox.setWindowTitle(tr("NCA Verification Disabled"));
msgbox.setText(tr("NCA Verification is disabled.\n"
"This is required to run new games and updates.\n"
"Running without verification can cause instability or crashes if NCA files "
"are corrupt, modified, or tampered.\n"
"If unsure, re-enable verification in Eden's Settings and use firmware "
"version 19.0.1 or below."));
msgbox.setIcon(QMessageBox::Warning);
msgbox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
msgbox.setDefaultButton(QMessageBox::Ok);
QCheckBox* cb = new QCheckBox(tr("Don't show again"), &msgbox);
cb->setChecked(currently_hidden);
msgbox.setCheckBox(cb);
int result = msgbox.exec();
const bool hide = cb->isChecked();
if (hide != currently_hidden) {
Settings::values.hide_nca_verification_popup.SetValue(hide);
}
return result == static_cast<int>(QMessageBox::Ok);
}
bool GMainWindow::CheckFirmwarePresence() { bool GMainWindow::CheckFirmwarePresence() {
return FirmwareManager::CheckFirmwarePresence(*QtCommon::system.get()); return FirmwareManager::CheckFirmwarePresence(*QtCommon::system.get());
} }

View file

@ -54,7 +54,6 @@ class QSlider;
class QHBoxLayout; class QHBoxLayout;
class WaitTreeWidget; class WaitTreeWidget;
enum class GameListOpenTarget; enum class GameListOpenTarget;
enum class GameListShortcutTarget;
enum class DumpRomFSTarget; enum class DumpRomFSTarget;
class GameListPlaceholder; class GameListPlaceholder;
@ -161,13 +160,6 @@ class GMainWindow : public QMainWindow {
/// Max number of recently loaded items to keep track of /// Max number of recently loaded items to keep track of
static const int max_recent_files_item = 10; static const int max_recent_files_item = 10;
enum {
CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES,
CREATE_SHORTCUT_MSGBOX_SUCCESS,
CREATE_SHORTCUT_MSGBOX_ERROR,
CREATE_SHORTCUT_MSGBOX_APPVOLATILE_WARNING,
};
public: public:
void filterBarSetChecked(bool state); void filterBarSetChecked(bool state);
void UpdateUITheme(); void UpdateUITheme();
@ -177,7 +169,7 @@ public:
bool DropAction(QDropEvent* event); bool DropAction(QDropEvent* event);
void AcceptDropEvent(QDropEvent* event); void AcceptDropEvent(QDropEvent* event);
std::filesystem::path GetShortcutPath(GameListShortcutTarget target); std::filesystem::path GetShortcutPath(QtCommon::Game::ShortcutTarget target);
signals: signals:
@ -358,8 +350,9 @@ private slots:
void OnGameListCopyTID(u64 program_id); void OnGameListCopyTID(u64 program_id);
void OnGameListNavigateToGamedbEntry(u64 program_id, void OnGameListNavigateToGamedbEntry(u64 program_id,
const CompatibilityList& compatibility_list); const CompatibilityList& compatibility_list);
void OnGameListCreateShortcut(u64 program_id, const std::string& game_path, void OnGameListCreateShortcut(u64 program_id,
GameListShortcutTarget target); const std::string& game_path,
const QtCommon::Game::ShortcutTarget target);
void OnGameListOpenDirectory(const QString& directory); void OnGameListOpenDirectory(const QString& directory);
void OnGameListAddDirectory(); void OnGameListAddDirectory();
void OnGameListShowList(bool show); void OnGameListShowList(bool show);
@ -416,7 +409,6 @@ private slots:
void OnInitialSetup(); void OnInitialSetup();
void OnCreateHomeMenuDesktopShortcut(); void OnCreateHomeMenuDesktopShortcut();
void OnCreateHomeMenuApplicationMenuShortcut(); void OnCreateHomeMenuApplicationMenuShortcut();
void OnCreateHomeMenuShortcut(GameListShortcutTarget target);
void OnCaptureScreenshot(); void OnCaptureScreenshot();
void OnCheckFirmwareDecryption(); void OnCheckFirmwareDecryption();
void OnLanguageChanged(const QString& locale); void OnLanguageChanged(const QString& locale);
@ -537,9 +529,6 @@ private:
QString startup_icon_theme; QString startup_icon_theme;
// FS
std::unique_ptr<FileSys::ManualContentProvider> provider;
// Debugger panes // Debugger panes
WaitTreeWidget* waitTreeWidget; WaitTreeWidget* waitTreeWidget;
ControllerDialog* controller_dialog; ControllerDialog* controller_dialog;
@ -586,7 +575,7 @@ private:
void CreateShortcut(const std::string& game_path, void CreateShortcut(const std::string& game_path,
const u64 program_id, const u64 program_id,
const std::string& game_title, const std::string& game_title,
GameListShortcutTarget target, QtCommon::Game::ShortcutTarget target,
std::string arguments, std::string arguments,
const bool needs_title); const bool needs_title);