summaryrefslogtreecommitdiff
path: root/src/yuzu
diff options
context:
space:
mode:
Diffstat (limited to 'src/yuzu')
-rw-r--r--src/yuzu/CMakeLists.txt3
-rw-r--r--src/yuzu/configuration/configure_applets.cpp86
-rw-r--r--src/yuzu/configuration/configure_applets.h48
-rw-r--r--src/yuzu/configuration/configure_applets.ui65
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp7
-rw-r--r--src/yuzu/configuration/configure_dialog.h2
-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.cpp180
-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
13 files changed, 612 insertions, 66 deletions
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 76f06da12..0259a8c29 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -41,6 +41,9 @@ add_executable(yuzu
configuration/configuration_shared.cpp
configuration/configuration_shared.h
configuration/configure.ui
+ configuration/configure_applets.cpp
+ configuration/configure_applets.h
+ configuration/configure_applets.ui
configuration/configure_audio.cpp
configuration/configure_audio.h
configuration/configure_audio.ui
diff --git a/src/yuzu/configuration/configure_applets.cpp b/src/yuzu/configuration/configure_applets.cpp
new file mode 100644
index 000000000..513ecb548
--- /dev/null
+++ b/src/yuzu/configuration/configure_applets.cpp
@@ -0,0 +1,86 @@
+// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/settings.h"
+#include "core/core.h"
+#include "ui_configure_applets.h"
+#include "yuzu/configuration/configuration_shared.h"
+#include "yuzu/configuration/configure_applets.h"
+#include "yuzu/configuration/shared_widget.h"
+
+ConfigureApplets::ConfigureApplets(Core::System& system_,
+ std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group_,
+ const ConfigurationShared::Builder& builder, QWidget* parent)
+ : Tab(group_, parent), ui{std::make_unique<Ui::ConfigureApplets>()}, system{system_} {
+ ui->setupUi(this);
+
+ Setup(builder);
+
+ SetConfiguration();
+}
+
+ConfigureApplets::~ConfigureApplets() = default;
+
+void ConfigureApplets::changeEvent(QEvent* event) {
+ if (event->type() == QEvent::LanguageChange) {
+ RetranslateUI();
+ }
+
+ QWidget::changeEvent(event);
+}
+
+void ConfigureApplets::RetranslateUI() {
+ ui->retranslateUi(this);
+}
+
+void ConfigureApplets::Setup(const ConfigurationShared::Builder& builder) {
+ auto& library_applets_layout = *ui->group_library_applet_modes->layout();
+ std::map<u32, QWidget*> applets_hold{};
+
+ std::vector<Settings::BasicSetting*> settings;
+ auto push = [&settings](auto& list) {
+ for (auto setting : list) {
+ settings.push_back(setting);
+ }
+ };
+
+ push(Settings::values.linkage.by_category[Settings::Category::LibraryApplet]);
+
+ for (auto setting : settings) {
+ ConfigurationShared::Widget* widget = builder.BuildWidget(setting, apply_funcs);
+
+ if (widget == nullptr) {
+ continue;
+ }
+ if (!widget->Valid()) {
+ widget->deleteLater();
+ continue;
+ }
+
+ // Untested applets
+ if (setting->Id() == Settings::values.data_erase_applet_mode.Id() ||
+ setting->Id() == Settings::values.error_applet_mode.Id() ||
+ setting->Id() == Settings::values.net_connect_applet_mode.Id() ||
+ setting->Id() == Settings::values.web_applet_mode.Id() ||
+ setting->Id() == Settings::values.shop_applet_mode.Id() ||
+ setting->Id() == Settings::values.login_share_applet_mode.Id() ||
+ setting->Id() == Settings::values.wifi_web_auth_applet_mode.Id() ||
+ setting->Id() == Settings::values.my_page_applet_mode.Id()) {
+ widget->setHidden(true);
+ }
+
+ applets_hold.emplace(setting->Id(), widget);
+ }
+ for (const auto& [label, widget] : applets_hold) {
+ library_applets_layout.addWidget(widget);
+ }
+}
+
+void ConfigureApplets::SetConfiguration() {}
+
+void ConfigureApplets::ApplyConfiguration() {
+ const bool powered_on = system.IsPoweredOn();
+ for (const auto& func : apply_funcs) {
+ func(powered_on);
+ }
+}
diff --git a/src/yuzu/configuration/configure_applets.h b/src/yuzu/configuration/configure_applets.h
new file mode 100644
index 000000000..54f494d2f
--- /dev/null
+++ b/src/yuzu/configuration/configure_applets.h
@@ -0,0 +1,48 @@
+// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <QWidget>
+#include "yuzu/configuration/configuration_shared.h"
+
+class QCheckBox;
+class QLineEdit;
+class QComboBox;
+class QDateTimeEdit;
+namespace Core {
+class System;
+}
+
+namespace Ui {
+class ConfigureApplets;
+}
+
+namespace ConfigurationShared {
+class Builder;
+}
+
+class ConfigureApplets : public ConfigurationShared::Tab {
+public:
+ explicit ConfigureApplets(Core::System& system_,
+ std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group,
+ const ConfigurationShared::Builder& builder,
+ QWidget* parent = nullptr);
+ ~ConfigureApplets() override;
+
+ void ApplyConfiguration() override;
+ void SetConfiguration() override;
+
+private:
+ void changeEvent(QEvent* event) override;
+ void RetranslateUI();
+
+ void Setup(const ConfigurationShared::Builder& builder);
+
+ std::vector<std::function<void(bool)>> apply_funcs{};
+
+ std::unique_ptr<Ui::ConfigureApplets> ui;
+ bool enabled = false;
+
+ Core::System& system;
+};
diff --git a/src/yuzu/configuration/configure_applets.ui b/src/yuzu/configuration/configure_applets.ui
new file mode 100644
index 000000000..6f2ca66bd
--- /dev/null
+++ b/src/yuzu/configuration/configure_applets.ui
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ConfigureApplets</class>
+ <widget class="QWidget" name="ConfigureApplets">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>605</width>
+ <height>300</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <property name="accessibleName">
+ <string>Applets</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_1">
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QGroupBox" name="group_library_applet_modes">
+ <property name="title">
+ <string>Applet mode preference</string>
+ </property>
+ <layout class="QVBoxLayout">
+ <item>
+ <widget class="QWidget" name="applets_widget" native="true">
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index aab54a1cc..37f23388e 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -8,6 +8,7 @@
#include "core/core.h"
#include "ui_configure.h"
#include "vk_device_info.h"
+#include "yuzu/configuration/configure_applets.h"
#include "yuzu/configuration/configure_audio.h"
#include "yuzu/configuration/configure_cpu.h"
#include "yuzu/configuration/configure_debug_tab.h"
@@ -34,6 +35,7 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_,
: QDialog(parent), ui{std::make_unique<Ui::ConfigureDialog>()},
registry(registry_), system{system_}, builder{std::make_unique<ConfigurationShared::Builder>(
this, !system_.IsPoweredOn())},
+ applets_tab{std::make_unique<ConfigureApplets>(system_, nullptr, *builder, this)},
audio_tab{std::make_unique<ConfigureAudio>(system_, nullptr, *builder, this)},
cpu_tab{std::make_unique<ConfigureCpu>(system_, nullptr, *builder, this)},
debug_tab_tab{std::make_unique<ConfigureDebugTab>(system_, this)},
@@ -58,6 +60,7 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_,
ui->setupUi(this);
+ ui->tabWidget->addTab(applets_tab.get(), tr("Applets"));
ui->tabWidget->addTab(audio_tab.get(), tr("Audio"));
ui->tabWidget->addTab(cpu_tab.get(), tr("CPU"));
ui->tabWidget->addTab(debug_tab_tab.get(), tr("Debug"));
@@ -124,6 +127,7 @@ void ConfigureDialog::ApplyConfiguration() {
debug_tab_tab->ApplyConfiguration();
web_tab->ApplyConfiguration();
network_tab->ApplyConfiguration();
+ applets_tab->ApplyConfiguration();
system.ApplySettings();
Settings::LogSettings();
}
@@ -161,7 +165,8 @@ void ConfigureDialog::PopulateSelectionList() {
{{tr("General"),
{general_tab.get(), hotkeys_tab.get(), ui_tab.get(), web_tab.get(), debug_tab_tab.get()}},
{tr("System"),
- {system_tab.get(), profile_tab.get(), network_tab.get(), filesystem_tab.get()}},
+ {system_tab.get(), profile_tab.get(), network_tab.get(), filesystem_tab.get(),
+ applets_tab.get()}},
{tr("CPU"), {cpu_tab.get()}},
{tr("Graphics"), {graphics_tab.get(), graphics_advanced_tab.get()}},
{tr("Audio"), {audio_tab.get()}},
diff --git a/src/yuzu/configuration/configure_dialog.h b/src/yuzu/configuration/configure_dialog.h
index b28ce288c..d0a24a07b 100644
--- a/src/yuzu/configuration/configure_dialog.h
+++ b/src/yuzu/configuration/configure_dialog.h
@@ -15,6 +15,7 @@ namespace Core {
class System;
}
+class ConfigureApplets;
class ConfigureAudio;
class ConfigureCpu;
class ConfigureDebugTab;
@@ -75,6 +76,7 @@ private:
std::unique_ptr<ConfigurationShared::Builder> builder;
std::vector<ConfigurationShared::Tab*> tab_group;
+ std::unique_ptr<ConfigureApplets> applets_tab;
std::unique_ptr<ConfigureAudio> audio_tab;
std::unique_ptr<ConfigureCpu> cpu_tab;
std::unique_ptr<ConfigureDebugTab> debug_tab_tab;
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 ed9c7d859..d138b53c8 100644
--- a/src/yuzu/configuration/shared_translation.cpp
+++ b/src/yuzu/configuration/shared_translation.cpp
@@ -26,6 +26,23 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {
// A setting can be ignored by giving it a blank name
+ // Applets
+ INSERT(Settings, cabinet_applet_mode, tr("Amiibo editor"), QStringLiteral());
+ INSERT(Settings, controller_applet_mode, tr("Controller configuration"), QStringLiteral());
+ INSERT(Settings, data_erase_applet_mode, tr("Data erase"), QStringLiteral());
+ INSERT(Settings, error_applet_mode, tr("Error"), QStringLiteral());
+ INSERT(Settings, net_connect_applet_mode, tr("Net connect"), QStringLiteral());
+ INSERT(Settings, player_select_applet_mode, tr("Player select"), QStringLiteral());
+ INSERT(Settings, swkbd_applet_mode, tr("Software keyboard"), QStringLiteral());
+ INSERT(Settings, mii_edit_applet_mode, tr("Mii Edit"), QStringLiteral());
+ INSERT(Settings, web_applet_mode, tr("Online web"), QStringLiteral());
+ INSERT(Settings, shop_applet_mode, tr("Shop"), QStringLiteral());
+ INSERT(Settings, photo_viewer_applet_mode, tr("Photo viewer"), QStringLiteral());
+ INSERT(Settings, offline_web_applet_mode, tr("Offline web"), QStringLiteral());
+ INSERT(Settings, login_share_applet_mode, tr("Login share"), QStringLiteral());
+ INSERT(Settings, wifi_web_auth_applet_mode, tr("Wifi web auth"), QStringLiteral());
+ INSERT(Settings, my_page_applet_mode, tr("My page"), QStringLiteral());
+
// Audio
INSERT(Settings, sink_id, tr("Output Engine:"), QStringLiteral());
INSERT(Settings, audio_output_device_id, tr("Output Device:"), QStringLiteral());
@@ -37,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
@@ -63,34 +95,75 @@ 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, vsync_mode, tr("VSync Mode:"),
tr("FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen "
@@ -104,22 +177,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(
@@ -140,19 +220,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
@@ -170,14 +258,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());
@@ -203,6 +296,11 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent) {
#define PAIR(ENUM, VALUE, TRANSLATION) {static_cast<u32>(Settings::ENUM::VALUE), (TRANSLATION)}
// Intentionally skipping VSyncMode to let the UI fill that one out
+ translations->insert({Settings::EnumMetadata<Settings::AppletMode>::Index(),
+ {
+ PAIR(AppletMode, HLE, tr("Custom frontend")),
+ PAIR(AppletMode, LLE, tr("Real applet")),
+ }});
translations->insert({Settings::EnumMetadata<Settings::AstcDecodeMode>::Index(),
{
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"/>