diff options
Diffstat (limited to 'src/yuzu')
| -rw-r--r-- | src/yuzu/CMakeLists.txt | 3 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_applets.cpp | 86 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_applets.h | 48 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_applets.ui | 65 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_dialog.cpp | 7 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_dialog.h | 2 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_input.cpp | 3 | ||||
| -rw-r--r-- | src/yuzu/configuration/qt_config.cpp | 1 | ||||
| -rw-r--r-- | src/yuzu/configuration/shared_translation.cpp | 180 | ||||
| -rw-r--r-- | src/yuzu/hotkeys.cpp | 6 | ||||
| -rw-r--r-- | src/yuzu/main.cpp | 251 | ||||
| -rw-r--r-- | src/yuzu/main.h | 2 | ||||
| -rw-r--r-- | src/yuzu/main.ui | 24 |
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 &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"/> |
