summaryrefslogtreecommitdiff
path: root/src/yuzu
diff options
context:
space:
mode:
Diffstat (limited to 'src/yuzu')
-rw-r--r--src/yuzu/configuration/configure_input.cpp3
-rw-r--r--src/yuzu/configuration/qt_config.cpp1
-rw-r--r--src/yuzu/configuration/shared_translation.cpp168
-rw-r--r--src/yuzu/hotkeys.cpp6
-rw-r--r--src/yuzu/main.cpp251
-rw-r--r--src/yuzu/main.h2
-rw-r--r--src/yuzu/main.ui24
7 files changed, 390 insertions, 65 deletions
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index e28df10bd..28c3baf08 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -8,10 +8,7 @@
#include "common/settings_enums.h"
#include "core/core.h"
#include "core/hle/service/am/am.h"
-#include "core/hle/service/am/applet_ae.h"
#include "core/hle/service/am/applet_manager.h"
-#include "core/hle/service/am/applet_message_queue.h"
-#include "core/hle/service/am/applet_oe.h"
#include "core/hle/service/sm/sm.h"
#include "hid_core/frontend/emulated_controller.h"
#include "hid_core/hid_core.h"
diff --git a/src/yuzu/configuration/qt_config.cpp b/src/yuzu/configuration/qt_config.cpp
index 1051031f2..37951b9c8 100644
--- a/src/yuzu/configuration/qt_config.cpp
+++ b/src/yuzu/configuration/qt_config.cpp
@@ -90,6 +90,7 @@ void QtConfig::ReadQtPlayerValues(const std::size_t player_index) {
if (profile_name.empty()) {
// Use the global input config
player = Settings::values.players.GetValue(true)[player_index];
+ player.profile_name = "";
return;
}
}
diff --git a/src/yuzu/configuration/shared_translation.cpp b/src/yuzu/configuration/shared_translation.cpp
index ce65b2bf1..0549e8ae4 100644
--- a/src/yuzu/configuration/shared_translation.cpp
+++ b/src/yuzu/configuration/shared_translation.cpp
@@ -54,13 +54,28 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {
QStringLiteral());
// Core
- INSERT(Settings, use_multi_core, tr("Multicore CPU Emulation"), QStringLiteral());
- INSERT(Settings, memory_layout_mode, tr("Memory Layout"), QStringLiteral());
+ INSERT(
+ Settings, use_multi_core, tr("Multicore CPU Emulation"),
+ tr("This option increases CPU emulation thread use from 1 to the Switch’s maximum of 4.\n"
+ "This is mainly a debug option and shouldn’t be disabled."));
+ INSERT(
+ Settings, memory_layout_mode, tr("Memory Layout"),
+ tr("Increases the amount of emulated RAM from the stock 4GB of the retail Switch to the "
+ "developer kit's 8/6GB.\nIt’s doesn’t improve stability or performance and is intended "
+ "to let big texture mods fit in emulated RAM.\nEnabling it will increase memory "
+ "use. It is not recommended to enable unless a specific game with a texture mod needs "
+ "it."));
INSERT(Settings, use_speed_limit, QStringLiteral(), QStringLiteral());
- INSERT(Settings, speed_limit, tr("Limit Speed Percent"), QStringLiteral());
+ INSERT(Settings, speed_limit, tr("Limit Speed Percent"),
+ tr("Controls the game's maximum rendering speed, but it’s up to each game if it runs "
+ "faster or not.\n200% for a 30 FPS game is 60 FPS, and for a "
+ "60 FPS game it will be 120 FPS.\nDisabling it means unlocking the framerate to the "
+ "maximum your PC can reach."));
// Cpu
- INSERT(Settings, cpu_accuracy, tr("Accuracy:"), QStringLiteral());
+ INSERT(Settings, cpu_accuracy, tr("Accuracy:"),
+ tr("This setting controls the accuracy of the emulated CPU.\nDon't change this unless "
+ "you know what you are doing."));
INSERT(Settings, cpu_backend, tr("Backend:"), QStringLiteral());
// Cpu Debug
@@ -80,34 +95,80 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {
tr("This option improves the speed of 32 bits ASIMD floating-point functions by running "
"with incorrect rounding modes."));
INSERT(Settings, cpuopt_unsafe_inaccurate_nan, tr("Inaccurate NaN handling"),
- tr("This option improves speed by removing NaN checking. Please note this also reduces "
+ tr("This option improves speed by removing NaN checking.\nPlease note this also reduces "
"accuracy of certain floating-point instructions."));
INSERT(Settings, cpuopt_unsafe_fastmem_check, tr("Disable address space checks"),
tr("This option improves speed by eliminating a safety check before every memory "
- "read/write "
- "in guest. Disabling it may allow a game to read/write the emulator's memory."));
+ "read/write in guest.\nDisabling it may allow a game to read/write the emulator's "
+ "memory."));
INSERT(
Settings, cpuopt_unsafe_ignore_global_monitor, tr("Ignore global monitor"),
tr("This option improves speed by relying only on the semantics of cmpxchg to ensure "
- "safety of exclusive access instructions. Please note this may result in deadlocks and "
+ "safety of exclusive access instructions.\nPlease note this may result in deadlocks and "
"other race conditions."));
// Renderer
- INSERT(Settings, renderer_backend, tr("API:"), QStringLiteral());
- INSERT(Settings, vulkan_device, tr("Device:"), QStringLiteral());
- INSERT(Settings, shader_backend, tr("Shader Backend:"), QStringLiteral());
- INSERT(Settings, resolution_setup, tr("Resolution:"), QStringLiteral());
+ INSERT(
+ Settings, renderer_backend, tr("API:"),
+ tr("Switches between the available graphics APIs.\nVulkan is recommended in most cases."));
+ INSERT(Settings, vulkan_device, tr("Device:"),
+ tr("This setting selects the GPU to use with the Vulkan backend."));
+ INSERT(Settings, shader_backend, tr("Shader Backend:"),
+ tr("The shader backend to use for the OpenGL renderer.\nGLSL is the fastest in "
+ "performance and the best in rendering accuracy.\n"
+ "GLASM is a deprecated NVIDIA-only backend that offers much better shader building "
+ "performance at the cost of FPS and rendering accuracy.\n"
+ "SPIR-V compiles the fastest, but yields poor results on most GPU drivers."));
+ INSERT(Settings, resolution_setup, tr("Resolution:"),
+ tr("Forces the game to render at a different resolution.\nHigher resolutions require "
+ "much more VRAM and bandwidth.\n"
+ "Options lower than 1X can cause rendering issues."));
INSERT(Settings, scaling_filter, tr("Window Adapting Filter:"), QStringLiteral());
- INSERT(Settings, fsr_sharpening_slider, tr("FSR Sharpness:"), QStringLiteral());
- INSERT(Settings, anti_aliasing, tr("Anti-Aliasing Method:"), QStringLiteral());
- INSERT(Settings, fullscreen_mode, tr("Fullscreen Mode:"), QStringLiteral());
- INSERT(Settings, aspect_ratio, tr("Aspect Ratio:"), QStringLiteral());
- INSERT(Settings, use_disk_shader_cache, tr("Use disk pipeline cache"), QStringLiteral());
- INSERT(Settings, use_asynchronous_gpu_emulation, tr("Use asynchronous GPU emulation"),
- QStringLiteral());
- INSERT(Settings, nvdec_emulation, tr("NVDEC emulation:"), QStringLiteral());
- INSERT(Settings, accelerate_astc, tr("ASTC Decoding Method:"), QStringLiteral());
- INSERT(Settings, astc_recompression, tr("ASTC Recompression Method:"), QStringLiteral());
+ INSERT(Settings, fsr_sharpening_slider, tr("FSR Sharpness:"),
+ tr("Determines how sharpened the image will look while using FSR’s dynamic contrast."));
+ INSERT(Settings, anti_aliasing, tr("Anti-Aliasing Method:"),
+ tr("The anti-aliasing method to use.\nSMAA offers the best quality.\nFXAA has a "
+ "lower performance impact and can produce a better and more stable picture under "
+ "very low resolutions."));
+ INSERT(Settings, fullscreen_mode, tr("Fullscreen Mode:"),
+ tr("The method used to render the window in fullscreen.\nBorderless offers the best "
+ "compatibility with the on-screen keyboard that some games request for "
+ "input.\nExclusive "
+ "fullscreen may offer better performance and better Freesync/Gsync support."));
+ INSERT(Settings, aspect_ratio, tr("Aspect Ratio:"),
+ tr("Stretches the game to fit the specified aspect ratio.\nSwitch games only support "
+ "16:9, so custom game mods are required to get other ratios.\nAlso controls the "
+ "aspect ratio of captured screenshots."));
+ INSERT(Settings, use_disk_shader_cache, tr("Use disk pipeline cache"),
+ tr("Allows saving shaders to storage for faster loading on following game "
+ "boots.\nDisabling "
+ "it is only intended for debugging."));
+ INSERT(
+ Settings, use_asynchronous_gpu_emulation, tr("Use asynchronous GPU emulation"),
+ tr("Uses an extra CPU thread for rendering.\nThis option should always remain enabled."));
+ INSERT(Settings, nvdec_emulation, tr("NVDEC emulation:"),
+ tr("Specifies how videos should be decoded.\nIt can either use the CPU or the GPU for "
+ "decoding, or perform no decoding at all (black screen on videos).\n"
+ "In most cases, GPU decoding provides the best performance."));
+ INSERT(Settings, accelerate_astc, tr("ASTC Decoding Method:"),
+ tr("This option controls how ASTC textures should be decoded.\n"
+ "CPU: Use the CPU for decoding, slowest but safest method.\n"
+ "GPU: Use the GPU's compute shaders to decode ASTC textures, recommended for most "
+ "games and users.\n"
+ "CPU Asynchronously: Use the CPU to decode ASTC textures as they arrive. Completely "
+ "eliminates ASTC decoding\nstuttering at the cost of rendering issues while the "
+ "texture is being decoded."));
+ INSERT(
+ Settings, astc_recompression, tr("ASTC Recompression Method:"),
+ tr("Almost all desktop and laptop dedicated GPUs lack support for ASTC textures, forcing "
+ "the emulator to decompress to an intermediate format any card supports, RGBA8.\n"
+ "This option recompresses RGBA8 to either the BC1 or BC3 format, saving VRAM but "
+ "negatively affecting image quality."));
+ INSERT(Settings, vram_usage_mode, tr("VRAM Usage Mode:"),
+ tr("Selects whether the emulator should prefer to conserve memory or make maximum usage "
+ "of available video memory for performance. Has no effect on integrated graphics. "
+ "Aggressive mode may severely impact the performance of other applications such as "
+ "recording software."));
INSERT(
Settings, vsync_mode, tr("VSync Mode:"),
tr("FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen "
@@ -121,22 +182,29 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {
// Renderer (Advanced Graphics)
INSERT(Settings, async_presentation, tr("Enable asynchronous presentation (Vulkan only)"),
- QStringLiteral());
+ tr("Slightly improves performance by moving presentation to a separate CPU thread."));
INSERT(
Settings, renderer_force_max_clock, tr("Force maximum clocks (Vulkan only)"),
tr("Runs work in the background while waiting for graphics commands to keep the GPU from "
"lowering its clock speed."));
- INSERT(Settings, max_anisotropy, tr("Anisotropic Filtering:"), QStringLiteral());
- INSERT(Settings, gpu_accuracy, tr("Accuracy Level:"), QStringLiteral());
- INSERT(
- Settings, use_asynchronous_shaders, tr("Use asynchronous shader building (Hack)"),
- tr("Enables asynchronous shader compilation, which may reduce shader stutter. This feature "
- "is experimental."));
+ INSERT(Settings, max_anisotropy, tr("Anisotropic Filtering:"),
+ tr("Controls the quality of texture rendering at oblique angles.\nIt’s a light setting "
+ "and safe to set at 16x on most GPUs."));
+ INSERT(Settings, gpu_accuracy, tr("Accuracy Level:"),
+ tr("GPU emulation accuracy.\nMost games render fine with Normal, but High is still "
+ "required for some.\nParticles tend to only render correctly with High "
+ "accuracy.\nExtreme should only be used for debugging.\nThis option can "
+ "be changed while playing.\nSome games may require booting on high to render "
+ "properly."));
+ INSERT(Settings, use_asynchronous_shaders, tr("Use asynchronous shader building (Hack)"),
+ tr("Enables asynchronous shader compilation, which may reduce shader stutter.\nThis "
+ "feature "
+ "is experimental."));
INSERT(Settings, use_fast_gpu_time, tr("Use Fast GPU Time (Hack)"),
tr("Enables Fast GPU Time. This option will force most games to run at their highest "
"native resolution."));
INSERT(Settings, use_vulkan_driver_pipeline_cache, tr("Use Vulkan pipeline cache"),
- tr("Enables GPU vendor-specific pipeline cache. This option can improve shader loading "
+ tr("Enables GPU vendor-specific pipeline cache.\nThis option can improve shader loading "
"time significantly in cases where the Vulkan driver does not store pipeline cache "
"files internally."));
INSERT(
@@ -157,19 +225,27 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {
// Renderer (Debug)
// System
- INSERT(Settings, rng_seed, tr("RNG Seed"), QStringLiteral());
+ INSERT(Settings, rng_seed, tr("RNG Seed"),
+ tr("Controls the seed of the random number generator.\nMainly used for speedrunning "
+ "purposes."));
INSERT(Settings, rng_seed_enabled, QStringLiteral(), QStringLiteral());
- INSERT(Settings, device_name, tr("Device Name"), QStringLiteral());
- INSERT(Settings, custom_rtc, tr("Custom RTC Date:"), QStringLiteral());
+ INSERT(Settings, device_name, tr("Device Name"), tr("The name of the emulated Switch."));
+ INSERT(Settings, custom_rtc, tr("Custom RTC Date:"),
+ tr("This option allows to change the emulated clock of the Switch.\n"
+ "Can be used to manipulate time in games."));
INSERT(Settings, custom_rtc_enabled, QStringLiteral(), QStringLiteral());
INSERT(Settings, custom_rtc_offset, QStringLiteral(" "),
QStringLiteral("The number of seconds from the current unix time"));
INSERT(Settings, language_index, tr("Language:"),
tr("Note: this can be overridden when region setting is auto-select"));
- INSERT(Settings, region_index, tr("Region:"), QStringLiteral());
- INSERT(Settings, time_zone_index, tr("Time Zone:"), QStringLiteral());
+ INSERT(Settings, region_index, tr("Region:"), tr("The region of the emulated Switch."));
+ INSERT(Settings, time_zone_index, tr("Time Zone:"),
+ tr("The time zone of the emulated Switch."));
INSERT(Settings, sound_index, tr("Sound Output Mode:"), QStringLiteral());
- INSERT(Settings, use_docked_mode, tr("Console Mode:"), QStringLiteral());
+ INSERT(Settings, use_docked_mode, tr("Console Mode:"),
+ tr("Selects if the console is emulated in Docked or Handheld mode.\nGames will change "
+ "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, QStringLiteral(), QStringLiteral());
// Controls
@@ -187,14 +263,19 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {
// Ui
// Ui General
- INSERT(UISettings, select_user_on_boot, tr("Prompt for user on game boot"), QStringLiteral());
+ INSERT(UISettings, select_user_on_boot, tr("Prompt for user on game boot"),
+ tr("Ask to select a user profile on each boot, useful if multiple people use yuzu on "
+ "the same PC."));
INSERT(UISettings, pause_when_in_background, tr("Pause emulation when in background"),
- QStringLiteral());
+ tr("This setting pauses yuzu when focusing other windows."));
INSERT(UISettings, confirm_before_stopping, tr("Confirm before stopping emulation"),
- QStringLiteral());
- INSERT(UISettings, hide_mouse, tr("Hide mouse on inactivity"), QStringLiteral());
+ tr("This setting overrides game prompts asking to confirm stopping the game.\nEnabling "
+ "it bypasses such prompts and directly exits the emulation."));
+ INSERT(UISettings, hide_mouse, tr("Hide mouse on inactivity"),
+ tr("This setting hides the mouse after 2.5s of inactivity."));
INSERT(UISettings, controller_applet_disabled, tr("Disable controller applet"),
- QStringLiteral());
+ tr("Forcibly disables the use of the controller applet by guests.\nWhen a guest "
+ "attempts to open the controller applet, it is immediately closed."));
// Linux
INSERT(Settings, enable_gamemode, tr("Enable Gamemode"), QStringLiteral());
@@ -239,6 +320,11 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent) {
PAIR(AstcRecompression, Bc1, tr("BC1 (Low quality)")),
PAIR(AstcRecompression, Bc3, tr("BC3 (Medium quality)")),
}});
+ translations->insert({Settings::EnumMetadata<Settings::VramUsageMode>::Index(),
+ {
+ PAIR(VramUsageMode, Conservative, tr("Conservative")),
+ PAIR(VramUsageMode, Aggressive, tr("Aggressive")),
+ }});
translations->insert({Settings::EnumMetadata<Settings::RendererBackend>::Index(),
{
#ifdef HAS_OPENGL
diff --git a/src/yuzu/hotkeys.cpp b/src/yuzu/hotkeys.cpp
index 170f14684..1931dcd1f 100644
--- a/src/yuzu/hotkeys.cpp
+++ b/src/yuzu/hotkeys.cpp
@@ -190,10 +190,8 @@ void ControllerShortcut::ControllerUpdateEvent(Core::HID::ControllerTriggerType
if (type != Core::HID::ControllerTriggerType::Button) {
return;
}
- if (!Settings::values.controller_navigation) {
- return;
- }
- if (button_sequence.npad.raw == Core::HID::NpadButton::None) {
+ if (button_sequence.npad.raw == Core::HID::NpadButton::None &&
+ button_sequence.capture.raw == 0 && button_sequence.home.raw == 0) {
return;
}
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 13381fea8..b2ae3db52 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -44,9 +44,6 @@
#include "core/frontend/applets/mii_edit.h"
#include "core/frontend/applets/software_keyboard.h"
#include "core/hle/service/acc/profile_manager.h"
-#include "core/hle/service/am/applet_ae.h"
-#include "core/hle/service/am/applet_message_queue.h"
-#include "core/hle/service/am/applet_oe.h"
#include "core/hle/service/am/frontend/applets.h"
#include "core/hle/service/set/system_settings_server.h"
#include "frontend_common/content_manager.h"
@@ -649,10 +646,10 @@ void GMainWindow::AmiiboSettingsShowDialog(const Core::Frontend::CabinetParamete
std::shared_ptr<Service::NFC::NfcDevice> nfp_device) {
cabinet_applet =
new QtAmiiboSettingsDialog(this, parameters, input_subsystem.get(), nfp_device);
- SCOPE_EXIT({
+ SCOPE_EXIT {
cabinet_applet->deleteLater();
cabinet_applet = nullptr;
- });
+ };
cabinet_applet->setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint |
Qt::WindowTitleHint | Qt::WindowSystemMenuHint);
@@ -676,10 +673,10 @@ void GMainWindow::ControllerSelectorReconfigureControllers(
const Core::Frontend::ControllerParameters& parameters) {
controller_applet =
new QtControllerSelectorDialog(this, parameters, input_subsystem.get(), *system);
- SCOPE_EXIT({
+ SCOPE_EXIT {
controller_applet->deleteLater();
controller_applet = nullptr;
- });
+ };
controller_applet->setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint |
Qt::WindowStaysOnTopHint | Qt::WindowTitleHint |
@@ -706,10 +703,10 @@ void GMainWindow::ControllerSelectorRequestExit() {
void GMainWindow::ProfileSelectorSelectProfile(
const Core::Frontend::ProfileSelectParameters& parameters) {
profile_select_applet = new QtProfileSelectionDialog(*system, this, parameters);
- SCOPE_EXIT({
+ SCOPE_EXIT {
profile_select_applet->deleteLater();
profile_select_applet = nullptr;
- });
+ };
profile_select_applet->setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint |
Qt::WindowStaysOnTopHint | Qt::WindowTitleHint |
@@ -1606,6 +1603,8 @@ void GMainWindow::ConnectMenuEvents() {
// Help
connect_menu(ui->action_Open_yuzu_Folder, &GMainWindow::OnOpenYuzuFolder);
connect_menu(ui->action_Verify_installed_contents, &GMainWindow::OnVerifyInstalledContents);
+ connect_menu(ui->action_Install_Firmware, &GMainWindow::OnInstallFirmware);
+ connect_menu(ui->action_Install_Keys, &GMainWindow::OnInstallDecryptionKeys);
connect_menu(ui->action_About, &GMainWindow::OnAbout);
}
@@ -1634,6 +1633,9 @@ void GMainWindow::UpdateMenuState() {
action->setEnabled(emulation_running);
}
+ ui->action_Install_Firmware->setEnabled(!emulation_running);
+ ui->action_Install_Keys->setEnabled(!emulation_running);
+
for (QAction* action : applet_actions) {
action->setEnabled(is_firmware_available && !emulation_running);
}
@@ -2885,17 +2887,19 @@ bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path,
LOG_ERROR(Frontend, "CoInitialize failed");
return false;
}
- SCOPE_EXIT({ CoUninitialize(); });
+ SCOPE_EXIT {
+ CoUninitialize();
+ };
IShellLinkW* ps1 = nullptr;
IPersistFile* persist_file = nullptr;
- SCOPE_EXIT({
+ SCOPE_EXIT {
if (persist_file != nullptr) {
persist_file->Release();
}
if (ps1 != nullptr) {
ps1->Release();
}
- });
+ };
HRESULT hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLinkW,
reinterpret_cast<void**>(&ps1));
if (FAILED(hres)) {
@@ -3520,10 +3524,10 @@ void GMainWindow::OnSaveConfig() {
void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_text) {
error_applet = new OverlayDialog(render_window, *system, error_code, error_text, QString{},
tr("OK"), Qt::AlignLeft | Qt::AlignVCenter);
- SCOPE_EXIT({
+ SCOPE_EXIT {
error_applet->deleteLater();
error_applet = nullptr;
- });
+ };
error_applet->exec();
emit ErrorDisplayFinished();
@@ -4153,6 +4157,221 @@ void GMainWindow::OnVerifyInstalledContents() {
}
}
+void GMainWindow::OnInstallFirmware() {
+ // Don't do this while emulation is running, that'd probably be a bad idea.
+ if (emu_thread != nullptr && emu_thread->IsRunning()) {
+ return;
+ }
+
+ // Check for installed keys, error out, suggest restart?
+ if (!ContentManager::AreKeysPresent()) {
+ QMessageBox::information(
+ this, tr("Keys not installed"),
+ tr("Install decryption keys and restart yuzu before attempting to install firmware."));
+ return;
+ }
+
+ const QString firmware_source_location = QFileDialog::getExistingDirectory(
+ this, tr("Select Dumped Firmware Source Location"), {}, QFileDialog::ShowDirsOnly);
+ if (firmware_source_location.isEmpty()) {
+ return;
+ }
+
+ 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<int>((processed_size * 100) / total_size));
+ return progress.wasCanceled();
+ };
+
+ LOG_INFO(Frontend, "Installing firmware from {}", firmware_source_location.toStdString());
+
+ // Check for a reasonable number of .nca files (don't hardcode them, just see if there's some in
+ // there.)
+ std::filesystem::path firmware_source_path = firmware_source_location.toStdString();
+ if (!Common::FS::IsDir(firmware_source_path)) {
+ progress.close();
+ return;
+ }
+
+ std::vector<std::filesystem::path> out;
+ const Common::FS::DirEntryCallable callback =
+ [&out](const std::filesystem::directory_entry& entry) {
+ if (entry.path().has_extension() && entry.path().extension() == ".nca") {
+ out.emplace_back(entry.path());
+ }
+
+ return true;
+ };
+
+ QtProgressCallback(100, 10);
+
+ Common::FS::IterateDirEntries(firmware_source_path, callback, Common::FS::DirEntryFilter::File);
+ if (out.size() <= 0) {
+ progress.close();
+ QMessageBox::warning(this, tr("Firmware install failed"),
+ tr("Unable to locate potential firmware NCA files"));
+ return;
+ }
+
+ // Locate and erase the content of nand/system/Content/registered/*.nca, if any.
+ auto sysnand_content_vdir = system->GetFileSystemController().GetSystemNANDContentDirectory();
+ if (!sysnand_content_vdir->CleanSubdirectoryRecursive("registered")) {
+ progress.close();
+ QMessageBox::critical(this, tr("Firmware install failed"),
+ tr("Failed to delete one or more firmware file."));
+ return;
+ }
+
+ LOG_INFO(Frontend,
+ "Cleaned nand/system/Content/registered folder in preparation for new firmware.");
+
+ QtProgressCallback(100, 20);
+
+ auto firmware_vdir = sysnand_content_vdir->GetDirectoryRelative("registered");
+
+ bool success = true;
+ int i = 0;
+ for (const auto& firmware_src_path : out) {
+ i++;
+ auto firmware_src_vfile =
+ vfs->OpenFile(firmware_src_path.generic_string(), FileSys::OpenMode::Read);
+ auto firmware_dst_vfile =
+ firmware_vdir->CreateFileRelative(firmware_src_path.filename().string());
+
+ if (!VfsRawCopy(firmware_src_vfile, firmware_dst_vfile)) {
+ LOG_ERROR(Frontend, "Failed to copy firmware file {} to {} in registered folder!",
+ firmware_src_path.generic_string(), firmware_src_path.filename().string());
+ success = false;
+ }
+
+ if (QtProgressCallback(
+ 100, 20 + static_cast<int>(((i) / static_cast<float>(out.size())) * 70.0))) {
+ progress.close();
+ QMessageBox::warning(
+ this, tr("Firmware install failed"),
+ tr("Firmware installation cancelled, firmware may be in bad state, "
+ "restart yuzu or re-install firmware."));
+ return;
+ }
+ }
+
+ if (!success) {
+ progress.close();
+ QMessageBox::critical(this, tr("Firmware install failed"),
+ tr("One or more firmware files failed to copy into NAND."));
+ 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<int>((processed_size * 10) / total_size));
+ return progress.wasCanceled();
+ };
+
+ auto result =
+ ContentManager::VerifyInstalledContents(*system, *provider, VerifyFirmwareCallback, true);
+
+ if (result.size() > 0) {
+ const auto failed_names =
+ QString::fromStdString(fmt::format("{}", fmt::join(result, "\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();
+}
+
+void GMainWindow::OnInstallDecryptionKeys() {
+ // Don't do this while emulation is running.
+ if (emu_thread != nullptr && emu_thread->IsRunning()) {
+ return;
+ }
+
+ const QString key_source_location = QFileDialog::getOpenFileName(
+ this, tr("Select Dumped Keys Location"), {}, QStringLiteral("prod.keys (prod.keys)"), {},
+ QFileDialog::ReadOnly);
+ if (key_source_location.isEmpty()) {
+ return;
+ }
+
+ // Verify that it contains prod.keys, title.keys and optionally, key_retail.bin
+ LOG_INFO(Frontend, "Installing key files from {}", key_source_location.toStdString());
+
+ const std::filesystem::path prod_key_path = key_source_location.toStdString();
+ const std::filesystem::path key_source_path = prod_key_path.parent_path();
+ if (!Common::FS::IsDir(key_source_path)) {
+ return;
+ }
+
+ bool prod_keys_found = false;
+ std::vector<std::filesystem::path> source_key_files;
+
+ if (Common::FS::Exists(prod_key_path)) {
+ prod_keys_found = true;
+ source_key_files.emplace_back(prod_key_path);
+ }
+
+ if (Common::FS::Exists(key_source_path / "title.keys")) {
+ source_key_files.emplace_back(key_source_path / "title.keys");
+ }
+
+ if (Common::FS::Exists(key_source_path / "key_retail.bin")) {
+ source_key_files.emplace_back(key_source_path / "key_retail.bin");
+ }
+
+ // There should be at least prod.keys.
+ if (source_key_files.empty() || !prod_keys_found) {
+ QMessageBox::warning(this, tr("Decryption Keys install failed"),
+ tr("prod.keys is a required decryption key file."));
+ return;
+ }
+
+ const auto yuzu_keys_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::KeysDir);
+ for (auto key_file : source_key_files) {
+ std::filesystem::path destination_key_file = yuzu_keys_dir / key_file.filename();
+ if (!std::filesystem::copy_file(key_file, destination_key_file,
+ std::filesystem::copy_options::overwrite_existing)) {
+ LOG_ERROR(Frontend, "Failed to copy file {} to {}", key_file.string(),
+ destination_key_file.string());
+ QMessageBox::critical(this, tr("Decryption Keys install failed"),
+ tr("One or more keys failed to copy."));
+ return;
+ }
+ }
+
+ // Reinitialize the key manager, re-read the vfs (for update/dlc files),
+ // and re-populate the game list in the UI if the user has already added
+ // game folders.
+ Core::Crypto::KeyManager::Instance().ReloadKeys();
+ system->GetFileSystemController().CreateFactories(*vfs);
+ game_list->PopulateAsync(UISettings::values.game_dirs);
+
+ if (ContentManager::AreKeysPresent()) {
+ QMessageBox::information(this, tr("Decryption Keys install succeeded"),
+ tr("Decryption Keys were successfully installed"));
+ } else {
+ QMessageBox::critical(
+ this, tr("Decryption Keys install failed"),
+ tr("Decryption Keys failed to initialize. Check that your dumping tools are "
+ "up to date and re-dump keys."));
+ }
+
+ OnCheckFirmwareDecryption();
+}
+
void GMainWindow::OnAbout() {
AboutDialog aboutDialog(this);
aboutDialog.exec();
@@ -5052,7 +5271,9 @@ int main(int argc, char* argv[]) {
Common::DetachedTasks detached_tasks;
MicroProfileOnThreadCreate("Frontend");
- SCOPE_EXIT({ MicroProfileShutdown(); });
+ SCOPE_EXIT {
+ MicroProfileShutdown();
+ };
Common::ConfigureNvidiaEnvironmentFlags();
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index aba61e388..fce643f3f 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -380,6 +380,8 @@ private slots:
void OnLoadAmiibo();
void OnOpenYuzuFolder();
void OnVerifyInstalledContents();
+ void OnInstallFirmware();
+ void OnInstallDecryptionKeys();
void OnAbout();
void OnToggleFilterBar();
void OnToggleStatusBar();
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui
index 6a6b0821f..85dc1f2f6 100644
--- a/src/yuzu/main.ui
+++ b/src/yuzu/main.ui
@@ -25,7 +25,16 @@
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QHBoxLayout" name="horizontalLayout">
- <property name="margin" stdset="0">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
<number>0</number>
</property>
</layout>
@@ -156,7 +165,8 @@
<addaction name="separator"/>
<addaction name="action_Configure_Tas"/>
</widget>
- <addaction name="action_Rederive"/>
+ <addaction name="action_Install_Keys"/>
+ <addaction name="action_Install_Firmware"/>
<addaction name="action_Verify_installed_contents"/>
<addaction name="separator"/>
<addaction name="menu_cabinet_applet"/>
@@ -455,6 +465,16 @@
<string>Open &amp;Controller Menu</string>
</property>
</action>
+ <action name="action_Install_Firmware">
+ <property name="text">
+ <string>Install Firmware</string>
+ </property>
+ </action>
+ <action name="action_Install_Keys">
+ <property name="text">
+ <string>Install Decryption Keys</string>
+ </property>
+ </action>
</widget>
<resources>
<include location="yuzu.qrc"/>