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"/>  | 
