diff --git a/.ci/license-header.sh b/.ci/license-header.sh
index d98f42fc97..3d4929d1c1 100755
--- a/.ci/license-header.sh
+++ b/.ci/license-header.sh
@@ -15,7 +15,6 @@ FILES=`git diff --name-only $BASE`
#FILES=$(git diff --name-only master)
-echo $FILES
echo "Done"
check_header() {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
index 98f342c274..b383cc147f 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
@@ -232,8 +232,6 @@ class GameAdapter(private val activity: AppCompatActivity) :
binding.root.findNavController().navigate(action)
}
- val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
-
if (NativeLibrary.gameRequiresFirmware(game.programId) && !NativeLibrary.isFirmwareAvailable()) {
MaterialAlertDialogBuilder(activity)
.setTitle(R.string.loader_requires_firmware)
@@ -248,23 +246,6 @@ class GameAdapter(private val activity: AppCompatActivity) :
}
.setNegativeButton(android.R.string.cancel) { _, _ -> }
.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 {
launch()
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
index 6d4bfd97ac..3c5b9003de 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
@@ -35,7 +35,6 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
RENDERER_SAMPLE_SHADING("sample_shading"),
PICTURE_IN_PICTURE("picture_in_picture"),
USE_CUSTOM_RTC("custom_rtc_enabled"),
- DISABLE_NCA_VERIFICATION("disable_nca_verification"),
BLACK_BACKGROUNDS("black_backgrounds"),
JOYSTICK_REL_CENTER("joystick_rel_center"),
DPAD_SLIDE("dpad_slide"),
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
index 883d8efaef..1f2ba81a73 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
@@ -297,13 +297,6 @@ abstract class SettingsItem(
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(
StringInputSetting(
StringSetting.WEB_TOKEN,
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
index 630bcb0d74..14d62ceec3 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
@@ -210,7 +210,6 @@ class SettingsFragmentPresenter(
add(IntSetting.LANGUAGE_INDEX.key)
add(BooleanSetting.USE_CUSTOM_RTC.key)
add(LongSetting.CUSTOM_RTC.key)
- add(BooleanSetting.DISABLE_NCA_VERIFICATION.key)
add(HeaderSetting(R.string.network))
add(StringSetting.WEB_TOKEN.key)
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index 2c7923d5a3..6e428a1f7f 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -784,9 +784,6 @@
Game Requires Firmware
dump and install firmware, or press "OK" to launch anyways.]]>
- NCA Verification Disabled
- 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.
-
Searching for game...
Game not found for Title ID: %1$s
diff --git a/src/common/settings.h b/src/common/settings.h
index 9d448a2b38..fafd765804 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -626,10 +626,6 @@ struct Values {
true, true, &rng_seed_enabled};
Setting device_name{
linkage, "Eden", "device_name", Category::System, Specialization::Default, true, true};
- SwitchableSetting disable_nca_verification{linkage, true, "disable_nca_verification",
- Category::System, Specialization::Default};
- Setting hide_nca_verification_popup{
- linkage, false, "hide_nca_verification_popup", Category::System, Specialization::Default};
Setting current_user{linkage, 0, "current_user", Category::System};
diff --git a/src/core/file_sys/fssystem/fssystem_bucket_tree.cpp b/src/core/file_sys/fssystem/fssystem_bucket_tree.cpp
index 615a624f4f..71ba458cef 100644
--- a/src/core/file_sys/fssystem/fssystem_bucket_tree.cpp
+++ b/src/core/file_sys/fssystem/fssystem_bucket_tree.cpp
@@ -238,9 +238,7 @@ void BucketTree::Initialize(size_t node_size, s64 end_offset) {
ASSERT(NodeSizeMin <= node_size && node_size <= NodeSizeMax);
ASSERT(Common::IsPowerOfTwo(node_size));
- if (!Settings::values.disable_nca_verification.GetValue()) {
- ASSERT(end_offset > 0);
- }
+ ASSERT(end_offset > 0);
ASSERT(!this->IsInitialized());
m_node_size = node_size;
diff --git a/src/core/file_sys/fssystem/fssystem_nca_file_system_driver.cpp b/src/core/file_sys/fssystem/fssystem_nca_file_system_driver.cpp
index 4cfa5c58f8..4e624ad3f4 100644
--- a/src/core/file_sys/fssystem/fssystem_nca_file_system_driver.cpp
+++ b/src/core/file_sys/fssystem/fssystem_nca_file_system_driver.cpp
@@ -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-License-Identifier: GPL-3.0-or-later
@@ -1296,91 +1299,65 @@ Result NcaFileSystemDriver::CreateIntegrityVerificationStorageImpl(
ASSERT(base_storage != nullptr);
ASSERT(layer_info_offset >= 0);
- if (!Settings::values.disable_nca_verification.GetValue()) {
- // Define storage types.
- using VerificationStorage = HierarchicalIntegrityVerificationStorage;
- using StorageInfo = VerificationStorage::HierarchicalStorageInformation;
+ // Define storage types.
+ using VerificationStorage = HierarchicalIntegrityVerificationStorage;
+ using StorageInfo = VerificationStorage::HierarchicalStorageInformation;
- // Validate the meta info.
- HierarchicalIntegrityVerificationInformation level_hash_info;
- std::memcpy(std::addressof(level_hash_info), std::addressof(meta_info.level_hash_info),
- sizeof(level_hash_info));
+ // Validate the meta info.
+ HierarchicalIntegrityVerificationInformation level_hash_info;
+ std::memcpy(std::addressof(level_hash_info),
+ std::addressof(meta_info.level_hash_info),
+ sizeof(level_hash_info));
- R_UNLESS(IntegrityMinLayerCount <= level_hash_info.max_layers,
- ResultInvalidNcaHierarchicalIntegrityVerificationLayerCount);
- R_UNLESS(level_hash_info.max_layers <= IntegrityMaxLayerCount,
- ResultInvalidNcaHierarchicalIntegrityVerificationLayerCount);
+ R_UNLESS(IntegrityMinLayerCount <= level_hash_info.max_layers,
+ ResultInvalidNcaHierarchicalIntegrityVerificationLayerCount);
+ R_UNLESS(level_hash_info.max_layers <= IntegrityMaxLayerCount,
+ ResultInvalidNcaHierarchicalIntegrityVerificationLayerCount);
- // Get the base storage size.
- s64 base_storage_size = base_storage->GetSize();
+ // Get the base storage size.
+ s64 base_storage_size = base_storage->GetSize();
- // Create storage info.
- StorageInfo storage_info;
- for (s32 i = 0; i < static_cast(level_hash_info.max_layers - 2); ++i) {
- const auto& layer_info = level_hash_info.info[i];
- R_UNLESS(layer_info_offset + layer_info.offset + layer_info.size <= base_storage_size,
- ResultNcaBaseStorageOutOfRangeD);
-
- storage_info[i + 1] = std::make_shared(
- 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,
+ // Create storage info.
+ StorageInfo storage_info;
+ for (s32 i = 0; i < static_cast(level_hash_info.max_layers - 2); ++i) {
+ const auto& layer_info = level_hash_info.info[i];
+ R_UNLESS(layer_info_offset + 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(
- std::move(base_storage), layer_info.size, last_layer_info_offset);
- // Make the integrity romfs storage.
- auto integrity_storage = std::make_shared();
- 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();
- } 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(base_storage, data_li.size, data_off);
- R_UNLESS(data_view != nullptr, ResultAllocationMemoryFailedAllocateShared);
-
- auto passthrough = std::make_shared(std::move(data_view));
- R_UNLESS(passthrough != nullptr, ResultAllocationMemoryFailedAllocateShared);
-
- *out = std::move(passthrough);
- R_SUCCEED();
+ storage_info[i + 1] = std::make_shared(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);
+ 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(std::move(base_storage),
+ layer_info.size,
+ last_layer_info_offset);
+
+ // Make the integrity romfs storage.
+ auto integrity_storage = std::make_shared();
+ 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,
diff --git a/src/qt_common/CMakeLists.txt b/src/qt_common/CMakeLists.txt
index 1acf8344a1..9d292da401 100644
--- a/src/qt_common/CMakeLists.txt
+++ b/src/qt_common/CMakeLists.txt
@@ -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-License-Identifier: GPL-2.0-or-later
+find_package(Qt6 REQUIRED COMPONENTS Core)
find_package(Qt6 REQUIRED COMPONENTS Core)
add_library(qt_common STATIC
@@ -22,6 +26,8 @@ add_library(qt_common STATIC
qt_content_util.h qt_content_util.cpp
qt_rom_util.h qt_rom_util.cpp
qt_applet_util.h qt_applet_util.cpp
+ qt_progress_dialog.h qt_progress_dialog.cpp
+
)
create_target_directory_groups(qt_common)
@@ -33,7 +39,8 @@ endif()
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)
target_include_directories(qt_common PRIVATE ${Qt6Gui_PRIVATE_INCLUDE_DIRS})
diff --git a/src/qt_common/externals/CMakeLists.txt b/src/qt_common/externals/CMakeLists.txt
index 50594a741f..189a52c0a6 100644
--- a/src/qt_common/externals/CMakeLists.txt
+++ b/src/qt_common/externals/CMakeLists.txt
@@ -14,3 +14,7 @@ set_directory_properties(PROPERTIES EXCLUDE_FROM_ALL ON)
# QuaZip
AddJsonPackage(quazip)
+
+# frozen
+# TODO(crueter): Qt String Lookup
+AddJsonPackage(frozen)
diff --git a/src/qt_common/externals/cpmfile.json b/src/qt_common/externals/cpmfile.json
index e3590d0f7f..0b464b95b2 100644
--- a/src/qt_common/externals/cpmfile.json
+++ b/src/qt_common/externals/cpmfile.json
@@ -8,5 +8,12 @@
"options": [
"QUAZIP_INSTALL OFF"
]
+ },
+ "frozen": {
+ "package": "frozen",
+ "repo": "serge-sans-paille/frozen",
+ "sha": "61dce5ae18",
+ "hash": "1ae3d073e659c1f24b2cdd76379c90d6af9e06bc707d285a4fafce05f7a4c9e592ff208c94a9ae0f0d07620b3c6cec191f126b03d70ad4dfa496a86ed5658a6d",
+ "bundled": true
}
}
diff --git a/src/qt_common/qt_common.cpp b/src/qt_common/qt_common.cpp
index 9c20f1e6d9..6be241c740 100644
--- a/src/qt_common/qt_common.cpp
+++ b/src/qt_common/qt_common.cpp
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#include "qt_common.h"
+#include "common/fs/fs.h"
#include
#include
@@ -22,9 +23,15 @@
namespace QtCommon {
-QObject *rootObject = nullptr;
+#ifdef YUZU_QT_WIDGETS
+QWidget* rootObject = nullptr;
+#else
+QObject* rootObject = nullptr;
+#endif
+
std::unique_ptr system = nullptr;
std::shared_ptr vfs = nullptr;
+std::unique_ptr provider = nullptr;
Core::Frontend::WindowSystemType GetWindowSystemType()
{
@@ -81,11 +88,35 @@ const QString tr(const std::string& str)
return QGuiApplication::tr(str.c_str());
}
+#ifdef YUZU_QT_WIDGETS
+void Init(QWidget* root)
+#else
void Init(QObject* root)
+#endif
{
system = std::make_unique();
rootObject = root;
vfs = std::make_unique();
+ provider = std::make_unique();
+}
+
+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
diff --git a/src/qt_common/qt_common.h b/src/qt_common/qt_common.h
index 5f9b5d7de9..a2700427ab 100644
--- a/src/qt_common/qt_common.h
+++ b/src/qt_common/qt_common.h
@@ -4,18 +4,25 @@
#ifndef QT_COMMON_H
#define QT_COMMON_H
-#include
#include
#include "core/core.h"
+#include "core/file_sys/registered_cache.h"
#include
+#include
#include
namespace QtCommon {
+#ifdef YUZU_QT_WIDGETS
+extern QWidget *rootObject;
+#else
extern QObject *rootObject;
+#endif
+
extern std::unique_ptr system;
extern std::shared_ptr vfs;
+extern std::unique_ptr provider;
typedef std::function QtProgressCallback;
@@ -23,10 +30,15 @@ Core::Frontend::WindowSystemType GetWindowSystemType();
Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow *window);
+#ifdef YUZU_QT_WIDGETS
+void Init(QWidget *root);
+#else
void Init(QObject *root);
+#endif
const QString tr(const char *str);
const QString tr(const std::string &str);
+std::filesystem::path GetEdenCommand();
} // namespace QtCommon
#endif
diff --git a/src/qt_common/qt_content_util.cpp b/src/qt_common/qt_content_util.cpp
index 5f8ca14504..e4625aa423 100644
--- a/src/qt_common/qt_content_util.cpp
+++ b/src/qt_common/qt_content_util.cpp
@@ -6,6 +6,7 @@
#include "frontend_common/content_manager.h"
#include "frontend_common/firmware_manager.h"
#include "qt_common/qt_common.h"
+#include "qt_common/qt_progress_dialog.h"
#include "qt_frontend_util.h"
#include
@@ -31,24 +32,34 @@ bool CheckGameFirmware(u64 program_id, QObject* parent)
return true;
}
-FirmwareInstallResult InstallFirmware(const QString& location,
- bool recursive,
- QtProgressCallback callback,
- FileSys::VfsFilesystem* vfs)
+void InstallFirmware(const QString& location, bool recursive)
{
- static constexpr const char* failedTitle = "Firmware Install Failed";
- static constexpr const char* successTitle = "Firmware Install Failed";
- static constexpr QMessageBox::StandardButtons buttons = QMessageBox::Ok;
+ QtCommon::Frontend::QtProgressDialog progress(tr("Installing Firmware..."),
+ tr("Cancel"),
+ 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((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;
FirmwareInstallResult result;
const auto ShowMessage = [&]() {
QtCommon::Frontend::ShowMessage(icon,
failedTitle,
- GetFirmwareInstallResultString(result),
- buttons,
- rootObject);
+ GetFirmwareInstallResultString(result));
};
LOG_INFO(Frontend, "Installing firmware from {}", location.toStdString());
@@ -57,7 +68,7 @@ FirmwareInstallResult InstallFirmware(const QString& location,
// there.)
std::filesystem::path firmware_source_path = location.toStdString();
if (!Common::FS::IsDir(firmware_source_path)) {
- return FirmwareInstallResult::NoOp;
+ return;
}
std::vector out;
@@ -86,7 +97,7 @@ FirmwareInstallResult InstallFirmware(const QString& location,
result = FirmwareInstallResult::NoNCAs;
icon = QMessageBox::Warning;
ShowMessage();
- return result;
+ return;
}
// 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;
icon = QMessageBox::Critical;
ShowMessage();
- return result;
+ return;
}
LOG_INFO(Frontend,
@@ -127,7 +138,7 @@ FirmwareInstallResult InstallFirmware(const QString& location,
result = FirmwareInstallResult::FailedCorrupted;
icon = QMessageBox::Warning;
ShowMessage();
- return result;
+ return;
}
}
@@ -135,12 +146,34 @@ FirmwareInstallResult InstallFirmware(const QString& location,
result = FirmwareInstallResult::FailedCopy;
icon = QMessageBox::Critical;
ShowMessage();
- return result;
+ return;
}
// Re-scan VFS for the newly placed firmware files.
system->GetFileSystemController().CreateFactories(*vfs);
+ auto VerifyFirmwareCallback = [&](size_t total_size, size_t processed_size) {
+ progress.setValue(90 + static_cast((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 firmware_data = pair.first;
const std::string display_version(firmware_data.display_version.data());
@@ -149,9 +182,7 @@ FirmwareInstallResult InstallFirmware(const QString& location,
QtCommon::Frontend::Information(rootObject,
tr(successTitle),
tr(GetFirmwareInstallResultString(result))
- .arg(QString::fromStdString(display_version)),
- buttons);
- return result;
+ .arg(QString::fromStdString(display_version)));
}
QString UnzipFirmwareToTmp(const QString& location)
@@ -179,8 +210,23 @@ QString UnzipFirmwareToTmp(const QString& location)
}
// 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((processed_size * 100) / total_size));
+ return progress.wasCanceled();
+ };
+
const auto result = ContentManager::VerifyGameContents(*system, game_path, callback);
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((processed_size * 100) / total_size));
+ return progress.wasCanceled();
+ };
+
+ const std::vector 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
diff --git a/src/qt_common/qt_content_util.h b/src/qt_common/qt_content_util.h
index b401bdf1e3..b572c1c4a3 100644
--- a/src/qt_common/qt_content_util.h
+++ b/src/qt_common/qt_content_util.h
@@ -6,8 +6,6 @@
#include
#include "common/common_types.h"
-#include "core/core.h"
-#include "qt_common/qt_common.h"
namespace QtCommon::Content {
@@ -37,11 +35,7 @@ inline constexpr const char *GetFirmwareInstallResultString(FirmwareInstallResul
return FIRMWARE_RESULTS.at(static_cast(result));
}
-FirmwareInstallResult InstallFirmware(
- const QString &location,
- bool recursive,
- QtProgressCallback callback,
- FileSys::VfsFilesystem *vfs);
+void InstallFirmware(const QString &location, bool recursive);
QString UnzipFirmwareToTmp(const QString &location);
@@ -49,6 +43,7 @@ QString UnzipFirmwareToTmp(const QString &location);
void InstallKeys();
// Content //
-void VerifyGameContents(const std::string &game_path, QtProgressCallback callback);
+void VerifyGameContents(const std::string &game_path);
+void VerifyInstalledContents();
}
#endif // QT_CONTENT_UTIL_H
diff --git a/src/qt_common/qt_frontend_util.cpp b/src/qt_common/qt_frontend_util.cpp
index d357c11338..d519669ad5 100644
--- a/src/qt_common/qt_frontend_util.cpp
+++ b/src/qt_common/qt_frontend_util.cpp
@@ -10,11 +10,8 @@
namespace QtCommon::Frontend {
-QMessageBox::StandardButton ShowMessage(QMessageBox::Icon icon,
- const QString &title,
- const QString &text,
- QMessageBox::StandardButtons buttons,
- QObject *parent)
+StandardButton ShowMessage(
+ Icon icon, const QString &title, const QString &text, StandardButtons buttons, QObject *parent)
{
#ifdef YUZU_QT_WIDGETS
QMessageBox *box = new QMessageBox(icon, title, text, buttons, (QWidget *) parent);
diff --git a/src/qt_common/qt_frontend_util.h b/src/qt_common/qt_frontend_util.h
index 7f43286430..f86b9e1357 100644
--- a/src/qt_common/qt_frontend_util.h
+++ b/src/qt_common/qt_frontend_util.h
@@ -5,12 +5,12 @@
#define QT_FRONTEND_UTIL_H
#include
-#include
#include "qt_common/qt_common.h"
#ifdef YUZU_QT_WIDGETS
#include
#include
+#include
#endif
/**
@@ -23,6 +23,11 @@ Q_NAMESPACE
#ifdef YUZU_QT_WIDGETS
using Options = QFileDialog::Options;
using Option = QFileDialog::Option;
+
+using StandardButton = QMessageBox::StandardButton;
+using StandardButtons = QMessageBox::StandardButtons;
+
+using Icon = QMessageBox::Icon;
#else
enum Option {
ShowDirsOnly = 0x00000001,
@@ -36,44 +41,96 @@ enum Option {
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,
+ 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
// TODO(crueter) widgets-less impl, choices et al.
-QMessageBox::StandardButton ShowMessage(QMessageBox::Icon icon,
+StandardButton ShowMessage(Icon icon,
const QString &title,
const QString &text,
- QMessageBox::StandardButtons buttons = QMessageBox::NoButton,
+ StandardButtons buttons = StandardButton::NoButton,
QObject *parent = nullptr);
#define UTIL_OVERRIDES(level) \
- inline QMessageBox::StandardButton level(QObject *parent, \
+ inline StandardButton level(QObject *parent, \
const QString &title, \
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 *text, \
- QMessageBox::StandardButtons buttons \
- = QMessageBox::Ok) \
+ StandardButtons buttons \
+ = 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, \
- QMessageBox::StandardButtons buttons \
- = QMessageBox::Ok) \
+ StandardButtons buttons \
+ = 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, \
- QMessageBox::StandardButtons buttons \
- = QMessageBox::Ok) \
+ StandardButtons buttons \
+ = StandardButton::Ok) \
{ \
- return ShowMessage(QMessageBox::level, title, text, buttons, rootObject); \
+ return ShowMessage(Icon::level, title, text, buttons, rootObject); \
}
UTIL_OVERRIDES(Information)
diff --git a/src/qt_common/qt_game_util.cpp b/src/qt_common/qt_game_util.cpp
index 488eb8c971..292eefa224 100644
--- a/src/qt_common/qt_game_util.cpp
+++ b/src/qt_common/qt_game_util.cpp
@@ -5,12 +5,15 @@
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
#include "core/file_sys/savedata_factory.h"
+#include "core/hle/service/am/am_types.h"
#include "frontend_common/content_manager.h"
#include "qt_common.h"
#include "qt_common/uisettings.h"
#include "qt_frontend_util.h"
+#include "yuzu/util/util.h"
#include
+#include
#include
#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 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(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(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
diff --git a/src/qt_common/qt_game_util.h b/src/qt_common/qt_game_util.h
index 9861910833..2872734390 100644
--- a/src/qt_common/qt_game_util.h
+++ b/src/qt_common/qt_game_util.h
@@ -5,6 +5,7 @@
#define QT_GAME_UTIL_H
#include
+#include
#include "common/fs/path_util.h"
#include
@@ -24,6 +25,18 @@ enum class GameListRemoveTarget {
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,
const std::string& comment,
const std::filesystem::path& icon_path,
@@ -57,6 +70,17 @@ void RemoveCacheStorage(u64 program_id, FileSys::VfsFilesystem* vfs);
// Metadata //
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
diff --git a/src/qt_common/qt_progress_dialog.cpp b/src/qt_common/qt_progress_dialog.cpp
new file mode 100644
index 0000000000..b4bf74c8bd
--- /dev/null
+++ b/src/qt_common/qt_progress_dialog.cpp
@@ -0,0 +1,4 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "qt_progress_dialog.h"
diff --git a/src/qt_common/qt_progress_dialog.h b/src/qt_common/qt_progress_dialog.h
new file mode 100644
index 0000000000..17f6817ffa
--- /dev/null
+++ b/src/qt_common/qt_progress_dialog.h
@@ -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
+
+#ifdef YUZU_QT_WIDGETS
+#include
+#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
diff --git a/src/qt_common/shared_translation.cpp b/src/qt_common/shared_translation.cpp
index 81ec35b6d8..8f31e07154 100644
--- a/src/qt_common/shared_translation.cpp
+++ b/src/qt_common/shared_translation.cpp
@@ -29,10 +29,10 @@ std::unique_ptr InitializeTranslations(QObject* parent)
#define INSERT(SETTINGS, ID, 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
- INSERT(Settings, cabinet_applet_mode, tr("Amiibo editor"), QString());
+ // Applets
+ INSERT(Settings, cabinet_applet_mode, tr("Amiibo editor"), QString());
INSERT(Settings, controller_applet_mode, tr("Controller configuration"), QString());
INSERT(Settings, data_erase_applet_mode, tr("Data erase"), QString());
INSERT(Settings, error_applet_mode, tr("Error"), QString());
@@ -407,12 +407,6 @@ std::unique_ptr InitializeTranslations(QObject* parent)
"their resolution, details and supported controllers and depending on this setting.\n"
"Setting to Handheld can help improve performance for low end systems."));
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
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index 7c89730651..fa61cdfb1f 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -650,10 +650,10 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri
// TODO: Implement shortcut creation for macOS
#if !defined(__APPLE__)
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]() {
- emit CreateShortcut(program_id, path, GameListShortcutTarget::Applications);
+ emit CreateShortcut(program_id, path, QtCommon::Game::ShortcutTarget::Applications);
});
#endif
connect(properties, &QAction::triggered,
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h
index 367d047c40..94e7b2dc42 100644
--- a/src/yuzu/game_list.h
+++ b/src/yuzu/game_list.h
@@ -52,11 +52,6 @@ enum class DumpRomFSTarget {
SDMC,
};
-enum class GameListShortcutTarget {
- Desktop,
- Applications,
-};
-
class GameList : public QWidget {
Q_OBJECT
@@ -113,7 +108,7 @@ signals:
void VerifyIntegrityRequested(const std::string& game_path);
void CopyTIDRequested(u64 program_id);
void CreateShortcut(u64 program_id, const std::string& game_path,
- GameListShortcutTarget target);
+ const QtCommon::Game::ShortcutTarget target);
void NavigateToGamedbEntryRequested(u64 program_id,
const CompatibilityList& compatibility_list);
void OpenPerGameGeneralRequested(const std::string& file);
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 728ed2068c..0d7a08d63d 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -1,21 +1,22 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// 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
#include
#include
#include
#include
#include
-#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__
#include // for chdir
@@ -398,8 +399,7 @@ inline static bool isDarkMode() {
GMainWindow::GMainWindow(bool has_broken_vulkan)
: ui{std::make_unique()},
- input_subsystem{std::make_shared()}, user_data_migrator{this},
- provider{std::make_unique()} {
+ input_subsystem{std::make_shared()}, user_data_migrator{this} {
QtCommon::Init(this);
Common::FS::CreateEdenPaths();
@@ -543,7 +543,7 @@ GMainWindow::GMainWindow(bool has_broken_vulkan)
QtCommon::system->SetContentProvider(std::make_unique());
QtCommon::system->RegisterContentProvider(FileSys::ContentProviderUnionSlot::FrontendManual,
- provider.get());
+ QtCommon::provider.get());
QtCommon::system->GetFileSystemController().CreateFactories(*QtCommon::vfs);
// 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->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);
game_list_placeholder = new GameListPlaceholder(this);
@@ -1913,10 +1913,6 @@ bool GMainWindow::LoadROM(const QString& filename, Service::AM::FrontendAppletPa
return false;
}
- if (!OnCheckNcaVerification()) {
- return false;
- }
-
/** Exec */
const Core::SystemResultStatus result{
QtCommon::system->Load(*render_window, filename.toStdString(), params)};
@@ -2023,7 +2019,7 @@ void GMainWindow::ConfigureFilesystemProvider(const std::string& filepath) {
u64 program_id = 0;
const auto res2 = loader->ReadProgramId(program_id);
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,
file);
} else if (res2 == Loader::ResultStatus::Success &&
@@ -2033,7 +2029,7 @@ void GMainWindow::ConfigureFilesystemProvider(const std::string& filepath) {
: FileSys::XCI{file}.GetSecurePartitionNSP();
for (const auto& title : nsp->GetNCAs()) {
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());
}
}
@@ -2761,18 +2757,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
// END
void GMainWindow::OnGameListVerifyIntegrity(const std::string& game_path) {
- QProgressDialog progress(tr("Verifying integrity..."), tr("Cancel"), 0, 100, this);
- 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((processed_size * 100) / total_size));
- return progress.wasCanceled();
- };
-
- QtCommon::Content::VerifyGameContents(game_path, QtProgressCallback);
+ QtCommon::Content::VerifyGameContents(game_path);
}
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));
}
-// 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,
- GameListShortcutTarget target) {
+ const QtCommon::Game::ShortcutTarget target) {
// Create shortcut
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) {
@@ -3884,74 +3837,11 @@ void GMainWindow::OnOpenLogFolder()
}
void GMainWindow::OnVerifyInstalledContents() {
- // Initialize a progress dialog.
- 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((processed_size * 100) / total_size));
- return progress.wasCanceled();
- };
-
- const std::vector 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));
- }
+ QtCommon::Content::VerifyInstalledContents();
}
void GMainWindow::InstallFirmware(const QString& location, bool recursive) {
- QProgressDialog progress(tr("Installing Firmware..."), tr("Cancel"), 0, 100, this);
- 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((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((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();
+ QtCommon::Content::InstallFirmware(location, recursive);
OnCheckFirmwareDecryption();
}
@@ -4190,8 +4080,6 @@ void GMainWindow::OnHomeMenu() {
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(Service::AM::AppletProgramId::QLaunch);
auto bis_system = QtCommon::system->GetFileSystemController().GetSystemNANDContents();
@@ -4233,164 +4121,11 @@ void GMainWindow::OnInitialSetup() {
}
void GMainWindow::OnCreateHomeMenuDesktopShortcut() {
- OnCreateHomeMenuShortcut(GameListShortcutTarget::Desktop);
+ QtCommon::Game::CreateHomeMenuShortcut(QtCommon::Game::ShortcutTarget::Desktop);
}
void GMainWindow::OnCreateHomeMenuApplicationMenuShortcut() {
- OnCreateHomeMenuShortcut(GameListShortcutTarget::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 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(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(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);
+ QtCommon::Game::CreateHomeMenuShortcut(QtCommon::Game::ShortcutTarget::Applications);
}
void GMainWindow::OnCaptureScreenshot() {
@@ -4718,7 +4453,6 @@ void GMainWindow::OnMouseActivity() {
}
void GMainWindow::OnCheckFirmwareDecryption() {
- QtCommon::system->GetFileSystemController().CreateFactories(*QtCommon::vfs);
if (!ContentManager::AreKeysPresent()) {
QMessageBox::warning(this, tr("Derivation Components Missing"),
tr("Encryption keys are missing."));
@@ -4727,41 +4461,6 @@ void GMainWindow::OnCheckFirmwareDecryption() {
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(QMessageBox::Ok);
-}
-
bool GMainWindow::CheckFirmwarePresence() {
return FirmwareManager::CheckFirmwarePresence(*QtCommon::system.get());
}
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 0bc5913acc..e3922759b0 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -54,7 +54,6 @@ class QSlider;
class QHBoxLayout;
class WaitTreeWidget;
enum class GameListOpenTarget;
-enum class GameListShortcutTarget;
enum class DumpRomFSTarget;
class GameListPlaceholder;
@@ -161,13 +160,6 @@ class GMainWindow : public QMainWindow {
/// Max number of recently loaded items to keep track of
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:
void filterBarSetChecked(bool state);
void UpdateUITheme();
@@ -177,7 +169,7 @@ public:
bool DropAction(QDropEvent* event);
void AcceptDropEvent(QDropEvent* event);
- std::filesystem::path GetShortcutPath(GameListShortcutTarget target);
+ std::filesystem::path GetShortcutPath(QtCommon::Game::ShortcutTarget target);
signals:
@@ -358,8 +350,9 @@ private slots:
void OnGameListCopyTID(u64 program_id);
void OnGameListNavigateToGamedbEntry(u64 program_id,
const CompatibilityList& compatibility_list);
- void OnGameListCreateShortcut(u64 program_id, const std::string& game_path,
- GameListShortcutTarget target);
+ void OnGameListCreateShortcut(u64 program_id,
+ const std::string& game_path,
+ const QtCommon::Game::ShortcutTarget target);
void OnGameListOpenDirectory(const QString& directory);
void OnGameListAddDirectory();
void OnGameListShowList(bool show);
@@ -416,7 +409,6 @@ private slots:
void OnInitialSetup();
void OnCreateHomeMenuDesktopShortcut();
void OnCreateHomeMenuApplicationMenuShortcut();
- void OnCreateHomeMenuShortcut(GameListShortcutTarget target);
void OnCaptureScreenshot();
void OnCheckFirmwareDecryption();
void OnLanguageChanged(const QString& locale);
@@ -537,9 +529,6 @@ private:
QString startup_icon_theme;
- // FS
- std::unique_ptr provider;
-
// Debugger panes
WaitTreeWidget* waitTreeWidget;
ControllerDialog* controller_dialog;
@@ -586,7 +575,7 @@ private:
void CreateShortcut(const std::string& game_path,
const u64 program_id,
const std::string& game_title,
- GameListShortcutTarget target,
+ QtCommon::Game::ShortcutTarget target,
std::string arguments,
const bool needs_title);