Kaynağa Gözat

Rebase to master

Ameer 6 yıl önce
ebeveyn
işleme
93fe982a0c
100 değiştirilmiş dosya ile 2100 ekleme ve 826 silme
  1. 19 7
      CMakeLists.txt
  2. 0 1
      CMakeModules/CopyYuzuQt5Deps.cmake
  3. 2 2
      README.md
  4. 1 1
      externals/dynarmic
  5. 1 1
      src/audio_core/cubeb_sink.cpp
  6. 8 18
      src/audio_core/stream.cpp
  7. 2 2
      src/audio_core/stream.h
  8. 3 1
      src/common/alignment.h
  9. 26 4
      src/core/arm/dynarmic/arm_dynarmic_32.cpp
  10. 28 6
      src/core/arm/dynarmic/arm_dynarmic_64.cpp
  11. 3 3
      src/core/core.cpp
  12. 2 4
      src/core/core_timing.cpp
  13. 8 4
      src/core/crypto/key_manager.cpp
  14. 13 10
      src/core/file_sys/bis_factory.cpp
  15. 3 1
      src/core/file_sys/sdmc_factory.cpp
  16. 17 10
      src/core/file_sys/vfs_real.cpp
  17. 1 1
      src/core/frontend/framebuffer_layout.cpp
  18. 1 1
      src/core/hle/kernel/process.cpp
  19. 10 11
      src/core/hle/kernel/scheduler.cpp
  20. 36 6
      src/core/hle/service/am/am.cpp
  21. 1 0
      src/core/hle/service/am/am.h
  22. 82 6
      src/core/hle/service/nim/nim.cpp
  23. 1 0
      src/core/hle/service/npns/npns.cpp
  24. 15 10
      src/core/hle/service/ns/ns.cpp
  25. 3 3
      src/core/hle/service/ns/ns.h
  26. 1 1
      src/core/hle/service/ns/pl_u.cpp
  27. 6 5
      src/core/hle/service/nvdrv/interface.cpp
  28. 2 2
      src/core/hle/service/nvdrv/interface.h
  29. 4 4
      src/core/hle/service/nvdrv/nvmemp.cpp
  30. 2 2
      src/core/hle/service/nvdrv/nvmemp.h
  31. 3 0
      src/core/hle/service/pcie/pcie.cpp
  32. 3 0
      src/core/hle/service/pcv/pcv.cpp
  33. 17 17
      src/core/hle/service/pm/pm.cpp
  34. 5 0
      src/core/hle/service/prepo/prepo.cpp
  35. 2 0
      src/core/hle/service/psc/psc.cpp
  36. 1 0
      src/core/hle/service/ptm/psm.cpp
  37. 5 5
      src/core/hle/service/set/set.cpp
  38. 11 10
      src/core/hle/service/sm/controller.cpp
  39. 3 3
      src/core/hle/service/sm/controller.h
  40. 6 0
      src/core/hle/service/sockets/nsd.cpp
  41. 15 10
      src/core/hle/service/sockets/sfdnsres.cpp
  42. 1 1
      src/core/hle/service/sockets/sfdnsres.h
  43. 1 1
      src/core/hle/service/spl/module.cpp
  44. 20 19
      src/core/hle/service/spl/spl.cpp
  45. 7 0
      src/core/hle/service/time/time.cpp
  46. 16 15
      src/core/hle/service/usb/usb.cpp
  47. 12 10
      src/core/hle/service/vi/vi.cpp
  48. 1 0
      src/core/hle/service/vi/vi_u.cpp
  49. 55 47
      src/core/hle/service/wlan/wlan.cpp
  50. 3 2
      src/core/perf_stats.cpp
  51. 63 21
      src/core/settings.cpp
  52. 98 73
      src/core/settings.h
  53. 16 11
      src/core/telemetry_session.cpp
  54. 2 1
      src/input_common/CMakeLists.txt
  55. 6 5
      src/input_common/gcadapter/gc_adapter.cpp
  56. 9 6
      src/input_common/gcadapter/gc_adapter.h
  57. 29 16
      src/input_common/gcadapter/gc_poller.cpp
  58. 1 1
      src/input_common/gcadapter/gc_poller.h
  59. 0 1
      src/input_common/main.cpp
  60. 1 1
      src/input_common/udp/client.cpp
  61. 4 2
      src/video_core/buffer_cache/buffer_cache.h
  62. 1 1
      src/video_core/gpu.cpp
  63. 1 1
      src/video_core/query_cache.h
  64. 1 1
      src/video_core/renderer_base.cpp
  65. 4 4
      src/video_core/renderer_opengl/gl_device.cpp
  66. 1 1
      src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
  67. 4 4
      src/video_core/renderer_opengl/renderer_opengl.cpp
  68. 1 1
      src/video_core/renderer_vulkan/renderer_vulkan.cpp
  69. 6 6
      src/video_core/renderer_vulkan/vk_rasterizer.cpp
  70. 1 1
      src/video_core/renderer_vulkan/vk_stream_buffer.cpp
  71. 1 1
      src/video_core/texture_cache/texture_cache.h
  72. 1 1
      src/video_core/textures/texture.cpp
  73. 4 4
      src/video_core/video_core.cpp
  74. 19 6
      src/yuzu/CMakeLists.txt
  75. 4 4
      src/yuzu/bootmanager.cpp
  76. 272 152
      src/yuzu/configuration/config.cpp
  77. 28 1
      src/yuzu/configuration/config.h
  78. 76 0
      src/yuzu/configuration/configuration_shared.cpp
  79. 36 0
      src/yuzu/configuration/configuration_shared.h
  80. 22 0
      src/yuzu/configuration/configure.ui
  81. 71 11
      src/yuzu/configuration/configure_audio.cpp
  82. 2 0
      src/yuzu/configuration/configure_audio.h
  83. 32 18
      src/yuzu/configuration/configure_audio.ui
  84. 61 0
      src/yuzu/configuration/configure_cpu.cpp
  85. 33 0
      src/yuzu/configuration/configure_cpu.h
  86. 92 0
      src/yuzu/configuration/configure_cpu.ui
  87. 65 0
      src/yuzu/configuration/configure_cpu_debug.cpp
  88. 31 0
      src/yuzu/configuration/configure_cpu_debug.h
  89. 174 0
      src/yuzu/configuration/configure_cpu_debug.ui
  90. 0 2
      src/yuzu/configuration/configure_debug.cpp
  91. 0 7
      src/yuzu/configuration/configure_debug.ui
  92. 8 1
      src/yuzu/configuration/configure_dialog.cpp
  93. 0 27
      src/yuzu/configuration/configure_filesystem.cpp
  94. 0 121
      src/yuzu/configuration/configure_filesystem.ui
  95. 66 14
      src/yuzu/configuration/configure_general.cpp
  96. 2 0
      src/yuzu/configuration/configure_general.h
  97. 132 18
      src/yuzu/configuration/configure_graphics.cpp
  98. 2 0
      src/yuzu/configuration/configure_graphics.h
  99. 36 0
      src/yuzu/configuration/configure_graphics.ui
  100. 93 13
      src/yuzu/configuration/configure_graphics_advanced.cpp

+ 19 - 7
CMakeLists.txt

@@ -118,8 +118,17 @@ message(STATUS "Target architecture: ${ARCHITECTURE}")
 # Configure C++ standard
 # ===========================
 
-set(CMAKE_CXX_STANDARD 17)
-set(CMAKE_CXX_STANDARD_REQUIRED ON)
+# boost asio's concept usage doesn't play nicely with some compilers yet.
+add_definitions(-DBOOST_ASIO_DISABLE_CONCEPTS)
+if (MSVC)
+    add_compile_options(/std:c++latest)
+
+    # cubeb and boost still make use of deprecated result_of.
+    add_definitions(-D_HAS_DEPRECATED_RESULT_OF)
+else()
+    set(CMAKE_CXX_STANDARD 20)
+    set(CMAKE_CXX_STANDARD_REQUIRED ON)
+endif()
 
 # Output binaries to bin/
 set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
@@ -151,7 +160,7 @@ macro(yuzu_find_packages)
     #    Cmake Pkg Prefix  Version     Conan Pkg
         "Boost             1.71        boost/1.72.0"
         "Catch2            2.11        catch2/2.11.0"
-        "fmt               6.2         fmt/6.2.0"
+        "fmt               7.0         fmt/7.0.1"
     # can't use until https://github.com/bincrafters/community/issues/1173
         #"libzip            1.5         libzip/1.5.2@bincrafters/stable"
         "lz4               1.8         lz4/1.9.2"
@@ -211,7 +220,7 @@ if(ENABLE_QT)
 
         set(QT_PREFIX_HINT HINTS "${QT_PREFIX}")
     endif()
-    find_package(Qt5 5.9 COMPONENTS Widgets OpenGL ${QT_PREFIX_HINT})
+    find_package(Qt5 5.9 COMPONENTS Widgets ${QT_PREFIX_HINT})
     if (YUZU_USE_QT_WEB_ENGINE)
         find_package(Qt5 COMPONENTS WebEngineCore WebEngineWidgets)
     endif()
@@ -287,7 +296,7 @@ if (CONAN_REQUIRED_LIBS)
     if(ENABLE_QT)
         list(APPEND CMAKE_MODULE_PATH "${CONAN_QT_ROOT_RELEASE}")
         list(APPEND CMAKE_PREFIX_PATH "${CONAN_QT_ROOT_RELEASE}")
-        find_package(Qt5 5.9 REQUIRED COMPONENTS Widgets OpenGL)
+        find_package(Qt5 5.9 REQUIRED COMPONENTS Widgets)
         if (YUZU_USE_QT_WEB_ENGINE)
             find_package(Qt5 REQUIRED COMPONENTS WebEngineCore WebEngineWidgets)
         endif()
@@ -330,13 +339,16 @@ elseif(SDL2_FOUND)
 endif()
 
 # Ensure libusb is properly configured (based on dolphin libusb include)
-find_package(LibUSB)
+if(NOT APPLE)
+    include(FindPkgConfig)
+    find_package(LibUSB)
+endif()
 if (NOT LIBUSB_FOUND)
     add_subdirectory(externals/libusb)
+    set(LIBUSB_INCLUDE_DIR "")
     set(LIBUSB_LIBRARIES usb)
 endif()
 
-
 # Prefer the -pthread flag on Linux.
 set(THREADS_PREFER_PTHREAD_FLAG ON)
 find_package(Threads REQUIRED)

+ 0 - 1
CMakeModules/CopyYuzuQt5Deps.cmake

@@ -15,7 +15,6 @@ function(copy_yuzu_Qt5_deps target_dir)
         icuuc*.dll
         Qt5Core$<$<CONFIG:Debug>:d>.*
         Qt5Gui$<$<CONFIG:Debug>:d>.*
-        Qt5OpenGL$<$<CONFIG:Debug>:d>.*
         Qt5Widgets$<$<CONFIG:Debug>:d>.*
     )
 

+ 2 - 2
README.md

@@ -2,7 +2,7 @@ yuzu emulator
 =============
 [![Travis CI Build Status](https://travis-ci.com/yuzu-emu/yuzu.svg?branch=master)](https://travis-ci.com/yuzu-emu/yuzu)
 [![Azure Mainline CI Build Status](https://dev.azure.com/yuzu-emu/yuzu/_apis/build/status/yuzu%20mainline?branchName=master)](https://dev.azure.com/yuzu-emu/yuzu/)
-[![Discord](https://img.shields.io/discord/398318088170242053?color=%237289DA&label=yuzu&logo=discord&logoColor=white)](https://discord.gg/XQV6dn9)
+[![Discord](https://img.shields.io/discord/398318088170242053?color=%237289DA&label=yuzu&logo=discord&logoColor=white)](https://discord.com/invite/u77vRWY)
 
 yuzu is an experimental open-source emulator for the Nintendo Switch from the creators of [Citra](https://citra-emu.org/).
 
@@ -16,7 +16,7 @@ yuzu is licensed under the GPLv2 (or any later version). Refer to the license.tx
 
 Check out our [website](https://yuzu-emu.org/)!
 
-For development discussion, please join us on [Discord](https://discord.gg/XQV6dn9).
+For development discussion, please join us on [Discord](https://discord.com/invite/u77vRWY).
 
 ### Development
 

+ 1 - 1
externals/dynarmic

@@ -1 +1 @@
-Subproject commit 4f967387c07365b7ea35d2fa3e19b7df8872a09b
+Subproject commit 82417da7803e2cf18efc28a1cd3f3d0a4b6045ae

+ 1 - 1
src/audio_core/cubeb_sink.cpp

@@ -193,7 +193,7 @@ long CubebSinkStream::DataCallback(cubeb_stream* stream, void* user_data, const
     const std::size_t samples_to_write = num_channels * num_frames;
     std::size_t samples_written;
 
-    if (Settings::values.enable_audio_stretching) {
+    if (Settings::values.enable_audio_stretching.GetValue()) {
         const std::vector<s16> in{impl->queue.Pop()};
         const std::size_t num_in{in.size() / num_channels};
         s16* const out{reinterpret_cast<s16*>(buffer)};

+ 8 - 18
src/audio_core/stream.cpp

@@ -38,7 +38,7 @@ Stream::Stream(Core::Timing::CoreTiming& core_timing, u32 sample_rate, Format fo
       sink_stream{sink_stream}, core_timing{core_timing}, name{std::move(name_)} {
 
     release_event = Core::Timing::CreateEvent(
-        name, [this](u64 userdata, s64 cycles_late) { ReleaseActiveBuffer(); });
+        name, [this](u64 userdata, s64 cycles_late) { ReleaseActiveBuffer(cycles_late); });
 }
 
 void Stream::Play() {
@@ -66,15 +66,6 @@ s64 Stream::GetBufferReleaseNS(const Buffer& buffer) const {
     return ns.count();
 }
 
-s64 Stream::GetBufferReleaseNSHostTiming(const Buffer& buffer) const {
-    const std::size_t num_samples{buffer.GetSamples().size() / GetNumChannels()};
-    /// DSP signals before playing the last sample, in HLE we emulate this in this way
-    s64 base_samples = std::max<s64>(static_cast<s64>(num_samples) - 1, 0);
-    const auto ns =
-        std::chrono::nanoseconds((static_cast<u64>(base_samples) * 1000000000ULL) / sample_rate);
-    return ns.count();
-}
-
 static void VolumeAdjustSamples(std::vector<s16>& samples, float game_volume) {
     const float volume{std::clamp(Settings::Volume() - (1.0f - game_volume), 0.0f, 1.0f)};
 
@@ -89,7 +80,7 @@ static void VolumeAdjustSamples(std::vector<s16>& samples, float game_volume) {
     }
 }
 
-void Stream::PlayNextBuffer() {
+void Stream::PlayNextBuffer(s64 cycles_late) {
     if (!IsPlaying()) {
         // Ensure we are in playing state before playing the next buffer
         sink_stream.Flush();
@@ -114,18 +105,17 @@ void Stream::PlayNextBuffer() {
 
     sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples());
 
-    if (core_timing.IsHostTiming()) {
-        core_timing.ScheduleEvent(GetBufferReleaseNSHostTiming(*active_buffer), release_event, {});
-    } else {
-        core_timing.ScheduleEvent(GetBufferReleaseNS(*active_buffer), release_event, {});
-    }
+    core_timing.ScheduleEvent(
+        GetBufferReleaseNS(*active_buffer) -
+            (Settings::values.enable_audio_stretching.GetValue() ? 0 : cycles_late),
+        release_event, {});
 }
 
-void Stream::ReleaseActiveBuffer() {
+void Stream::ReleaseActiveBuffer(s64 cycles_late) {
     ASSERT(active_buffer);
     released_buffers.push(std::move(active_buffer));
     release_callback();
-    PlayNextBuffer();
+    PlayNextBuffer(cycles_late);
 }
 
 bool Stream::QueueBuffer(BufferPtr&& buffer) {

+ 2 - 2
src/audio_core/stream.h

@@ -90,10 +90,10 @@ public:
 
 private:
     /// Plays the next queued buffer in the audio stream, starting playback if necessary
-    void PlayNextBuffer();
+    void PlayNextBuffer(s64 cycles_late = 0);
 
     /// Releases the actively playing buffer, signalling that it has been completed
-    void ReleaseActiveBuffer();
+    void ReleaseActiveBuffer(s64 cycles_late = 0);
 
     /// Gets the number of core cycles when the specified buffer will be released
     s64 GetBufferReleaseNS(const Buffer& buffer) const;

+ 3 - 1
src/common/alignment.h

@@ -11,7 +11,9 @@ namespace Common {
 template <typename T>
 constexpr T AlignUp(T value, std::size_t size) {
     static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
-    return static_cast<T>(value + (size - value % size) % size);
+    auto mod{static_cast<T>(value % size)};
+    value -= mod;
+    return static_cast<T>(mod == T{0} ? value : value + size);
 }
 
 template <typename T>

+ 26 - 4
src/core/arm/dynarmic/arm_dynarmic_32.cpp

@@ -142,10 +142,32 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable&
     // Timing
     config.wall_clock_cntpct = uses_wall_clock;
 
-    // Optimizations
-    if (Settings::values.disable_cpu_opt) {
-        config.enable_optimizations = false;
-        config.enable_fast_dispatch = false;
+    // Safe optimizations
+    if (Settings::values.cpu_accuracy != Settings::CPUAccuracy::Accurate) {
+        if (!Settings::values.cpuopt_page_tables) {
+            config.page_table = nullptr;
+        }
+        if (!Settings::values.cpuopt_block_linking) {
+            config.optimizations &= ~Dynarmic::OptimizationFlag::BlockLinking;
+        }
+        if (!Settings::values.cpuopt_return_stack_buffer) {
+            config.optimizations &= ~Dynarmic::OptimizationFlag::ReturnStackBuffer;
+        }
+        if (!Settings::values.cpuopt_fast_dispatcher) {
+            config.optimizations &= ~Dynarmic::OptimizationFlag::FastDispatch;
+        }
+        if (!Settings::values.cpuopt_context_elimination) {
+            config.optimizations &= ~Dynarmic::OptimizationFlag::GetSetElimination;
+        }
+        if (!Settings::values.cpuopt_const_prop) {
+            config.optimizations &= ~Dynarmic::OptimizationFlag::ConstProp;
+        }
+        if (!Settings::values.cpuopt_misc_ir) {
+            config.optimizations &= ~Dynarmic::OptimizationFlag::MiscIROpt;
+        }
+        if (!Settings::values.cpuopt_reduce_misalign_checks) {
+            config.only_detect_misalignment_via_page_table_on_page_boundary = false;
+        }
     }
 
     return std::make_unique<Dynarmic::A32::Jit>(config);

+ 28 - 6
src/core/arm/dynarmic/arm_dynarmic_64.cpp

@@ -191,15 +191,37 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable&
     // Unpredictable instructions
     config.define_unpredictable_behaviour = true;
 
-    // Optimizations
-    if (Settings::values.disable_cpu_opt) {
-        config.enable_optimizations = false;
-        config.enable_fast_dispatch = false;
-    }
-
     // Timing
     config.wall_clock_cntpct = uses_wall_clock;
 
+    // Safe optimizations
+    if (Settings::values.cpu_accuracy != Settings::CPUAccuracy::Accurate) {
+        if (!Settings::values.cpuopt_page_tables) {
+            config.page_table = nullptr;
+        }
+        if (!Settings::values.cpuopt_block_linking) {
+            config.optimizations &= ~Dynarmic::OptimizationFlag::BlockLinking;
+        }
+        if (!Settings::values.cpuopt_return_stack_buffer) {
+            config.optimizations &= ~Dynarmic::OptimizationFlag::ReturnStackBuffer;
+        }
+        if (!Settings::values.cpuopt_fast_dispatcher) {
+            config.optimizations &= ~Dynarmic::OptimizationFlag::FastDispatch;
+        }
+        if (!Settings::values.cpuopt_context_elimination) {
+            config.optimizations &= ~Dynarmic::OptimizationFlag::GetSetElimination;
+        }
+        if (!Settings::values.cpuopt_const_prop) {
+            config.optimizations &= ~Dynarmic::OptimizationFlag::ConstProp;
+        }
+        if (!Settings::values.cpuopt_misc_ir) {
+            config.optimizations &= ~Dynarmic::OptimizationFlag::MiscIROpt;
+        }
+        if (!Settings::values.cpuopt_reduce_misalign_checks) {
+            config.only_detect_misalignment_via_page_table_on_page_boundary = false;
+        }
+    }
+
     return std::make_shared<Dynarmic::A64::Jit>(config);
 }
 

+ 3 - 3
src/core/core.cpp

@@ -147,8 +147,8 @@ struct System::Impl {
 
         device_memory = std::make_unique<Core::DeviceMemory>(system);
 
-        is_multicore = Settings::values.use_multi_core;
-        is_async_gpu = is_multicore || Settings::values.use_asynchronous_gpu_emulation;
+        is_multicore = Settings::values.use_multi_core.GetValue();
+        is_async_gpu = is_multicore || Settings::values.use_asynchronous_gpu_emulation.GetValue();
 
         kernel.SetMulticore(is_multicore);
         cpu_manager.SetMulticore(is_multicore);
@@ -162,7 +162,7 @@ struct System::Impl {
         const auto current_time = std::chrono::duration_cast<std::chrono::seconds>(
             std::chrono::system_clock::now().time_since_epoch());
         Settings::values.custom_rtc_differential =
-            Settings::values.custom_rtc.value_or(current_time) - current_time;
+            Settings::values.custom_rtc.GetValue().value_or(current_time) - current_time;
 
         // Create a default fs if one doesn't already exist.
         if (virtual_filesystem == nullptr)

+ 2 - 4
src/core/core_timing.cpp

@@ -172,7 +172,7 @@ void CoreTiming::ClearPendingEvents() {
 }
 
 void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) {
-    basic_lock.lock();
+    std::scoped_lock lock{basic_lock};
 
     const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) {
         return e.type.lock().get() == event_type.get();
@@ -183,12 +183,10 @@ void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) {
         event_queue.erase(itr, event_queue.end());
         std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>());
     }
-    basic_lock.unlock();
 }
 
 std::optional<s64> CoreTiming::Advance() {
-    std::scoped_lock advance_scope{advance_lock};
-    std::scoped_lock basic_scope{basic_lock};
+    std::scoped_lock lock{advance_lock, basic_lock};
     global_timer = GetGlobalTimeNs().count();
 
     while (!event_queue.empty() && event_queue.front().time <= global_timer) {

+ 8 - 4
src/core/crypto/key_manager.cpp

@@ -695,8 +695,9 @@ void KeyManager::WriteKeyToFile(KeyCategory category, std::string_view keyname,
 }
 
 void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
-    if (s128_keys.find({id, field1, field2}) != s128_keys.end())
+    if (s128_keys.find({id, field1, field2}) != s128_keys.end() || key == Key128{}) {
         return;
+    }
     if (id == S128KeyType::Titlekey) {
         Key128 rights_id;
         std::memcpy(rights_id.data(), &field2, sizeof(u64));
@@ -716,8 +717,9 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
             return std::tie(elem.second.type, elem.second.field1, elem.second.field2) ==
                    std::tie(id, field1, field2);
         });
-    if (iter2 != s128_file_id.end())
+    if (iter2 != s128_file_id.end()) {
         WriteKeyToFile(category, iter2->first, key);
+    }
 
     // Variable cases
     if (id == S128KeyType::KeyArea) {
@@ -745,16 +747,18 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
 }
 
 void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) {
-    if (s256_keys.find({id, field1, field2}) != s256_keys.end())
+    if (s256_keys.find({id, field1, field2}) != s256_keys.end() || key == Key256{}) {
         return;
+    }
     const auto iter = std::find_if(
         s256_file_id.begin(), s256_file_id.end(),
         [&id, &field1, &field2](const std::pair<std::string, KeyIndex<S256KeyType>> elem) {
             return std::tie(elem.second.type, elem.second.field1, elem.second.field2) ==
                    std::tie(id, field1, field2);
         });
-    if (iter != s256_file_id.end())
+    if (iter != s256_file_id.end()) {
         WriteKeyToFile(KeyCategory::Standard, iter->first, key);
+    }
     s256_keys[{id, field1, field2}] = key;
 }
 

+ 13 - 10
src/core/file_sys/bis_factory.cpp

@@ -12,6 +12,10 @@
 
 namespace FileSys {
 
+constexpr u64 NAND_USER_SIZE = 0x680000000;  // 26624 MiB
+constexpr u64 NAND_SYSTEM_SIZE = 0xA0000000; // 2560 MiB
+constexpr u64 NAND_TOTAL_SIZE = 0x747C00000; // 29820 MiB
+
 BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_, VirtualDir dump_root_)
     : nand_root(std::move(nand_root_)), load_root(std::move(load_root_)),
       dump_root(std::move(dump_root_)),
@@ -110,30 +114,29 @@ VirtualDir BISFactory::GetImageDirectory() const {
 
 u64 BISFactory::GetSystemNANDFreeSpace() const {
     const auto sys_dir = GetOrCreateDirectoryRelative(nand_root, "/system");
-    if (sys_dir == nullptr)
-        return 0;
+    if (sys_dir == nullptr) {
+        return GetSystemNANDTotalSpace();
+    }
 
     return GetSystemNANDTotalSpace() - sys_dir->GetSize();
 }
 
 u64 BISFactory::GetSystemNANDTotalSpace() const {
-    return static_cast<u64>(Settings::values.nand_system_size);
+    return NAND_SYSTEM_SIZE;
 }
 
 u64 BISFactory::GetUserNANDFreeSpace() const {
-    const auto usr_dir = GetOrCreateDirectoryRelative(nand_root, "/user");
-    if (usr_dir == nullptr)
-        return 0;
-
-    return GetUserNANDTotalSpace() - usr_dir->GetSize();
+    // For some reason games such as BioShock 1 checks whether this is exactly 0x680000000 bytes.
+    // Set the free space to be 1 MiB less than the total as a workaround to this issue.
+    return GetUserNANDTotalSpace() - 0x100000;
 }
 
 u64 BISFactory::GetUserNANDTotalSpace() const {
-    return static_cast<u64>(Settings::values.nand_user_size);
+    return NAND_USER_SIZE;
 }
 
 u64 BISFactory::GetFullNANDTotalSpace() const {
-    return static_cast<u64>(Settings::values.nand_total_size);
+    return NAND_TOTAL_SIZE;
 }
 
 VirtualDir BISFactory::GetBCATDirectory(u64 title_id) const {

+ 3 - 1
src/core/file_sys/sdmc_factory.cpp

@@ -10,6 +10,8 @@
 
 namespace FileSys {
 
+constexpr u64 SDMC_TOTAL_SIZE = 0x10000000000; // 1 TiB
+
 SDMCFactory::SDMCFactory(VirtualDir dir_)
     : dir(std::move(dir_)), contents(std::make_unique<RegisteredCache>(
                                 GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/registered"),
@@ -46,7 +48,7 @@ u64 SDMCFactory::GetSDMCFreeSpace() const {
 }
 
 u64 SDMCFactory::GetSDMCTotalSpace() const {
-    return static_cast<u64>(Settings::values.sdmc_size);
+    return SDMC_TOTAL_SIZE;
 }
 
 } // namespace FileSys

+ 17 - 10
src/core/file_sys/vfs_real.cpp

@@ -112,19 +112,26 @@ VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_
     const auto new_path =
         FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault);
 
-    if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) ||
-        FileUtil::IsDirectory(old_path) || !FileUtil::Rename(old_path, new_path))
-        return nullptr;
-
     if (cache.find(old_path) != cache.end()) {
-        auto cached = cache[old_path];
-        if (!cached.expired()) {
-            auto file = cached.lock();
-            file->Open(new_path, "r+b");
-            cache.erase(old_path);
-            cache[new_path] = file;
+        auto file = cache[old_path].lock();
+
+        if (!cache[old_path].expired()) {
+            file->Close();
+        }
+
+        if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) ||
+            FileUtil::IsDirectory(old_path) || !FileUtil::Rename(old_path, new_path)) {
+            return nullptr;
         }
+
+        cache.erase(old_path);
+        file->Open(new_path, "r+b");
+        cache[new_path] = file;
+    } else {
+        UNREACHABLE();
+        return nullptr;
     }
+
     return OpenFile(new_path, Mode::ReadWrite);
 }
 

+ 1 - 1
src/core/frontend/framebuffer_layout.cpp

@@ -29,7 +29,7 @@ FramebufferLayout DefaultFrameLayout(u32 width, u32 height) {
 
     const float window_aspect_ratio = static_cast<float>(height) / width;
     const float emulation_aspect_ratio = EmulationAspectRatio(
-        static_cast<AspectRatio>(Settings::values.aspect_ratio), window_aspect_ratio);
+        static_cast<AspectRatio>(Settings::values.aspect_ratio.GetValue()), window_aspect_ratio);
 
     const Common::Rectangle<u32> screen_window_area{0, 0, width, height};
     Common::Rectangle<u32> screen = MaxRectangle(screen_window_area, emulation_aspect_ratio);

+ 1 - 1
src/core/hle/kernel/process.cpp

@@ -123,7 +123,7 @@ std::shared_ptr<Process> Process::Create(Core::System& system, std::string name,
                                                               : kernel.CreateNewUserProcessID();
     process->capabilities.InitializeForMetadatalessProcess();
 
-    std::mt19937 rng(Settings::values.rng_seed.value_or(0));
+    std::mt19937 rng(Settings::values.rng_seed.GetValue().value_or(0));
     std::uniform_int_distribution<u64> distribution;
     std::generate(process->random_entropy.begin(), process->random_entropy.end(),
                   [&] { return distribution(rng); });

+ 10 - 11
src/core/hle/kernel/scheduler.cpp

@@ -6,6 +6,7 @@
 // licensed under GPLv2 or later under exception provided by the author.
 
 #include <algorithm>
+#include <mutex>
 #include <set>
 #include <unordered_set>
 #include <utility>
@@ -31,22 +32,20 @@ GlobalScheduler::GlobalScheduler(KernelCore& kernel) : kernel{kernel} {}
 GlobalScheduler::~GlobalScheduler() = default;
 
 void GlobalScheduler::AddThread(std::shared_ptr<Thread> thread) {
-    global_list_guard.lock();
+    std::scoped_lock lock{global_list_guard};
     thread_list.push_back(std::move(thread));
-    global_list_guard.unlock();
 }
 
 void GlobalScheduler::RemoveThread(std::shared_ptr<Thread> thread) {
-    global_list_guard.lock();
+    std::scoped_lock lock{global_list_guard};
     thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread),
                       thread_list.end());
-    global_list_guard.unlock();
 }
 
 u32 GlobalScheduler::SelectThreads() {
     ASSERT(is_locked);
     const auto update_thread = [](Thread* thread, Scheduler& sched) {
-        sched.guard.lock();
+        std::scoped_lock lock{sched.guard};
         if (thread != sched.selected_thread_set.get()) {
             if (thread == nullptr) {
                 ++sched.idle_selection_count;
@@ -57,7 +56,6 @@ u32 GlobalScheduler::SelectThreads() {
             sched.is_context_switch_pending || (sched.selected_thread_set != sched.current_thread);
         sched.is_context_switch_pending = reschedule_pending;
         std::atomic_thread_fence(std::memory_order_seq_cst);
-        sched.guard.unlock();
         return reschedule_pending;
     };
     if (!is_reselection_pending.load()) {
@@ -757,11 +755,12 @@ void Scheduler::OnSwitch(void* this_scheduler) {
 
 void Scheduler::SwitchToCurrent() {
     while (true) {
-        guard.lock();
-        selected_thread = selected_thread_set;
-        current_thread = selected_thread;
-        is_context_switch_pending = false;
-        guard.unlock();
+        {
+            std::scoped_lock lock{guard};
+            selected_thread = selected_thread_set;
+            current_thread = selected_thread;
+            is_context_switch_pending = false;
+        }
         while (!is_context_switch_pending) {
             if (current_thread != nullptr && !current_thread->IsHLEThread()) {
                 current_thread->context_guard.lock();

+ 36 - 6
src/core/hle/service/am/am.cpp

@@ -272,7 +272,7 @@ ISelfController::ISelfController(Core::System& system,
         {41, nullptr, "IsSystemBufferSharingEnabled"},
         {42, nullptr, "GetSystemSharedLayerHandle"},
         {43, nullptr, "GetSystemSharedBufferHandle"},
-        {44, nullptr, "CreateManagedDisplaySeparableLayer"},
+        {44, &ISelfController::CreateManagedDisplaySeparableLayer, "CreateManagedDisplaySeparableLayer"},
         {45, nullptr, "SetManagedDisplayLayerSeparationMode"},
         {50, &ISelfController::SetHandlesRequestToDisplay, "SetHandlesRequestToDisplay"},
         {51, nullptr, "ApproveToDisplay"},
@@ -462,6 +462,24 @@ void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx)
     rb.Push(*layer_id);
 }
 
+void ISelfController::CreateManagedDisplaySeparableLayer(Kernel::HLERequestContext& ctx) {
+    LOG_WARNING(Service_AM, "(STUBBED) called");
+
+    // TODO(Subv): Find out how AM determines the display to use, for now just
+    // create the layer in the Default display.
+    // This calls nn::vi::CreateRecordingLayer() which creates another layer.
+    // Currently we do not support more than 1 layer per display, output 1 layer id for now.
+    // Outputting 1 layer id instead of the expected 2 has not been observed to cause any adverse
+    // side effects.
+    // TODO: Support multiple layers
+    const auto display_id = nvflinger->OpenDisplay("Default");
+    const auto layer_id = nvflinger->CreateLayer(*display_id);
+
+    IPC::ResponseBuilder rb{ctx, 4};
+    rb.Push(RESULT_SUCCESS);
+    rb.Push(*layer_id);
+}
+
 void ISelfController::SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx) {
     LOG_WARNING(Service_AM, "(STUBBED) called");
 
@@ -731,14 +749,14 @@ void ICommonStateGetter::GetDefaultDisplayResolution(Kernel::HLERequestContext&
 
     if (Settings::values.use_docked_mode) {
         rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth) *
-                static_cast<u32>(Settings::values.resolution_factor));
+                static_cast<u32>(Settings::values.resolution_factor.GetValue()));
         rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight) *
-                static_cast<u32>(Settings::values.resolution_factor));
+                static_cast<u32>(Settings::values.resolution_factor.GetValue()));
     } else {
         rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth) *
-                static_cast<u32>(Settings::values.resolution_factor));
+                static_cast<u32>(Settings::values.resolution_factor.GetValue()));
         rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight) *
-                static_cast<u32>(Settings::values.resolution_factor));
+                static_cast<u32>(Settings::values.resolution_factor.GetValue()));
     }
 }
 
@@ -1389,7 +1407,19 @@ void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) {
     u32 supported_languages = 0;
     FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID()};
 
-    const auto res = pm.GetControlMetadata();
+    const auto res = [this] {
+        const auto title_id = system.CurrentProcess()->GetTitleID();
+
+        FileSys::PatchManager pm{title_id};
+        auto res = pm.GetControlMetadata();
+        if (res.first != nullptr) {
+            return res;
+        }
+
+        FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(title_id)};
+        return pm_update.GetControlMetadata();
+    }();
+
     if (res.first != nullptr) {
         supported_languages = res.first->GetSupportedLanguages();
     }

+ 1 - 0
src/core/hle/service/am/am.h

@@ -140,6 +140,7 @@ private:
     void SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx);
     void SetAlbumImageOrientation(Kernel::HLERequestContext& ctx);
     void CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx);
+    void CreateManagedDisplaySeparableLayer(Kernel::HLERequestContext& ctx);
     void SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx);
     void SetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx);
     void GetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx);

+ 82 - 6
src/core/hle/service/nim/nim.cpp

@@ -121,11 +121,83 @@ public:
             {39, nullptr, "PrepareShutdown"},
             {40, nullptr, "ListApplyDeltaTask"},
             {41, nullptr, "ClearNotEnoughSpaceStateOfApplyDeltaTask"},
-            {42, nullptr, "Unknown1"},
-            {43, nullptr, "Unknown2"},
-            {44, nullptr, "Unknown3"},
-            {45, nullptr, "Unknown4"},
-            {46, nullptr, "Unknown5"},
+            {42, nullptr, "Unknown42"},
+            {43, nullptr, "Unknown43"},
+            {44, nullptr, "Unknown44"},
+            {45, nullptr, "Unknown45"},
+            {46, nullptr, "Unknown46"},
+            {47, nullptr, "Unknown47"},
+            {48, nullptr, "Unknown48"},
+            {49, nullptr, "Unknown49"},
+            {50, nullptr, "Unknown50"},
+            {51, nullptr, "Unknown51"},
+            {52, nullptr, "Unknown52"},
+            {53, nullptr, "Unknown53"},
+            {54, nullptr, "Unknown54"},
+            {55, nullptr, "Unknown55"},
+            {56, nullptr, "Unknown56"},
+            {57, nullptr, "Unknown57"},
+            {58, nullptr, "Unknown58"},
+            {59, nullptr, "Unknown59"},
+            {60, nullptr, "Unknown60"},
+            {61, nullptr, "Unknown61"},
+            {62, nullptr, "Unknown62"},
+            {63, nullptr, "Unknown63"},
+            {64, nullptr, "Unknown64"},
+            {65, nullptr, "Unknown65"},
+            {66, nullptr, "Unknown66"},
+            {67, nullptr, "Unknown67"},
+            {68, nullptr, "Unknown68"},
+            {69, nullptr, "Unknown69"},
+            {70, nullptr, "Unknown70"},
+            {71, nullptr, "Unknown71"},
+            {72, nullptr, "Unknown72"},
+            {73, nullptr, "Unknown73"},
+            {74, nullptr, "Unknown74"},
+            {75, nullptr, "Unknown75"},
+            {76, nullptr, "Unknown76"},
+            {77, nullptr, "Unknown77"},
+            {78, nullptr, "Unknown78"},
+            {79, nullptr, "Unknown79"},
+            {80, nullptr, "Unknown80"},
+            {81, nullptr, "Unknown81"},
+            {82, nullptr, "Unknown82"},
+            {83, nullptr, "Unknown83"},
+            {84, nullptr, "Unknown84"},
+            {85, nullptr, "Unknown85"},
+            {86, nullptr, "Unknown86"},
+            {87, nullptr, "Unknown87"},
+            {88, nullptr, "Unknown88"},
+            {89, nullptr, "Unknown89"},
+            {90, nullptr, "Unknown90"},
+            {91, nullptr, "Unknown91"},
+            {92, nullptr, "Unknown92"},
+            {93, nullptr, "Unknown93"},
+            {94, nullptr, "Unknown94"},
+            {95, nullptr, "Unknown95"},
+            {96, nullptr, "Unknown96"},
+            {97, nullptr, "Unknown97"},
+            {98, nullptr, "Unknown98"},
+            {99, nullptr, "Unknown99"},
+            {100, nullptr, "Unknown100"},
+            {101, nullptr, "Unknown101"},
+            {102, nullptr, "Unknown102"},
+            {103, nullptr, "Unknown103"},
+            {104, nullptr, "Unknown104"},
+            {105, nullptr, "Unknown105"},
+            {106, nullptr, "Unknown106"},
+            {107, nullptr, "Unknown107"},
+            {108, nullptr, "Unknown108"},
+            {109, nullptr, "Unknown109"},
+            {110, nullptr, "Unknown110"},
+            {111, nullptr, "Unknown111"},
+            {112, nullptr, "Unknown112"},
+            {113, nullptr, "Unknown113"},
+            {114, nullptr, "Unknown114"},
+            {115, nullptr, "Unknown115"},
+            {116, nullptr, "Unknown116"},
+            {117, nullptr, "Unknown117"},
+            {118, nullptr, "Unknown118"},
         };
         // clang-format on
 
@@ -142,6 +214,7 @@ public:
             {1, nullptr, "RefreshDebugAvailability"},
             {2, nullptr, "ClearDebugResponse"},
             {3, nullptr, "RegisterDebugResponse"},
+            {4, nullptr, "IsLargeResourceAvailable"},
         };
         // clang-format on
 
@@ -164,6 +237,8 @@ public:
         static const FunctionInfo functions[] = {
             {0, nullptr, "RequestDeviceAuthenticationToken"},
             {1, nullptr, "RequestCachedDeviceAuthenticationToken"},
+            {2, nullptr, "RequestEdgeToken"},
+            {3, nullptr, "RequestCachedEdgeToken"},
             {100, nullptr, "RequestRegisterDeviceAccount"},
             {101, nullptr, "RequestUnregisterDeviceAccount"},
             {102, nullptr, "RequestDeviceAccountStatus"},
@@ -181,7 +256,8 @@ public:
             {305, nullptr, "RequestCreateVirtualAccount"},
             {306, nullptr, "RequestDeviceLinkStatus"},
             {400, nullptr, "GetAccountByVirtualAccount"},
-            {500, nullptr, "RequestSyncTicket"},
+            {401, nullptr, "GetVirtualAccount"},
+            {500, nullptr, "RequestSyncTicketLegacy"},
             {501, nullptr, "RequestDownloadTicket"},
             {502, nullptr, "RequestDownloadTicketForPrepurchasedContents"},
             {503, nullptr, "RequestSyncTicket"},

+ 1 - 0
src/core/hle/service/npns/npns.cpp

@@ -30,6 +30,7 @@ public:
             {23, nullptr, "DestroyToken"},
             {24, nullptr, "DestroyTokenWithApplicationId"},
             {25, nullptr, "QueryIsTokenValid"},
+            {26, nullptr, "ListenToMyApplicationId"},
             {31, nullptr, "UploadTokenToBaaS"},
             {32, nullptr, "DestroyTokenForBaaS"},
             {33, nullptr, "CreateTokenForBaaS"},

+ 15 - 10
src/core/hle/service/ns/ns.cpp

@@ -104,7 +104,7 @@ IApplicationManagerInterface::IApplicationManagerInterface()
         {94, nullptr, "LaunchApplication"},
         {95, nullptr, "GetApplicationLaunchInfo"},
         {96, nullptr, "AcquireApplicationLaunchInfo"},
-        {97, nullptr, "GetMainApplicationProgramIndex2"},
+        {97, nullptr, "GetMainApplicationProgramIndexByApplicationLaunchInfo"},
         {98, nullptr, "EnableApplicationAllThreadDumpOnCrash"},
         {99, nullptr, "LaunchDevMenu"},
         {100, nullptr, "ResetToFactorySettings"},
@@ -254,7 +254,7 @@ IApplicationManagerInterface::IApplicationManagerInterface()
         {2170, nullptr, "GetRightsEnvironmentStatus"},
         {2171, nullptr, "GetRightsEnvironmentStatusChangedEvent"},
         {2180, nullptr, "RequestExtendRightsInRightsEnvironment"},
-        {2181, nullptr, "GetLastResultOfExtendRightsInRightsEnvironment"},
+        {2181, nullptr, "GetResultOfExtendRightsInRightsEnvironment"},
         {2182, nullptr, "SetActiveRightsContextUsingStateToRightsEnvironment"},
         {2190, nullptr, "GetRightsEnvironmentHandleForApplication"},
         {2199, nullptr, "GetRightsEnvironmentCountForDebug"},
@@ -366,7 +366,8 @@ ResultVal<u8> IApplicationManagerInterface::GetApplicationDesiredLanguage(
     LOG_DEBUG(Service_NS, "called with supported_languages={:08X}", supported_languages);
 
     // Get language code from settings
-    const auto language_code = Set::GetLanguageCodeFromIndex(Settings::values.language_index);
+    const auto language_code =
+        Set::GetLanguageCodeFromIndex(Settings::values.language_index.GetValue());
 
     // Convert to application language, get priority list
     const auto application_language = ConvertToApplicationLanguage(language_code);
@@ -445,8 +446,8 @@ IApplicationVersionInterface::IApplicationVersionInterface()
 
 IApplicationVersionInterface::~IApplicationVersionInterface() = default;
 
-IContentManagerInterface::IContentManagerInterface()
-    : ServiceFramework{"IContentManagerInterface"} {
+IContentManagementInterface::IContentManagementInterface()
+    : ServiceFramework{"IContentManagementInterface"} {
     // clang-format off
     static const FunctionInfo functions[] = {
         {11, nullptr, "CalculateApplicationOccupiedSize"},
@@ -463,7 +464,7 @@ IContentManagerInterface::IContentManagerInterface()
     RegisterHandlers(functions);
 }
 
-IContentManagerInterface::~IContentManagerInterface() = default;
+IContentManagementInterface::~IContentManagementInterface() = default;
 
 IDocumentInterface::IDocumentInterface() : ServiceFramework{"IDocumentInterface"} {
     // clang-format off
@@ -545,7 +546,7 @@ NS::NS(const char* name) : ServiceFramework{name} {
         {7995, &NS::PushInterface<IAccountProxyInterface>, "GetAccountProxyInterface"},
         {7996, &NS::PushInterface<IApplicationManagerInterface>, "GetApplicationManagerInterface"},
         {7997, &NS::PushInterface<IDownloadTaskInterface>, "GetDownloadTaskInterface"},
-        {7998, &NS::PushInterface<IContentManagerInterface>, "GetContentManagementInterface"},
+        {7998, &NS::PushInterface<IContentManagementInterface>, "GetContentManagementInterface"},
         {7999, &NS::PushInterface<IDocumentInterface>, "GetDocumentInterface"},
     };
     // clang-format on
@@ -572,9 +573,9 @@ public:
             {6, nullptr, "TerminateApplication"},
             {7, nullptr, "PrepareLaunchProgramFromHost"},
             {8, nullptr, "LaunchApplication"},
-            {9, nullptr, "LaunchApplicationWithStorageId"},
-            {10, nullptr, "TerminateApplication2"},
-            {11, nullptr, "GetRunningApplicationProcessId"},
+            {9, nullptr, "LaunchApplicationWithStorageIdForDevelop"},
+            {10, nullptr, "IsSystemMemoryResourceLimitBoosted"},
+            {11, nullptr, "GetRunningApplicationProcessIdForDevelop"},
             {12, nullptr, "SetCurrentApplicationRightsEnvironmentCanBeActive"},
             {13, nullptr, "CreateApplicationResourceForDevelop"},
             {14, nullptr, "IsPreomiaForDevelop"},
@@ -636,6 +637,10 @@ public:
             {9, nullptr, "GetSystemUpdateNotificationEventForContentDelivery"},
             {10, nullptr, "NotifySystemUpdateForContentDelivery"},
             {11, nullptr, "PrepareShutdown"},
+            {12, nullptr, "Unknown12"},
+            {13, nullptr, "Unknown13"},
+            {14, nullptr, "Unknown14"},
+            {15, nullptr, "Unknown15"},
             {16, nullptr, "DestroySystemUpdateTask"},
             {17, nullptr, "RequestSendSystemUpdate"},
             {18, nullptr, "GetSendSystemUpdateProgress"},

+ 3 - 3
src/core/hle/service/ns/ns.h

@@ -40,10 +40,10 @@ public:
     ~IApplicationVersionInterface() override;
 };
 
-class IContentManagerInterface final : public ServiceFramework<IContentManagerInterface> {
+class IContentManagementInterface final : public ServiceFramework<IContentManagementInterface> {
 public:
-    explicit IContentManagerInterface();
-    ~IContentManagerInterface() override;
+    explicit IContentManagementInterface();
+    ~IContentManagementInterface() override;
 };
 
 class IDocumentInterface final : public ServiceFramework<IDocumentInterface> {

+ 1 - 1
src/core/hle/service/ns/pl_u.cpp

@@ -163,7 +163,7 @@ PL_U::PL_U(Core::System& system)
         {5, &PL_U::GetSharedFontInOrderOfPriority, "GetSharedFontInOrderOfPriority"},
         {6, nullptr, "GetSharedFontInOrderOfPriorityForSystem"},
         {100, nullptr, "RequestApplicationFunctionAuthorization"},
-        {101, nullptr, "RequestApplicationFunctionAuthorizationForSystem"},
+        {101, nullptr, "RequestApplicationFunctionAuthorizationByProcessId"},
         {102, nullptr, "RequestApplicationFunctionAuthorizationByApplicationId"},
         {1000, nullptr, "LoadNgWordDataForPlatformRegionChina"},
         {1001, nullptr, "GetNgWordDataSizeForPlatformRegionChina"},

+ 6 - 5
src/core/hle/service/nvdrv/interface.cpp

@@ -144,7 +144,7 @@ void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) {
     }
 }
 
-void NVDRV::SetClientPID(Kernel::HLERequestContext& ctx) {
+void NVDRV::SetAruid(Kernel::HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
     pid = rp.Pop<u64>();
     LOG_WARNING(Service_NVDRV, "(STUBBED) called, pid=0x{:X}", pid);
@@ -154,7 +154,7 @@ void NVDRV::SetClientPID(Kernel::HLERequestContext& ctx) {
     rb.Push<u32>(0);
 }
 
-void NVDRV::FinishInitialize(Kernel::HLERequestContext& ctx) {
+void NVDRV::SetGraphicsFirmwareMemoryMarginEnabled(Kernel::HLERequestContext& ctx) {
     LOG_WARNING(Service_NVDRV, "(STUBBED) called");
 
     IPC::ResponseBuilder rb{ctx, 2};
@@ -187,13 +187,14 @@ NVDRV::NVDRV(std::shared_ptr<Module> nvdrv, const char* name)
         {4, &NVDRV::QueryEvent, "QueryEvent"},
         {5, nullptr, "MapSharedMem"},
         {6, &NVDRV::GetStatus, "GetStatus"},
-        {7, nullptr, "ForceSetClientPID"},
-        {8, &NVDRV::SetClientPID, "SetClientPID"},
+        {7, nullptr, "SetAruidForTest"},
+        {8, &NVDRV::SetAruid, "SetAruid"},
         {9, &NVDRV::DumpGraphicsMemoryInfo, "DumpGraphicsMemoryInfo"},
         {10, nullptr, "InitializeDevtools"},
         {11, &NVDRV::Ioctl2, "Ioctl2"},
         {12, &NVDRV::Ioctl3, "Ioctl3"},
-        {13, &NVDRV::FinishInitialize, "FinishInitialize"},
+        {13, &NVDRV::SetGraphicsFirmwareMemoryMarginEnabled,
+         "SetGraphicsFirmwareMemoryMarginEnabled"},
     };
     RegisterHandlers(functions);
 }

+ 2 - 2
src/core/hle/service/nvdrv/interface.h

@@ -29,8 +29,8 @@ private:
     void Close(Kernel::HLERequestContext& ctx);
     void Initialize(Kernel::HLERequestContext& ctx);
     void QueryEvent(Kernel::HLERequestContext& ctx);
-    void SetClientPID(Kernel::HLERequestContext& ctx);
-    void FinishInitialize(Kernel::HLERequestContext& ctx);
+    void SetAruid(Kernel::HLERequestContext& ctx);
+    void SetGraphicsFirmwareMemoryMarginEnabled(Kernel::HLERequestContext& ctx);
     void GetStatus(Kernel::HLERequestContext& ctx);
     void DumpGraphicsMemoryInfo(Kernel::HLERequestContext& ctx);
     void IoctlBase(Kernel::HLERequestContext& ctx, IoctlVersion version);

+ 4 - 4
src/core/hle/service/nvdrv/nvmemp.cpp

@@ -10,19 +10,19 @@ namespace Service::Nvidia {
 
 NVMEMP::NVMEMP() : ServiceFramework("nvmemp") {
     static const FunctionInfo functions[] = {
-        {0, &NVMEMP::Cmd0, "Cmd0"},
-        {1, &NVMEMP::Cmd1, "Cmd1"},
+        {0, &NVMEMP::Open, "Open"},
+        {1, &NVMEMP::GetAruid, "GetAruid"},
     };
     RegisterHandlers(functions);
 }
 
 NVMEMP::~NVMEMP() = default;
 
-void NVMEMP::Cmd0(Kernel::HLERequestContext& ctx) {
+void NVMEMP::Open(Kernel::HLERequestContext& ctx) {
     UNIMPLEMENTED();
 }
 
-void NVMEMP::Cmd1(Kernel::HLERequestContext& ctx) {
+void NVMEMP::GetAruid(Kernel::HLERequestContext& ctx) {
     UNIMPLEMENTED();
 }
 

+ 2 - 2
src/core/hle/service/nvdrv/nvmemp.h

@@ -14,8 +14,8 @@ public:
     ~NVMEMP() override;
 
 private:
-    void Cmd0(Kernel::HLERequestContext& ctx);
-    void Cmd1(Kernel::HLERequestContext& ctx);
+    void Open(Kernel::HLERequestContext& ctx);
+    void GetAruid(Kernel::HLERequestContext& ctx);
 };
 
 } // namespace Service::Nvidia

+ 3 - 0
src/core/hle/service/pcie/pcie.cpp

@@ -36,6 +36,9 @@ public:
             {18, nullptr, "ReleaseIrq"},
             {19, nullptr, "SetIrqEnable"},
             {20, nullptr, "SetAspmEnable"},
+            {21, nullptr, "SetResetUponResumeEnable"},
+            {22, nullptr, "Unknown22"},
+            {23, nullptr, "Unknown23"},
         };
         // clang-format on
 

+ 3 - 0
src/core/hle/service/pcv/pcv.cpp

@@ -42,6 +42,9 @@ public:
             {24, nullptr, "GetModuleStateTable"},
             {25, nullptr, "GetPowerDomainStateTable"},
             {26, nullptr, "GetFuseInfo"},
+            {27, nullptr, "GetDramId"},
+            {28, nullptr, "IsPoweredOn"},
+            {29, nullptr, "GetVoltage"},
         };
         // clang-format on
 

+ 17 - 17
src/core/hle/service/pm/pm.cpp

@@ -78,13 +78,13 @@ public:
         : ServiceFramework{"pm:dmnt"}, kernel(kernel) {
         // clang-format off
         static const FunctionInfo functions[] = {
-            {0, nullptr, "GetDebugProcesses"},
-            {1, nullptr, "StartDebugProcess"},
-            {2, &DebugMonitor::GetTitlePid, "GetTitlePid"},
-            {3, nullptr, "EnableDebugForTitleId"},
-            {4, &DebugMonitor::GetApplicationPid, "GetApplicationPid"},
-            {5, nullptr, "EnableDebugForApplication"},
-            {6, nullptr, "DisableDebug"},
+            {0, nullptr, "GetJitDebugProcessIdList"},
+            {1, nullptr, "StartProcess"},
+            {2, &DebugMonitor::GetProcessId, "GetProcessId"},
+            {3, nullptr, "HookToCreateProcess"},
+            {4, &DebugMonitor::GetApplicationProcessId, "GetApplicationProcessId"},
+            {5, nullptr, "HookToCreateApplicationProgress"},
+            {6, nullptr, "ClearHook"},
         };
         // clang-format on
 
@@ -92,7 +92,7 @@ public:
     }
 
 private:
-    void GetTitlePid(Kernel::HLERequestContext& ctx) {
+    void GetProcessId(Kernel::HLERequestContext& ctx) {
         IPC::RequestParser rp{ctx};
         const auto title_id = rp.PopRaw<u64>();
 
@@ -114,7 +114,7 @@ private:
         rb.Push((*process)->GetProcessID());
     }
 
-    void GetApplicationPid(Kernel::HLERequestContext& ctx) {
+    void GetApplicationProcessId(Kernel::HLERequestContext& ctx) {
         LOG_DEBUG(Service_PM, "called");
         GetApplicationPidGeneric(ctx, kernel.GetProcessList());
     }
@@ -163,15 +163,15 @@ public:
         : ServiceFramework{"pm:shell"}, kernel(kernel) {
         // clang-format off
         static const FunctionInfo functions[] = {
-            {0, nullptr, "LaunchProcess"},
-            {1, nullptr, "TerminateProcessByPid"},
-            {2, nullptr, "TerminateProcessByTitleId"},
-            {3, nullptr, "GetProcessEventWaiter"},
-            {4, nullptr, "GetProcessEventType"},
+            {0, nullptr, "LaunchProgram"},
+            {1, nullptr, "TerminateProcess"},
+            {2, nullptr, "TerminateProgram"},
+            {3, nullptr, "GetProcessEventHandle"},
+            {4, nullptr, "GetProcessEventInfo"},
             {5, nullptr, "NotifyBootFinished"},
-            {6, &Shell::GetApplicationPid, "GetApplicationPid"},
+            {6, &Shell::GetApplicationProcessIdForShell, "GetApplicationProcessIdForShell"},
             {7, nullptr, "BoostSystemMemoryResourceLimit"},
-            {8, nullptr, "EnableAdditionalSystemThreads"},
+            {8, nullptr, "BoostApplicationThreadResourceLimit"},
             {9, nullptr, "GetBootFinishedEventHandle"},
         };
         // clang-format on
@@ -180,7 +180,7 @@ public:
     }
 
 private:
-    void GetApplicationPid(Kernel::HLERequestContext& ctx) {
+    void GetApplicationProcessIdForShell(Kernel::HLERequestContext& ctx) {
         LOG_DEBUG(Service_PM, "called");
         GetApplicationPidGeneric(ctx, kernel.GetProcessList());
     }

+ 5 - 0
src/core/hle/service/prepo/prepo.cpp

@@ -42,6 +42,11 @@ public:
             {40101, nullptr, "SetUserAgreementCheckEnabled"},
             {50100, nullptr, "ReadAllApplicationReportFiles"},
             {90100, nullptr, "ReadAllReportFiles"},
+            {90101, nullptr, "Unknown90101"},
+            {90102, nullptr, "Unknown90102"},
+            {90200, nullptr, "GetStatistics"},
+            {90201, nullptr, "GetThroughputHistory"},
+            {90300, nullptr, "GetLastUploadError"},
         };
         // clang-format on
 

+ 2 - 0
src/core/hle/service/psc/psc.cpp

@@ -24,6 +24,8 @@ public:
             {4, nullptr, "Cancel"},
             {5, nullptr, "PrintModuleInformation"},
             {6, nullptr, "GetModuleInformation"},
+            {10, nullptr, "Unknown10"},
+            {11, nullptr, "Unknown11"},
         };
         // clang-format on
 

+ 1 - 0
src/core/hle/service/ptm/psm.cpp

@@ -35,6 +35,7 @@ public:
             {15, nullptr, "GetBatteryAgePercentage"},
             {16, nullptr, "GetBatteryChargeInfoEvent"},
             {17, nullptr, "GetBatteryChargeInfoFields"},
+            {18, nullptr, "GetBatteryChargeCalibratedEvent"},
         };
         // clang-format on
 

+ 5 - 5
src/core/hle/service/set/set.cpp

@@ -91,7 +91,7 @@ void GetAvailableLanguageCodesImpl(Kernel::HLERequestContext& ctx, std::size_t m
 }
 
 void GetKeyCodeMapImpl(Kernel::HLERequestContext& ctx) {
-    const auto language_code = available_language_codes[Settings::values.language_index];
+    const auto language_code = available_language_codes[Settings::values.language_index.GetValue()];
     const auto key_code =
         std::find_if(language_to_layout.cbegin(), language_to_layout.cend(),
                      [=](const auto& element) { return element.first == language_code; });
@@ -99,7 +99,7 @@ void GetKeyCodeMapImpl(Kernel::HLERequestContext& ctx) {
     if (key_code == language_to_layout.cend()) {
         LOG_ERROR(Service_SET,
                   "Could not find keyboard layout for language index {}, defaulting to English us",
-                  Settings::values.language_index);
+                  Settings::values.language_index.GetValue());
     } else {
         layout = key_code->second;
     }
@@ -163,11 +163,11 @@ void SET::GetQuestFlag(Kernel::HLERequestContext& ctx) {
 }
 
 void SET::GetLanguageCode(Kernel::HLERequestContext& ctx) {
-    LOG_DEBUG(Service_SET, "called {}", Settings::values.language_index);
+    LOG_DEBUG(Service_SET, "called {}", Settings::values.language_index.GetValue());
 
     IPC::ResponseBuilder rb{ctx, 4};
     rb.Push(RESULT_SUCCESS);
-    rb.PushEnum(available_language_codes[Settings::values.language_index]);
+    rb.PushEnum(available_language_codes[Settings::values.language_index.GetValue()]);
 }
 
 void SET::GetRegionCode(Kernel::HLERequestContext& ctx) {
@@ -175,7 +175,7 @@ void SET::GetRegionCode(Kernel::HLERequestContext& ctx) {
 
     IPC::ResponseBuilder rb{ctx, 3};
     rb.Push(RESULT_SUCCESS);
-    rb.Push(Settings::values.region_index);
+    rb.Push(Settings::values.region_index.GetValue());
 }
 
 void SET::GetKeyCodeMap(Kernel::HLERequestContext& ctx) {

+ 11 - 10
src/core/hle/service/sm/controller.cpp

@@ -12,7 +12,7 @@
 
 namespace Service::SM {
 
-void Controller::ConvertSessionToDomain(Kernel::HLERequestContext& ctx) {
+void Controller::ConvertCurrentObjectToDomain(Kernel::HLERequestContext& ctx) {
     ASSERT_MSG(ctx.Session()->IsSession(), "Session is already a domain");
     LOG_DEBUG(Service, "called, server_session={}", ctx.Session()->GetObjectId());
     ctx.Session()->ConvertToDomain();
@@ -22,7 +22,7 @@ void Controller::ConvertSessionToDomain(Kernel::HLERequestContext& ctx) {
     rb.Push<u32>(1); // Converted sessions start with 1 request handler
 }
 
-void Controller::DuplicateSession(Kernel::HLERequestContext& ctx) {
+void Controller::CloneCurrentObject(Kernel::HLERequestContext& ctx) {
     // TODO(bunnei): This is just creating a new handle to the same Session. I assume this is wrong
     // and that we probably want to actually make an entirely new Session, but we still need to
     // verify this on hardware.
@@ -33,10 +33,10 @@ void Controller::DuplicateSession(Kernel::HLERequestContext& ctx) {
     rb.PushMoveObjects(ctx.Session()->GetParent()->Client());
 }
 
-void Controller::DuplicateSessionEx(Kernel::HLERequestContext& ctx) {
-    LOG_WARNING(Service, "(STUBBED) called, using DuplicateSession");
+void Controller::CloneCurrentObjectEx(Kernel::HLERequestContext& ctx) {
+    LOG_WARNING(Service, "(STUBBED) called, using CloneCurrentObject");
 
-    DuplicateSession(ctx);
+    CloneCurrentObject(ctx);
 }
 
 void Controller::QueryPointerBufferSize(Kernel::HLERequestContext& ctx) {
@@ -47,13 +47,14 @@ void Controller::QueryPointerBufferSize(Kernel::HLERequestContext& ctx) {
     rb.Push<u16>(0x1000);
 }
 
+// https://switchbrew.org/wiki/IPC_Marshalling
 Controller::Controller() : ServiceFramework("IpcController") {
     static const FunctionInfo functions[] = {
-        {0x00000000, &Controller::ConvertSessionToDomain, "ConvertSessionToDomain"},
-        {0x00000001, nullptr, "ConvertDomainToSession"},
-        {0x00000002, &Controller::DuplicateSession, "DuplicateSession"},
-        {0x00000003, &Controller::QueryPointerBufferSize, "QueryPointerBufferSize"},
-        {0x00000004, &Controller::DuplicateSessionEx, "DuplicateSessionEx"},
+        {0, &Controller::ConvertCurrentObjectToDomain, "ConvertCurrentObjectToDomain"},
+        {1, nullptr, "CopyFromCurrentDomain"},
+        {2, &Controller::CloneCurrentObject, "CloneCurrentObject"},
+        {3, &Controller::QueryPointerBufferSize, "QueryPointerBufferSize"},
+        {4, &Controller::CloneCurrentObjectEx, "CloneCurrentObjectEx"},
     };
     RegisterHandlers(functions);
 }

+ 3 - 3
src/core/hle/service/sm/controller.h

@@ -14,9 +14,9 @@ public:
     ~Controller() override;
 
 private:
-    void ConvertSessionToDomain(Kernel::HLERequestContext& ctx);
-    void DuplicateSession(Kernel::HLERequestContext& ctx);
-    void DuplicateSessionEx(Kernel::HLERequestContext& ctx);
+    void ConvertCurrentObjectToDomain(Kernel::HLERequestContext& ctx);
+    void CloneCurrentObject(Kernel::HLERequestContext& ctx);
+    void CloneCurrentObjectEx(Kernel::HLERequestContext& ctx);
     void QueryPointerBufferSize(Kernel::HLERequestContext& ctx);
 };
 

+ 6 - 0
src/core/hle/service/sockets/nsd.cpp

@@ -14,6 +14,7 @@ NSD::NSD(const char* name) : ServiceFramework(name) {
         {12, nullptr, "GetDeviceId"},
         {13, nullptr, "DeleteSettings"},
         {14, nullptr, "ImportSettings"},
+        {15, nullptr, "SetChangeEnvironmentIdentifierDisabled"},
         {20, nullptr, "Resolve"},
         {21, nullptr, "ResolveEx"},
         {30, nullptr, "GetNasServiceSetting"},
@@ -28,6 +29,11 @@ NSD::NSD(const char* name) : ServiceFramework(name) {
         {60, nullptr, "ReadSaveDataFromFsForTest"},
         {61, nullptr, "WriteSaveDataToFsForTest"},
         {62, nullptr, "DeleteSaveDataOfFsForTest"},
+        {63, nullptr, "IsChangeEnvironmentIdentifierDisabled"},
+        {64, nullptr, "SetWithoutDomainExchangeFqdns"},
+        {100, nullptr, "GetApplicationServerEnvironmentType"},
+        {101, nullptr, "SetApplicationServerEnvironmentType"},
+        {102, nullptr, "DeleteApplicationServerEnvironmentType"},
     };
     // clang-format on
 

+ 15 - 10
src/core/hle/service/sockets/sfdnsres.cpp

@@ -7,7 +7,7 @@
 
 namespace Service::Sockets {
 
-void SFDNSRES::GetAddrInfo(Kernel::HLERequestContext& ctx) {
+void SFDNSRES::GetAddrInfoRequest(Kernel::HLERequestContext& ctx) {
     struct Parameters {
         u8 use_nsd_resolve;
         u32 unknown;
@@ -29,15 +29,20 @@ SFDNSRES::SFDNSRES() : ServiceFramework("sfdnsres") {
     static const FunctionInfo functions[] = {
         {0, nullptr, "SetDnsAddressesPrivate"},
         {1, nullptr, "GetDnsAddressPrivate"},
-        {2, nullptr, "GetHostByName"},
-        {3, nullptr, "GetHostByAddr"},
-        {4, nullptr, "GetHostStringError"},
-        {5, nullptr, "GetGaiStringError"},
-        {6, &SFDNSRES::GetAddrInfo, "GetAddrInfo"},
-        {7, nullptr, "GetNameInfo"},
-        {8, nullptr, "RequestCancelHandle"},
-        {9, nullptr, "CancelSocketCall"},
-        {11, nullptr, "ClearDnsIpServerAddressArray"},
+        {2, nullptr, "GetHostByNameRequest"},
+        {3, nullptr, "GetHostByAddrRequest"},
+        {4, nullptr, "GetHostStringErrorRequest"},
+        {5, nullptr, "GetGaiStringErrorRequest"},
+        {6, &SFDNSRES::GetAddrInfoRequest, "GetAddrInfoRequest"},
+        {7, nullptr, "GetNameInfoRequest"},
+        {8, nullptr, "RequestCancelHandleRequest"},
+        {9, nullptr, "CancelRequest"},
+        {10, nullptr, "GetHostByNameRequestWithOptions"},
+        {11, nullptr, "GetHostByAddrRequestWithOptions"},
+        {12, nullptr, "GetAddrInfoRequestWithOptions"},
+        {13, nullptr, "GetNameInfoRequestWithOptions"},
+        {14, nullptr, "ResolverSetOptionRequest"},
+        {15, nullptr, "ResolverGetOptionRequest"},
     };
     RegisterHandlers(functions);
 }

+ 1 - 1
src/core/hle/service/sockets/sfdnsres.h

@@ -15,7 +15,7 @@ public:
     ~SFDNSRES() override;
 
 private:
-    void GetAddrInfo(Kernel::HLERequestContext& ctx);
+    void GetAddrInfoRequest(Kernel::HLERequestContext& ctx);
 };
 
 } // namespace Service::Sockets

+ 1 - 1
src/core/hle/service/spl/module.cpp

@@ -19,7 +19,7 @@ namespace Service::SPL {
 
 Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
     : ServiceFramework(name), module(std::move(module)),
-      rng(Settings::values.rng_seed.value_or(std::time(nullptr))) {}
+      rng(Settings::values.rng_seed.GetValue().value_or(std::time(nullptr))) {}
 
 Module::Interface::~Interface() = default;
 

+ 20 - 19
src/core/hle/service/spl/spl.cpp

@@ -9,35 +9,36 @@ namespace Service::SPL {
 SPL::SPL(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "spl:") {
     static const FunctionInfo functions[] = {
         {0, nullptr, "GetConfig"},
-        {1, nullptr, "UserExpMod"},
+        {1, nullptr, "ModularExponentiate"},
         {2, nullptr, "GenerateAesKek"},
         {3, nullptr, "LoadAesKey"},
         {4, nullptr, "GenerateAesKey"},
         {5, nullptr, "SetConfig"},
         {7, &SPL::GetRandomBytes, "GetRandomBytes"},
-        {9, nullptr, "LoadSecureExpModKey"},
-        {10, nullptr, "SecureExpMod"},
+        {9, nullptr, "ImportLotusKey"},
+        {10, nullptr, "DecryptLotusMessage"},
         {11, nullptr, "IsDevelopment"},
         {12, nullptr, "GenerateSpecificAesKey"},
-        {13, nullptr, "DecryptPrivk"},
+        {13, nullptr, "DecryptDeviceUniqueData"},
         {14, nullptr, "DecryptAesKey"},
-        {15, nullptr, "DecryptAesCtr"},
+        {15, nullptr, "CryptAesCtr"},
         {16, nullptr, "ComputeCmac"},
-        {17, nullptr, "LoadRsaOaepKey"},
-        {18, nullptr, "UnwrapRsaOaepWrappedTitleKey"},
+        {17, nullptr, "ImportEsKey"},
+        {18, nullptr, "UnwrapTitleKey"},
         {19, nullptr, "LoadTitleKey"},
-        {20, nullptr, "UnwrapAesWrappedTitleKey"},
-        {21, nullptr, "LockAesEngine"},
-        {22, nullptr, "UnlockAesEngine"},
-        {23, nullptr, "GetSplWaitEvent"},
-        {24, nullptr, "SetSharedData"},
-        {25, nullptr, "GetSharedData"},
-        {26, nullptr, "ImportSslRsaKey"},
-        {27, nullptr, "SecureExpModWithSslKey"},
-        {28, nullptr, "ImportEsRsaKey"},
-        {29, nullptr, "SecureExpModWithEsKey"},
-        {30, nullptr, "EncryptManuRsaKeyForImport"},
-        {31, nullptr, "GetPackage2Hash"},
+        {20, nullptr, "PrepareEsCommonKey"},
+        {21, nullptr, "AllocateAesKeyslot"},
+        {22, nullptr, "DeallocateAesKeySlot"},
+        {23, nullptr, "GetAesKeyslotAvailableEvent"},
+        {24, nullptr, "SetBootReason"},
+        {25, nullptr, "GetBootReason"},
+        {26, nullptr, "DecryptAndStoreSslClientCertKey"},
+        {27, nullptr, "ModularExponentiateWithSslClientCertKey"},
+        {28, nullptr, "DecryptAndStoreDrmDeviceCertKey"},
+        {29, nullptr, "ModularExponentiateWithDrmDeviceCertKey"},
+        {30, nullptr, "ReencryptDeviceUniqueData "},
+        {31, nullptr, "PrepareEsArchiveKey"}, // This is also GetPackage2Hash?
+        {32, nullptr, "LoadPreparedAesKey"},
     };
     RegisterHandlers(functions);
 }

+ 7 - 0
src/core/hle/service/time/time.cpp

@@ -90,6 +90,13 @@ public:
         : ServiceFramework("ISteadyClock"), clock_core{clock_core}, system{system} {
         static const FunctionInfo functions[] = {
             {0, &ISteadyClock::GetCurrentTimePoint, "GetCurrentTimePoint"},
+            {2, nullptr, "GetTestOffset"},
+            {3, nullptr, "SetTestOffset"},
+            {100, nullptr, "GetRtcValue"},
+            {101, nullptr, "IsRtcResetDetected"},
+            {102, nullptr, "GetSetupResultValue"},
+            {200, nullptr, "GetInternalOffset"},
+            {201, nullptr, "SetInternalOffset"},
         };
         RegisterHandlers(functions);
     }

+ 16 - 15
src/core/hle/service/usb/usb.cpp

@@ -20,7 +20,7 @@ public:
         static const FunctionInfo functions[] = {
             {0, nullptr, "GetDsEndpoint"},
             {1, nullptr, "GetSetupEvent"},
-            {2, nullptr, "Unknown"},
+            {2, nullptr, "Unknown2"},
             {3, nullptr, "EnableInterface"},
             {4, nullptr, "DisableInterface"},
             {5, nullptr, "CtrlInPostBufferAsync"},
@@ -55,6 +55,7 @@ public:
             {9, nullptr, "SetBinaryObjectStore"},
             {10, nullptr, "Enable"},
             {11, nullptr, "Disable"},
+            {12, nullptr, "Unknown12"},
         };
         // clang-format on
 
@@ -69,13 +70,13 @@ public:
         static const FunctionInfo functions[] = {
             {0, nullptr, "Open"},
             {1, nullptr, "Close"},
-            {2, nullptr, "Unknown1"},
+            {2, nullptr, "Unknown2"},
             {3, nullptr, "Populate"},
             {4, nullptr, "PostBufferAsync"},
             {5, nullptr, "GetXferReport"},
             {6, nullptr, "PostBufferMultiAsync"},
-            {7, nullptr, "Unknown3"},
-            {8, nullptr, "Unknown4"},
+            {7, nullptr, "Unknown7"},
+            {8, nullptr, "Unknown8"},
         };
         // clang-format on
 
@@ -88,13 +89,13 @@ public:
     explicit IClientIfSession() : ServiceFramework{"IClientIfSession"} {
         // clang-format off
         static const FunctionInfo functions[] = {
-            {0, nullptr, "Unknown1"},
+            {0, nullptr, "Unknown0"},
             {1, nullptr, "SetInterface"},
             {2, nullptr, "GetInterface"},
             {3, nullptr, "GetAlternateInterface"},
             {4, nullptr, "GetCurrentFrame"},
             {5, nullptr, "CtrlXferAsync"},
-            {6, nullptr, "Unknown2"},
+            {6, nullptr, "Unknown6"},
             {7, nullptr, "GetCtrlXferReport"},
             {8, nullptr, "ResetDevice"},
             {9, nullptr, "OpenUsbEp"},
@@ -118,7 +119,7 @@ public:
             {5, nullptr, "DestroyInterfaceAvailableEvent"},
             {6, nullptr, "GetInterfaceStateChangeEvent"},
             {7, nullptr, "AcquireUsbIf"},
-            {8, nullptr, "Unknown1"},
+            {8, nullptr, "Unknown8"},
         };
         // clang-format on
 
@@ -179,8 +180,8 @@ public:
             {4, nullptr, "GetFwRevision"},
             {5, nullptr, "GetManufacturerId"},
             {6, nullptr, "GetDeviceId"},
-            {7, nullptr, "Unknown1"},
-            {8, nullptr, "Unknown2"},
+            {7, nullptr, "Unknown7"},
+            {8, nullptr, "Unknown8"},
         };
         // clang-format on
 
@@ -215,12 +216,12 @@ public:
     explicit USB_PM() : ServiceFramework{"usb:pm"} {
         // clang-format off
         static const FunctionInfo functions[] = {
-            {0, nullptr, "Unknown1"},
-            {1, nullptr, "Unknown2"},
-            {2, nullptr, "Unknown3"},
-            {3, nullptr, "Unknown4"},
-            {4, nullptr, "Unknown5"},
-            {5, nullptr, "Unknown6"},
+            {0, nullptr, "Unknown0"},
+            {1, nullptr, "Unknown1"},
+            {2, nullptr, "Unknown2"},
+            {3, nullptr, "Unknown3"},
+            {4, nullptr, "Unknown4"},
+            {5, nullptr, "Unknown5"},
         };
         // clang-format on
 

+ 12 - 10
src/core/hle/service/vi/vi.cpp

@@ -519,9 +519,9 @@ private:
             IGBPConnectRequestParcel request{ctx.ReadBuffer()};
             IGBPConnectResponseParcel response{
                 static_cast<u32>(static_cast<u32>(DisplayResolution::UndockedWidth) *
-                                 Settings::values.resolution_factor),
+                                 Settings::values.resolution_factor.GetValue()),
                 static_cast<u32>(static_cast<u32>(DisplayResolution::UndockedHeight) *
-                                 Settings::values.resolution_factor)};
+                                 Settings::values.resolution_factor.GetValue())};
             ctx.WriteBuffer(response.Serialize());
             break;
         }
@@ -700,6 +700,7 @@ public:
             {3215, nullptr, "SetDisplayGamma"},
             {3216, nullptr, "GetDisplayCmuLuma"},
             {3217, nullptr, "SetDisplayCmuLuma"},
+            {6013, nullptr, "GetLayerPresentationSubmissionTimestamps"},
             {8225, nullptr, "GetSharedBufferMemoryHandleId"},
             {8250, nullptr, "OpenSharedLayer"},
             {8251, nullptr, "CloseSharedLayer"},
@@ -748,14 +749,14 @@ private:
 
         if (Settings::values.use_docked_mode) {
             rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth) *
-                    static_cast<u32>(Settings::values.resolution_factor));
+                    static_cast<u32>(Settings::values.resolution_factor.GetValue()));
             rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight) *
-                    static_cast<u32>(Settings::values.resolution_factor));
+                    static_cast<u32>(Settings::values.resolution_factor.GetValue()));
         } else {
             rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth) *
-                    static_cast<u32>(Settings::values.resolution_factor));
+                    static_cast<u32>(Settings::values.resolution_factor.GetValue()));
             rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight) *
-                    static_cast<u32>(Settings::values.resolution_factor));
+                    static_cast<u32>(Settings::values.resolution_factor.GetValue()));
         }
 
         rb.PushRaw<float>(60.0f); // This wouldn't seem to be correct for 30 fps games.
@@ -785,6 +786,7 @@ public:
             {2300, nullptr, "AcquireLayerTexturePresentingEvent"},
             {2301, nullptr, "ReleaseLayerTexturePresentingEvent"},
             {2302, nullptr, "GetDisplayHotplugEvent"},
+            {2303, nullptr, "GetDisplayModeChangedEvent"},
             {2402, nullptr, "GetDisplayHotplugState"},
             {2501, nullptr, "GetCompositorErrorInfo"},
             {2601, nullptr, "GetDisplayErrorEvent"},
@@ -1029,9 +1031,9 @@ private:
         // between docked and undocked dimensions. We take the liberty of applying
         // the resolution scaling factor here.
         rb.Push(static_cast<u64>(DisplayResolution::UndockedWidth) *
-                static_cast<u32>(Settings::values.resolution_factor));
+                static_cast<u32>(Settings::values.resolution_factor.GetValue()));
         rb.Push(static_cast<u64>(DisplayResolution::UndockedHeight) *
-                static_cast<u32>(Settings::values.resolution_factor));
+                static_cast<u32>(Settings::values.resolution_factor.GetValue()));
     }
 
     void SetLayerScalingMode(Kernel::HLERequestContext& ctx) {
@@ -1064,8 +1066,8 @@ private:
         LOG_WARNING(Service_VI, "(STUBBED) called");
 
         DisplayInfo display_info;
-        display_info.width *= static_cast<u64>(Settings::values.resolution_factor);
-        display_info.height *= static_cast<u64>(Settings::values.resolution_factor);
+        display_info.width *= static_cast<u64>(Settings::values.resolution_factor.GetValue());
+        display_info.height *= static_cast<u64>(Settings::values.resolution_factor.GetValue());
         ctx.WriteBuffer(&display_info, sizeof(DisplayInfo));
         IPC::ResponseBuilder rb{ctx, 4};
         rb.Push(RESULT_SUCCESS);

+ 1 - 0
src/core/hle/service/vi/vi_u.cpp

@@ -12,6 +12,7 @@ VI_U::VI_U(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
     : ServiceFramework{"vi:u"}, nv_flinger{std::move(nv_flinger)} {
     static const FunctionInfo functions[] = {
         {0, &VI_U::GetDisplayService, "GetDisplayService"},
+        {1, nullptr, "GetDisplayServiceWithProxyNameExchange"},
     };
     RegisterHandlers(functions);
 }

+ 55 - 47
src/core/hle/service/wlan/wlan.cpp

@@ -15,34 +15,37 @@ public:
     explicit WLANInfra() : ServiceFramework{"wlan:inf"} {
         // clang-format off
         static const FunctionInfo functions[] = {
-            {0, nullptr, "Unknown1"},
-            {1, nullptr, "Unknown2"},
+            {0, nullptr, "OpenMode"},
+            {1, nullptr, "CloseMode"},
             {2, nullptr, "GetMacAddress"},
             {3, nullptr, "StartScan"},
             {4, nullptr, "StopScan"},
             {5, nullptr, "Connect"},
             {6, nullptr, "CancelConnect"},
             {7, nullptr, "Disconnect"},
-            {8, nullptr, "Unknown3"},
-            {9, nullptr, "Unknown4"},
+            {8, nullptr, "GetConnectionEvent"},
+            {9, nullptr, "GetConnectionStatus"},
             {10, nullptr, "GetState"},
             {11, nullptr, "GetScanResult"},
             {12, nullptr, "GetRssi"},
             {13, nullptr, "ChangeRxAntenna"},
-            {14, nullptr, "Unknown5"},
-            {15, nullptr, "Unknown6"},
+            {14, nullptr, "GetFwVersion"},
+            {15, nullptr, "RequestSleep"},
             {16, nullptr, "RequestWakeUp"},
             {17, nullptr, "RequestIfUpDown"},
-            {18, nullptr, "Unknown7"},
-            {19, nullptr, "Unknown8"},
-            {20, nullptr, "Unknown9"},
-            {21, nullptr, "Unknown10"},
-            {22, nullptr, "Unknown11"},
-            {23, nullptr, "Unknown12"},
-            {24, nullptr, "Unknown13"},
-            {25, nullptr, "Unknown14"},
-            {26, nullptr, "Unknown15"},
-            {27, nullptr, "Unknown16"},
+            {18, nullptr, "Unknown18"},
+            {19, nullptr, "Unknown19"},
+            {20, nullptr, "Unknown20"},
+            {21, nullptr, "Unknown21"},
+            {22, nullptr, "Unknown22"},
+            {23, nullptr, "Unknown23"},
+            {24, nullptr, "Unknown24"},
+            {25, nullptr, "Unknown25"},
+            {26, nullptr, "Unknown26"},
+            {27, nullptr, "Unknown27"},
+            {28, nullptr, "Unknown28"},
+            {29, nullptr, "Unknown29"},
+            {30, nullptr, "Unknown30"},
         };
         // clang-format on
 
@@ -55,12 +58,12 @@ public:
     explicit WLANLocal() : ServiceFramework{"wlan:lcl"} {
         // clang-format off
         static const FunctionInfo functions[] = {
-            {0, nullptr, "Unknown1"},
-            {1, nullptr, "Unknown2"},
-            {2, nullptr, "Unknown3"},
-            {3, nullptr, "Unknown4"},
-            {4, nullptr, "Unknown5"},
-            {5, nullptr, "Unknown6"},
+            {0, nullptr, "Unknown0"},
+            {1, nullptr, "Unknown1"},
+            {2, nullptr, "Unknown2"},
+            {3, nullptr, "Unknown3"},
+            {4, nullptr, "Unknown4"},
+            {5, nullptr, "Unknown5"},
             {6, nullptr, "GetMacAddress"},
             {7, nullptr, "CreateBss"},
             {8, nullptr, "DestroyBss"},
@@ -72,38 +75,42 @@ public:
             {14, nullptr, "CancelJoin"},
             {15, nullptr, "Disconnect"},
             {16, nullptr, "SetBeaconLostCount"},
-            {17, nullptr, "Unknown7"},
-            {18, nullptr, "Unknown8"},
-            {19, nullptr, "Unknown9"},
+            {17, nullptr, "Unknown17"},
+            {18, nullptr, "Unknown18"},
+            {19, nullptr, "Unknown19"},
             {20, nullptr, "GetBssIndicationEvent"},
             {21, nullptr, "GetBssIndicationInfo"},
             {22, nullptr, "GetState"},
             {23, nullptr, "GetAllowedChannels"},
             {24, nullptr, "AddIe"},
             {25, nullptr, "DeleteIe"},
-            {26, nullptr, "Unknown10"},
-            {27, nullptr, "Unknown11"},
+            {26, nullptr, "Unknown26"},
+            {27, nullptr, "Unknown27"},
             {28, nullptr, "CreateRxEntry"},
             {29, nullptr, "DeleteRxEntry"},
-            {30, nullptr, "Unknown12"},
-            {31, nullptr, "Unknown13"},
+            {30, nullptr, "Unknown30"},
+            {31, nullptr, "Unknown31"},
             {32, nullptr, "AddMatchingDataToRxEntry"},
             {33, nullptr, "RemoveMatchingDataFromRxEntry"},
             {34, nullptr, "GetScanResult"},
-            {35, nullptr, "Unknown14"},
+            {35, nullptr, "Unknown35"},
             {36, nullptr, "SetActionFrameWithBeacon"},
             {37, nullptr, "CancelActionFrameWithBeacon"},
             {38, nullptr, "CreateRxEntryForActionFrame"},
             {39, nullptr, "DeleteRxEntryForActionFrame"},
-            {40, nullptr, "Unknown15"},
-            {41, nullptr, "Unknown16"},
+            {40, nullptr, "Unknown40"},
+            {41, nullptr, "Unknown41"},
             {42, nullptr, "CancelGetActionFrame"},
             {43, nullptr, "GetRssi"},
-            {44, nullptr, "Unknown17"},
-            {45, nullptr, "Unknown18"},
-            {46, nullptr, "Unknown19"},
-            {47, nullptr, "Unknown20"},
-            {48, nullptr, "Unknown21"},
+            {44, nullptr, "Unknown44"},
+            {45, nullptr, "Unknown45"},
+            {46, nullptr, "Unknown46"},
+            {47, nullptr, "Unknown47"},
+            {48, nullptr, "Unknown48"},
+            {49, nullptr, "Unknown49"},
+            {50, nullptr, "Unknown50"},
+            {51, nullptr, "Unknown51"},
+            {52, nullptr, "Unknown52"},
         };
         // clang-format on
 
@@ -142,18 +149,19 @@ public:
     explicit WLANSocketManager() : ServiceFramework{"wlan:soc"} {
         // clang-format off
         static const FunctionInfo functions[] = {
-            {0, nullptr, "Unknown1"},
-            {1, nullptr, "Unknown2"},
-            {2, nullptr, "Unknown3"},
-            {3, nullptr, "Unknown4"},
-            {4, nullptr, "Unknown5"},
-            {5, nullptr, "Unknown6"},
+            {0, nullptr, "Unknown0"},
+            {1, nullptr, "Unknown1"},
+            {2, nullptr, "Unknown2"},
+            {3, nullptr, "Unknown3"},
+            {4, nullptr, "Unknown4"},
+            {5, nullptr, "Unknown5"},
             {6, nullptr, "GetMacAddress"},
             {7, nullptr, "SwitchTsfTimerFunction"},
-            {8, nullptr, "Unknown7"},
-            {9, nullptr, "Unknown8"},
-            {10, nullptr, "Unknown9"},
-            {11, nullptr, "Unknown10"},
+            {8, nullptr, "Unknown8"},
+            {9, nullptr, "Unknown9"},
+            {10, nullptr, "Unknown10"},
+            {11, nullptr, "Unknown11"},
+            {12, nullptr, "Unknown12"},
         };
         // clang-format on
 

+ 3 - 2
src/core/perf_stats.cpp

@@ -119,13 +119,14 @@ double PerfStats::GetLastFrameTimeScale() {
 }
 
 void FrameLimiter::DoFrameLimiting(microseconds current_system_time_us) {
-    if (!Settings::values.use_frame_limit || Settings::values.use_multi_core) {
+    if (!Settings::values.use_frame_limit.GetValue() ||
+        Settings::values.use_multi_core.GetValue()) {
         return;
     }
 
     auto now = Clock::now();
 
-    const double sleep_scale = Settings::values.frame_limit / 100.0;
+    const double sleep_scale = Settings::values.frame_limit.GetValue() / 100.0;
 
     // Max lag caused by slow frames. Shouldn't be more than the length of a frame at the current
     // speed percent or it will clamp too much and prevent this from properly limiting to that

+ 63 - 21
src/core/settings.cpp

@@ -62,6 +62,7 @@ const std::array<const char*, NumMouseButtons> mapping = {{
 }
 
 Values values = {};
+bool configuring_global = true;
 
 std::string GetTimeZoneString() {
     static constexpr std::array<const char*, 46> timezones{{
@@ -73,9 +74,9 @@ std::string GetTimeZoneString() {
         "UCT",       "Universal", "UTC", "W-SU",    "WET",     "Zulu",
     }};
 
-    ASSERT(Settings::values.time_zone_index < timezones.size());
+    ASSERT(Settings::values.time_zone_index.GetValue() < timezones.size());
 
-    return timezones[Settings::values.time_zone_index];
+    return timezones[Settings::values.time_zone_index.GetValue()];
 }
 
 void Apply() {
@@ -97,25 +98,25 @@ void LogSetting(const std::string& name, const T& value) {
 
 void LogSettings() {
     LOG_INFO(Config, "yuzu Configuration:");
-    LogSetting("System_UseDockedMode", Settings::values.use_docked_mode);
-    LogSetting("System_RngSeed", Settings::values.rng_seed.value_or(0));
+    LogSetting("Controls_UseDockedMode", Settings::values.use_docked_mode);
+    LogSetting("System_RngSeed", Settings::values.rng_seed.GetValue().value_or(0));
     LogSetting("System_CurrentUser", Settings::values.current_user);
-    LogSetting("System_LanguageIndex", Settings::values.language_index);
-    LogSetting("System_RegionIndex", Settings::values.region_index);
-    LogSetting("System_TimeZoneIndex", Settings::values.time_zone_index);
-    LogSetting("Core_UseMultiCore", Settings::values.use_multi_core);
-    LogSetting("Renderer_UseResolutionFactor", Settings::values.resolution_factor);
-    LogSetting("Renderer_UseFrameLimit", Settings::values.use_frame_limit);
-    LogSetting("Renderer_FrameLimit", Settings::values.frame_limit);
-    LogSetting("Renderer_UseDiskShaderCache", Settings::values.use_disk_shader_cache);
-    LogSetting("Renderer_GPUAccuracyLevel", Settings::values.gpu_accuracy);
+    LogSetting("System_LanguageIndex", Settings::values.language_index.GetValue());
+    LogSetting("System_RegionIndex", Settings::values.region_index.GetValue());
+    LogSetting("System_TimeZoneIndex", Settings::values.time_zone_index.GetValue());
+    LogSetting("Core_UseMultiCore", Settings::values.use_multi_core.GetValue());
+    LogSetting("Renderer_UseResolutionFactor", Settings::values.resolution_factor.GetValue());
+    LogSetting("Renderer_UseFrameLimit", Settings::values.use_frame_limit.GetValue());
+    LogSetting("Renderer_FrameLimit", Settings::values.frame_limit.GetValue());
+    LogSetting("Renderer_UseDiskShaderCache", Settings::values.use_disk_shader_cache.GetValue());
+    LogSetting("Renderer_GPUAccuracyLevel", Settings::values.gpu_accuracy.GetValue());
     LogSetting("Renderer_UseAsynchronousGpuEmulation",
-               Settings::values.use_asynchronous_gpu_emulation);
-    LogSetting("Renderer_UseVsync", Settings::values.use_vsync);
-    LogSetting("Renderer_UseAssemblyShaders", Settings::values.use_assembly_shaders);
-    LogSetting("Renderer_AnisotropicFilteringLevel", Settings::values.max_anisotropy);
+               Settings::values.use_asynchronous_gpu_emulation.GetValue());
+    LogSetting("Renderer_UseVsync", Settings::values.use_vsync.GetValue());
+    LogSetting("Renderer_UseAssemblyShaders", Settings::values.use_assembly_shaders.GetValue());
+    LogSetting("Renderer_AnisotropicFilteringLevel", Settings::values.max_anisotropy.GetValue());
     LogSetting("Audio_OutputEngine", Settings::values.sink_id);
-    LogSetting("Audio_EnableAudioStretching", Settings::values.enable_audio_stretching);
+    LogSetting("Audio_EnableAudioStretching", Settings::values.enable_audio_stretching.GetValue());
     LogSetting("Audio_OutputDevice", Settings::values.audio_device_id);
     LogSetting("DataStorage_UseVirtualSd", Settings::values.use_virtual_sd);
     LogSetting("DataStorage_NandDir", FileUtil::GetUserPath(FileUtil::UserPath::NANDDir));
@@ -131,15 +132,56 @@ float Volume() {
     if (values.audio_muted) {
         return 0.0f;
     }
-    return values.volume;
+    return values.volume.GetValue();
 }
 
 bool IsGPULevelExtreme() {
-    return values.gpu_accuracy == GPUAccuracy::Extreme;
+    return values.gpu_accuracy.GetValue() == GPUAccuracy::Extreme;
 }
 
 bool IsGPULevelHigh() {
-    return values.gpu_accuracy == GPUAccuracy::Extreme || values.gpu_accuracy == GPUAccuracy::High;
+    return values.gpu_accuracy.GetValue() == GPUAccuracy::Extreme ||
+           values.gpu_accuracy.GetValue() == GPUAccuracy::High;
+}
+
+void RestoreGlobalState() {
+    // If a game is running, DO NOT restore the global settings state
+    if (Core::System::GetInstance().IsPoweredOn()) {
+        return;
+    }
+
+    // Audio
+    values.enable_audio_stretching.SetGlobal(true);
+    values.volume.SetGlobal(true);
+
+    // Core
+    values.use_multi_core.SetGlobal(true);
+
+    // Renderer
+    values.renderer_backend.SetGlobal(true);
+    values.vulkan_device.SetGlobal(true);
+    values.aspect_ratio.SetGlobal(true);
+    values.max_anisotropy.SetGlobal(true);
+    values.use_frame_limit.SetGlobal(true);
+    values.frame_limit.SetGlobal(true);
+    values.use_disk_shader_cache.SetGlobal(true);
+    values.gpu_accuracy.SetGlobal(true);
+    values.use_asynchronous_gpu_emulation.SetGlobal(true);
+    values.use_vsync.SetGlobal(true);
+    values.use_assembly_shaders.SetGlobal(true);
+    values.use_fast_gpu_time.SetGlobal(true);
+    values.force_30fps_mode.SetGlobal(true);
+    values.bg_red.SetGlobal(true);
+    values.bg_green.SetGlobal(true);
+    values.bg_blue.SetGlobal(true);
+
+    // System
+    values.language_index.SetGlobal(true);
+    values.region_index.SetGlobal(true);
+    values.time_zone_index.SetGlobal(true);
+    values.rng_seed.SetGlobal(true);
+    values.custom_rtc.SetGlobal(true);
+    values.sound_index.SetGlobal(true);
 }
 
 } // namespace Settings

+ 98 - 73
src/core/settings.h

@@ -346,31 +346,6 @@ struct TouchscreenInput {
     u32 rotation_angle;
 };
 
-enum class NANDTotalSize : u64 {
-    S29_1GB = 0x747C00000ULL,
-};
-
-enum class NANDUserSize : u64 {
-    S26GB = 0x680000000ULL,
-};
-
-enum class NANDSystemSize : u64 {
-    S2_5GB = 0xA0000000,
-};
-
-enum class SDMCSize : u64 {
-    S1GB = 0x40000000,
-    S2GB = 0x80000000,
-    S4GB = 0x100000000ULL,
-    S8GB = 0x200000000ULL,
-    S16GB = 0x400000000ULL,
-    S32GB = 0x800000000ULL,
-    S64GB = 0x1000000000ULL,
-    S128GB = 0x2000000000ULL,
-    S256GB = 0x4000000000ULL,
-    S1TB = 0x10000000000ULL,
-};
-
 enum class RendererBackend {
     OpenGL = 0,
     Vulkan = 1,
@@ -382,20 +357,102 @@ enum class GPUAccuracy : u32 {
     Extreme = 2,
 };
 
+enum class CPUAccuracy {
+    Accurate = 0,
+    DebugMode = 1,
+};
+
+extern bool configuring_global;
+
+template <typename Type>
+class Setting final {
+public:
+    Setting() = default;
+    explicit Setting(Type val) : global{val} {}
+    ~Setting() = default;
+    void SetGlobal(bool to_global) {
+        use_global = to_global;
+    }
+    bool UsingGlobal() const {
+        return use_global;
+    }
+    Type GetValue(bool need_global = false) const {
+        if (use_global || need_global) {
+            return global;
+        }
+        return local;
+    }
+    void SetValue(const Type& value) {
+        if (use_global) {
+            global = value;
+        } else {
+            local = value;
+        }
+    }
+
+private:
+    bool use_global = true;
+    Type global{};
+    Type local{};
+};
+
 struct Values {
+    // Audio
+    std::string audio_device_id;
+    std::string sink_id;
+    bool audio_muted;
+    Setting<bool> enable_audio_stretching;
+    Setting<float> volume;
+
+    // Core
+    Setting<bool> use_multi_core;
+
+    // Cpu
+    CPUAccuracy cpu_accuracy;
+
+    bool cpuopt_page_tables;
+    bool cpuopt_block_linking;
+    bool cpuopt_return_stack_buffer;
+    bool cpuopt_fast_dispatcher;
+    bool cpuopt_context_elimination;
+    bool cpuopt_const_prop;
+    bool cpuopt_misc_ir;
+    bool cpuopt_reduce_misalign_checks;
+
+    // Renderer
+    Setting<RendererBackend> renderer_backend;
+    bool renderer_debug;
+    Setting<int> vulkan_device;
+
+    Setting<u16> resolution_factor = Setting(static_cast<u16>(1));
+    Setting<int> aspect_ratio;
+    Setting<int> max_anisotropy;
+    Setting<bool> use_frame_limit;
+    Setting<u16> frame_limit;
+    Setting<bool> use_disk_shader_cache;
+    Setting<GPUAccuracy> gpu_accuracy;
+    Setting<bool> use_asynchronous_gpu_emulation;
+    Setting<bool> use_vsync;
+    Setting<bool> use_assembly_shaders;
+    Setting<bool> force_30fps_mode;
+    Setting<bool> use_fast_gpu_time;
+
+    Setting<float> bg_red;
+    Setting<float> bg_green;
+    Setting<float> bg_blue;
+
     // System
-    bool use_docked_mode;
-    std::optional<u32> rng_seed;
+    Setting<std::optional<u32>> rng_seed;
     // Measured in seconds since epoch
-    std::optional<std::chrono::seconds> custom_rtc;
+    Setting<std::optional<std::chrono::seconds>> custom_rtc;
     // Set on game boot, reset on stop. Seconds difference between current time and `custom_rtc`
     std::chrono::seconds custom_rtc_differential;
 
     s32 current_user;
-    s32 language_index;
-    s32 region_index;
-    s32 time_zone_index;
-    s32 sound_index;
+    Setting<s32> language_index;
+    Setting<s32> region_index;
+    Setting<s32> time_zone_index;
+    Setting<s32> sound_index;
 
     // Controls
     std::array<PlayerInput, 10> players;
@@ -419,51 +476,13 @@ struct Values {
     u16 udp_input_port;
     u8 udp_pad_index;
 
-    // Core
-    bool use_multi_core;
+    bool use_docked_mode;
 
     // Data Storage
     bool use_virtual_sd;
     bool gamecard_inserted;
     bool gamecard_current_game;
     std::string gamecard_path;
-    NANDTotalSize nand_total_size;
-    NANDSystemSize nand_system_size;
-    NANDUserSize nand_user_size;
-    SDMCSize sdmc_size;
-
-    // Renderer
-    RendererBackend renderer_backend;
-    bool renderer_debug;
-    int vulkan_device;
-
-    u16 resolution_factor{1};
-    int aspect_ratio;
-    int max_anisotropy;
-    bool use_frame_limit;
-    u16 frame_limit;
-    bool use_disk_shader_cache;
-    GPUAccuracy gpu_accuracy;
-    bool use_asynchronous_gpu_emulation;
-    bool use_vsync;
-    bool use_assembly_shaders;
-    bool force_30fps_mode;
-    bool use_fast_gpu_time;
-
-    float bg_red;
-    float bg_green;
-    float bg_blue;
-
-    std::string log_filter;
-
-    bool use_dev_keys;
-
-    // Audio
-    bool audio_muted;
-    std::string sink_id;
-    bool enable_audio_stretching;
-    std::string audio_device_id;
-    float volume;
 
     // Debugging
     bool record_frame_times;
@@ -474,10 +493,13 @@ struct Values {
     bool dump_nso;
     bool reporting_services;
     bool quest_flag;
-    bool disable_cpu_opt;
     bool disable_macro_jit;
 
-    // BCAT
+    // Misceallaneous
+    std::string log_filter;
+    bool use_dev_keys;
+
+    // Services
     std::string bcat_backend;
     bool bcat_boxcat_local;
 
@@ -501,4 +523,7 @@ std::string GetTimeZoneString();
 void Apply();
 void LogSettings();
 
+// Restore the global state of all applicable settings in the Values struct
+void RestoreGlobalState();
+
 } // namespace Settings

+ 16 - 11
src/core/telemetry_session.cpp

@@ -189,19 +189,24 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) {
     // Log user configuration information
     constexpr auto field_type = Telemetry::FieldType::UserConfig;
     AddField(field_type, "Audio_SinkId", Settings::values.sink_id);
-    AddField(field_type, "Audio_EnableAudioStretching", Settings::values.enable_audio_stretching);
-    AddField(field_type, "Core_UseMultiCore", Settings::values.use_multi_core);
-    AddField(field_type, "Renderer_Backend", TranslateRenderer(Settings::values.renderer_backend));
-    AddField(field_type, "Renderer_ResolutionFactor", Settings::values.resolution_factor);
-    AddField(field_type, "Renderer_UseFrameLimit", Settings::values.use_frame_limit);
-    AddField(field_type, "Renderer_FrameLimit", Settings::values.frame_limit);
-    AddField(field_type, "Renderer_UseDiskShaderCache", Settings::values.use_disk_shader_cache);
+    AddField(field_type, "Audio_EnableAudioStretching",
+             Settings::values.enable_audio_stretching.GetValue());
+    AddField(field_type, "Core_UseMultiCore", Settings::values.use_multi_core.GetValue());
+    AddField(field_type, "Renderer_Backend",
+             TranslateRenderer(Settings::values.renderer_backend.GetValue()));
+    AddField(field_type, "Renderer_ResolutionFactor",
+             Settings::values.resolution_factor.GetValue());
+    AddField(field_type, "Renderer_UseFrameLimit", Settings::values.use_frame_limit.GetValue());
+    AddField(field_type, "Renderer_FrameLimit", Settings::values.frame_limit.GetValue());
+    AddField(field_type, "Renderer_UseDiskShaderCache",
+             Settings::values.use_disk_shader_cache.GetValue());
     AddField(field_type, "Renderer_GPUAccuracyLevel",
-             TranslateGPUAccuracyLevel(Settings::values.gpu_accuracy));
+             TranslateGPUAccuracyLevel(Settings::values.gpu_accuracy.GetValue()));
     AddField(field_type, "Renderer_UseAsynchronousGpuEmulation",
-             Settings::values.use_asynchronous_gpu_emulation);
-    AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync);
-    AddField(field_type, "Renderer_UseAssemblyShaders", Settings::values.use_assembly_shaders);
+             Settings::values.use_asynchronous_gpu_emulation.GetValue());
+    AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync.GetValue());
+    AddField(field_type, "Renderer_UseAssemblyShaders",
+             Settings::values.use_assembly_shaders.GetValue());
     AddField(field_type, "System_UseDockedMode", Settings::values.use_docked_mode);
 }
 

+ 2 - 1
src/input_common/CMakeLists.txt

@@ -30,7 +30,8 @@ if(SDL2_FOUND)
     target_compile_definitions(input_common PRIVATE HAVE_SDL2)
 endif()
 
-target_link_libraries(input_common PUBLIC ${LIBUSB_LIBRARIES})
+target_include_directories(input_common SYSTEM PRIVATE ${LIBUSB_INCLUDE_DIR})
+target_link_libraries(input_common PRIVATE ${LIBUSB_LIBRARIES})
 
 create_target_directory_groups(input_common)
 target_link_libraries(input_common PUBLIC core PRIVATE common Boost::boost)

+ 6 - 5
src/input_common/gcadapter/gc_adapter.cpp

@@ -4,6 +4,7 @@
 
 #include <chrono>
 #include <thread>
+#include <libusb.h>
 #include "common/logging/log.h"
 #include "input_common/gcadapter/gc_adapter.h"
 
@@ -34,7 +35,7 @@ Adapter::Adapter() {
     }
 }
 
-GCPadStatus Adapter::GetPadStatus(int port, const std::array<u8, 37>& adapter_payload) {
+GCPadStatus Adapter::GetPadStatus(std::size_t port, const std::array<u8, 37>& adapter_payload) {
     GCPadStatus pad = {};
 
     ControllerTypes type = ControllerTypes(adapter_payload[1 + (9 * port)] >> 4);
@@ -205,7 +206,7 @@ void Adapter::StartScanThread() {
     }
 
     detect_thread_running = true;
-    detect_thread = std::thread([=] { ScanThreadFunc(); });
+    detect_thread = std::thread(&Adapter::ScanThreadFunc, this);
 }
 
 void Adapter::StopScanThread() {
@@ -234,7 +235,7 @@ void Adapter::Setup() {
     }
 
     if (devices != nullptr) {
-        for (std::size_t index = 0; index < device_count; ++index) {
+        for (std::size_t index = 0; index < static_cast<std::size_t>(device_count); ++index) {
             if (CheckDeviceAccess(devices[index])) {
                 // GC Adapter found and accessible, registering it
                 GetGCEndpoint(devices[index]);
@@ -368,11 +369,11 @@ void Adapter::Reset() {
     }
 }
 
-bool Adapter::DeviceConnected(int port) {
+bool Adapter::DeviceConnected(std::size_t port) {
     return adapter_controllers_status[port] != ControllerTypes::None;
 }
 
-void Adapter::ResetDeviceType(int port) {
+void Adapter::ResetDeviceType(std::size_t port) {
     adapter_controllers_status[port] = ControllerTypes::None;
 }
 

+ 9 - 6
src/input_common/gcadapter/gc_adapter.h

@@ -8,10 +8,13 @@
 #include <mutex>
 #include <thread>
 #include <unordered_map>
-#include <libusb.h>
 #include "common/common_types.h"
 #include "common/threadsafe_queue.h"
 
+struct libusb_context;
+struct libusb_device;
+struct libusb_device_handle;
+
 namespace GCAdapter {
 
 enum class PadButton {
@@ -91,6 +94,9 @@ public:
     void BeginConfiguration();
     void EndConfiguration();
 
+    /// Returns true if there is a device connected to port
+    bool DeviceConnected(std::size_t port);
+
     std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue();
     const std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue() const;
 
@@ -100,7 +106,7 @@ public:
     int GetOriginValue(int port, int axis) const;
 
 private:
-    GCPadStatus GetPadStatus(int port, const std::array<u8, 37>& adapter_payload);
+    GCPadStatus GetPadStatus(std::size_t port, const std::array<u8, 37>& adapter_payload);
 
     void PadToState(const GCPadStatus& pad, GCState& state);
 
@@ -112,11 +118,8 @@ private:
     /// Stop scanning for the adapter
     void StopScanThread();
 
-    /// Returns true if there is a device connected to port
-    bool DeviceConnected(int port);
-
     /// Resets status of device connected to port
-    void ResetDeviceType(int port);
+    void ResetDeviceType(std::size_t port);
 
     /// Returns true if we successfully gain access to GC Adapter
     bool CheckDeviceAccess(libusb_device* device);

+ 29 - 16
src/input_common/gcadapter/gc_poller.cpp

@@ -6,6 +6,7 @@
 #include <list>
 #include <mutex>
 #include <utility>
+#include "common/assert.h"
 #include "common/threadsafe_queue.h"
 #include "input_common/gcadapter/gc_adapter.h"
 #include "input_common/gcadapter/gc_poller.h"
@@ -20,7 +21,10 @@ public:
     ~GCButton() override;
 
     bool GetStatus() const override {
-        return gcadapter->GetPadState()[port].buttons.at(button);
+        if (gcadapter->DeviceConnected(port)) {
+            return gcadapter->GetPadState()[port].buttons.at(button);
+        }
+        return false;
     }
 
 private:
@@ -37,14 +41,17 @@ public:
           gcadapter(adapter), origin_value(adapter->GetOriginValue(port_, axis_)) {}
 
     bool GetStatus() const override {
-        const float current_axis_value = gcadapter->GetPadState()[port].axes.at(axis);
-        const float axis_value = (current_axis_value - origin_value) / 128.0f;
-        if (trigger_if_greater) {
-            // TODO: Might be worthwile to set a slider for the trigger threshold. It is currently
-            // always set to 0.5 in configure_input_player.cpp ZL/ZR HandleClick
-            return axis_value > threshold;
+        if (gcadapter->DeviceConnected(port)) {
+            const float current_axis_value = gcadapter->GetPadState()[port].axes.at(axis);
+            const float axis_value = (current_axis_value - origin_value) / 128.0f;
+            if (trigger_if_greater) {
+                // TODO: Might be worthwile to set a slider for the trigger threshold. It is
+                // currently always set to 0.5 in configure_input_player.cpp ZL/ZR HandleClick
+                return axis_value > threshold;
+            }
+            return axis_value < -threshold;
         }
-        return axis_value < -threshold;
+        return false;
     }
 
 private:
@@ -90,9 +97,12 @@ std::unique_ptr<Input::ButtonDevice> GCButtonFactory::Create(const Common::Param
         return std::make_unique<GCAxisButton>(port, axis, threshold, trigger_if_greater,
                                               adapter.get());
     }
+
+    UNREACHABLE();
+    return nullptr;
 }
 
-Common::ParamPackage GCButtonFactory::GetNextInput() {
+Common::ParamPackage GCButtonFactory::GetNextInput() const {
     Common::ParamPackage params;
     GCAdapter::GCPadStatus pad;
     auto& queue = adapter->GetPadQueue();
@@ -145,12 +155,15 @@ public:
           origin_value_y(adapter->GetOriginValue(port_, axis_y_)) {}
 
     float GetAxis(int axis) const {
-        std::lock_guard lock{mutex};
-        const auto origin_value = axis % 2 == 0 ? origin_value_x : origin_value_y;
-        // division is not by a perfect 128 to account for some variance in center location
-        // e.g. my device idled at 131 in X, 120 in Y, and full range of motion was in range
-        // [20-230]
-        return (gcadapter->GetPadState()[port].axes.at(axis) - origin_value) / 95.0f;
+        if (gcadapter->DeviceConnected(port)) {
+            std::lock_guard lock{mutex};
+            const auto origin_value = axis % 2 == 0 ? origin_value_x : origin_value_y;
+            // division is not by a perfect 128 to account for some variance in center location
+            // e.g. my device idled at 131 in X, 120 in Y, and full range of motion was in range
+            // [20-230]
+            return (gcadapter->GetPadState()[port].axes.at(axis) - origin_value) / 95.0f;
+        }
+        return 0.0f;
     }
 
     std::pair<float, float> GetAnalog(int axis_x, int axis_y) const {
@@ -250,7 +263,7 @@ Common::ParamPackage GCAnalogFactory::GetNextInput() {
             const u8 axis = static_cast<u8>(pad.axis);
             if (analog_x_axis == -1) {
                 analog_x_axis = axis;
-                controller_number = port;
+                controller_number = static_cast<int>(port);
             } else if (analog_y_axis == -1 && analog_x_axis != axis && controller_number == port) {
                 analog_y_axis = axis;
             }

+ 1 - 1
src/input_common/gcadapter/gc_poller.h

@@ -25,7 +25,7 @@ public:
      */
     std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override;
 
-    Common::ParamPackage GetNextInput();
+    Common::ParamPackage GetNextInput() const;
 
     /// For device input configuration/polling
     void BeginConfiguration();

+ 0 - 1
src/input_common/main.cpp

@@ -4,7 +4,6 @@
 
 #include <memory>
 #include <thread>
-#include <libusb.h>
 #include "common/param_package.h"
 #include "input_common/analog_from_button.h"
 #include "input_common/gcadapter/gc_adapter.h"

+ 1 - 1
src/input_common/udp/client.cpp

@@ -234,7 +234,7 @@ CalibrationConfigurationJob::CalibrationConfigurationJob(
     std::function<void(Status)> status_callback,
     std::function<void(u16, u16, u16, u16)> data_callback) {
 
-    std::thread([=] {
+    std::thread([=, this] {
         constexpr u16 CALIBRATION_THRESHOLD = 100;
 
         u16 min_x{UINT16_MAX};

+ 4 - 2
src/video_core/buffer_cache/buffer_cache.h

@@ -96,7 +96,8 @@ public:
         }
         if (is_written) {
             map->MarkAsModified(true, GetModifiedTicks());
-            if (Settings::IsGPULevelHigh() && Settings::values.use_asynchronous_gpu_emulation) {
+            if (Settings::IsGPULevelHigh() &&
+                Settings::values.use_asynchronous_gpu_emulation.GetValue()) {
                 MarkForAsyncFlush(map);
             }
             if (!map->is_written) {
@@ -369,7 +370,8 @@ private:
         }
         if (modified_inheritance) {
             map->MarkAsModified(true, GetModifiedTicks());
-            if (Settings::IsGPULevelHigh() && Settings::values.use_asynchronous_gpu_emulation) {
+            if (Settings::IsGPULevelHigh() &&
+                Settings::values.use_asynchronous_gpu_emulation.GetValue()) {
                 MarkForAsyncFlush(map);
             }
         }

+ 1 - 1
src/video_core/gpu.cpp

@@ -157,7 +157,7 @@ u64 GPU::GetTicks() const {
     constexpr u64 gpu_ticks_den = 625;
 
     u64 nanoseconds = system.CoreTiming().GetGlobalTimeNs().count();
-    if (Settings::values.use_fast_gpu_time) {
+    if (Settings::values.use_fast_gpu_time.GetValue()) {
         nanoseconds /= 256;
     }
     const u64 nanoseconds_num = nanoseconds / gpu_ticks_den;

+ 1 - 1
src/video_core/query_cache.h

@@ -132,7 +132,7 @@ public:
         }
 
         query->BindCounter(Stream(type).Current(), timestamp);
-        if (Settings::values.use_asynchronous_gpu_emulation) {
+        if (Settings::values.use_asynchronous_gpu_emulation.GetValue()) {
             AsyncFlushQuery(cpu_addr);
         }
     }

+ 1 - 1
src/video_core/renderer_base.cpp

@@ -18,7 +18,7 @@ RendererBase::~RendererBase() = default;
 void RendererBase::RefreshBaseSettings() {
     UpdateCurrentFramebufferLayout();
 
-    renderer_settings.use_framelimiter = Settings::values.use_frame_limit;
+    renderer_settings.use_framelimiter = Settings::values.use_frame_limit.GetValue();
     renderer_settings.set_background_color = true;
 }
 

+ 4 - 4
src/video_core/renderer_opengl/gl_device.cpp

@@ -229,15 +229,15 @@ Device::Device()
     // uniform buffers as "push constants"
     has_fast_buffer_sub_data = is_nvidia && !disable_fast_buffer_sub_data;
 
-    use_assembly_shaders = Settings::values.use_assembly_shaders && GLAD_GL_NV_gpu_program5 &&
-                           GLAD_GL_NV_compute_program5 && GLAD_GL_NV_transform_feedback &&
-                           GLAD_GL_NV_transform_feedback2;
+    use_assembly_shaders = Settings::values.use_assembly_shaders.GetValue() &&
+                           GLAD_GL_NV_gpu_program5 && GLAD_GL_NV_compute_program5 &&
+                           GLAD_GL_NV_transform_feedback && GLAD_GL_NV_transform_feedback2;
 
     LOG_INFO(Render_OpenGL, "Renderer_VariableAOFFI: {}", has_variable_aoffi);
     LOG_INFO(Render_OpenGL, "Renderer_ComponentIndexingBug: {}", has_component_indexing_bug);
     LOG_INFO(Render_OpenGL, "Renderer_PreciseBug: {}", has_precise_bug);
 
-    if (Settings::values.use_assembly_shaders && !use_assembly_shaders) {
+    if (Settings::values.use_assembly_shaders.GetValue() && !use_assembly_shaders) {
         LOG_ERROR(Render_OpenGL, "Assembly shaders enabled but not supported");
     }
 }

+ 1 - 1
src/video_core/renderer_opengl/gl_shader_disk_cache.cpp

@@ -213,7 +213,7 @@ ShaderDiskCacheOpenGL::~ShaderDiskCacheOpenGL() = default;
 std::optional<std::vector<ShaderDiskCacheEntry>> ShaderDiskCacheOpenGL::LoadTransferable() {
     // Skip games without title id
     const bool has_title_id = system.CurrentProcess()->GetTitleID() != 0;
-    if (!Settings::values.use_disk_shader_cache || !has_title_id) {
+    if (!Settings::values.use_disk_shader_cache.GetValue() || !has_title_id) {
         return {};
     }
 

+ 4 - 4
src/video_core/renderer_opengl/renderer_opengl.cpp

@@ -455,8 +455,8 @@ void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color
 void RendererOpenGL::InitOpenGLObjects() {
     frame_mailbox = std::make_unique<FrameMailbox>();
 
-    glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue,
-                 0.0f);
+    glClearColor(Settings::values.bg_red.GetValue(), Settings::values.bg_green.GetValue(),
+                 Settings::values.bg_blue.GetValue(), 0.0f);
 
     // Create shader programs
     OGLShader vertex_shader;
@@ -561,8 +561,8 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
 void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
     if (renderer_settings.set_background_color) {
         // Update background color before drawing
-        glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue,
-                     0.0f);
+        glClearColor(Settings::values.bg_red.GetValue(), Settings::values.bg_green.GetValue(),
+                     Settings::values.bg_blue.GetValue(), 0.0f);
     }
 
     // Set projection matrix

+ 1 - 1
src/video_core/renderer_vulkan/renderer_vulkan.cpp

@@ -409,7 +409,7 @@ bool RendererVulkan::PickDevices() {
         return false;
     }
 
-    const s32 device_index = Settings::values.vulkan_device;
+    const s32 device_index = Settings::values.vulkan_device.GetValue();
     if (device_index < 0 || device_index >= static_cast<s32>(devices->size())) {
         LOG_ERROR(Render_Vulkan, "Invalid device index {}!", device_index);
         return false;

+ 6 - 6
src/video_core/renderer_vulkan/vk_rasterizer.cpp

@@ -332,23 +332,23 @@ private:
 
         if constexpr (has_extended_dynamic_state) {
             // With extended dynamic states we can specify the length and stride of a vertex buffer
-            // std::array<VkDeviceSize, N> sizes;
+            std::array<VkDeviceSize, N> sizes;
             std::array<u16, N> strides;
-            // std::copy(vertex.sizes.begin(), vertex.sizes.begin() + N, sizes.begin());
+            std::copy(vertex.sizes.begin(), vertex.sizes.begin() + N, sizes.begin());
             std::copy(vertex.strides.begin(), vertex.strides.begin() + N, strides.begin());
 
             if constexpr (is_indexed) {
                 scheduler.Record(
-                    [buffers, offsets, strides, index = index](vk::CommandBuffer cmdbuf) {
+                    [buffers, offsets, sizes, strides, index = index](vk::CommandBuffer cmdbuf) {
                         cmdbuf.BindIndexBuffer(index.buffer, index.offset, index.type);
                         cmdbuf.BindVertexBuffers2EXT(0, static_cast<u32>(N), buffers.data(),
-                                                     offsets.data(), nullptr,
+                                                     offsets.data(), sizes.data(),
                                                      ExpandStrides(strides).data());
                     });
             } else {
-                scheduler.Record([buffers, offsets, strides](vk::CommandBuffer cmdbuf) {
+                scheduler.Record([buffers, offsets, sizes, strides](vk::CommandBuffer cmdbuf) {
                     cmdbuf.BindVertexBuffers2EXT(0, static_cast<u32>(N), buffers.data(),
-                                                 offsets.data(), nullptr,
+                                                 offsets.data(), sizes.data(),
                                                  ExpandStrides(strides).data());
                 });
             }

+ 1 - 1
src/video_core/renderer_vulkan/vk_stream_buffer.cpp

@@ -121,7 +121,7 @@ void VKStreamBuffer::CreateBuffers(VkBufferUsageFlags usage) {
 
     // Substract from the preferred heap size some bytes to avoid getting out of memory.
     const VkDeviceSize heap_size = memory_properties.memoryHeaps[preferred_heap].size;
-    const VkDeviceSize allocable_size = heap_size - 4 * 1024 * 1024;
+    const VkDeviceSize allocable_size = heap_size - 9 * 1024 * 1024;
 
     VkBufferCreateInfo buffer_ci;
     buffer_ci.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;

+ 1 - 1
src/video_core/texture_cache/texture_cache.h

@@ -249,7 +249,7 @@ public:
             auto& surface = render_targets[index].target;
             surface->MarkAsRenderTarget(false, NO_RT);
             const auto& cr_params = surface->GetSurfaceParams();
-            if (!cr_params.is_tiled && Settings::values.use_asynchronous_gpu_emulation) {
+            if (!cr_params.is_tiled && Settings::values.use_asynchronous_gpu_emulation.GetValue()) {
                 AsyncFlushSurface(surface);
             }
         }

+ 1 - 1
src/video_core/textures/texture.cpp

@@ -48,7 +48,7 @@ constexpr std::array<float, 256> SRGB_CONVERSION_LUT = {
 };
 
 unsigned SettingsMinimumAnisotropy() noexcept {
-    switch (static_cast<Anisotropy>(Settings::values.max_anisotropy)) {
+    switch (static_cast<Anisotropy>(Settings::values.max_anisotropy.GetValue())) {
     default:
     case Anisotropy::Default:
         return 1U;

+ 4 - 4
src/video_core/video_core.cpp

@@ -19,7 +19,7 @@ namespace {
 std::unique_ptr<VideoCore::RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window,
                                                         Core::System& system,
                                                         Core::Frontend::GraphicsContext& context) {
-    switch (Settings::values.renderer_backend) {
+    switch (Settings::values.renderer_backend.GetValue()) {
     case Settings::RendererBackend::OpenGL:
         return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system, context);
 #ifdef HAS_VULKAN
@@ -42,7 +42,7 @@ std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Cor
         return nullptr;
     }
 
-    if (Settings::values.use_asynchronous_gpu_emulation) {
+    if (Settings::values.use_asynchronous_gpu_emulation.GetValue()) {
         return std::make_unique<VideoCommon::GPUAsynch>(system, std::move(renderer),
                                                         std::move(context));
     }
@@ -51,8 +51,8 @@ std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Cor
 
 u16 GetResolutionScaleFactor(const RendererBase& renderer) {
     return static_cast<u16>(
-        Settings::values.resolution_factor != 0
-            ? Settings::values.resolution_factor
+        Settings::values.resolution_factor.GetValue() != 0
+            ? Settings::values.resolution_factor.GetValue()
             : renderer.GetRenderWindow().GetFramebufferLayout().GetScalingRatio());
 }
 

+ 19 - 6
src/yuzu/CMakeLists.txt

@@ -24,10 +24,18 @@ add_executable(yuzu
     compatibility_list.h
     configuration/config.cpp
     configuration/config.h
+    configuration/configuration_shared.cpp
+    configuration/configuration_shared.h
     configuration/configure.ui
     configuration/configure_audio.cpp
     configuration/configure_audio.h
     configuration/configure_audio.ui
+    configuration/configure_cpu.cpp
+    configuration/configure_cpu.h
+    configuration/configure_cpu.ui
+    configuration/configure_cpu_debug.cpp
+    configuration/configure_cpu_debug.h
+    configuration/configure_cpu_debug.ui
     configuration/configure_debug.cpp
     configuration/configure_debug.h
     configuration/configure_debug.ui
@@ -60,9 +68,12 @@ add_executable(yuzu
     configuration/configure_mouse_advanced.cpp
     configuration/configure_mouse_advanced.h
     configuration/configure_mouse_advanced.ui
-    configuration/configure_per_general.cpp
-    configuration/configure_per_general.h
-    configuration/configure_per_general.ui
+    configuration/configure_per_game.cpp
+    configuration/configure_per_game.h
+    configuration/configure_per_game.ui
+    configuration/configure_per_game_addons.cpp
+    configuration/configure_per_game_addons.h
+    configuration/configure_per_game_addons.ui
     configuration/configure_profile_manager.cpp
     configuration/configure_profile_manager.h
     configuration/configure_profile_manager.ui
@@ -93,11 +104,13 @@ add_executable(yuzu
     game_list_p.h
     game_list_worker.cpp
     game_list_worker.h
+    hotkeys.cpp
+    hotkeys.h
+    install_dialog.cpp
+    install_dialog.h
     loading_screen.cpp
     loading_screen.h
     loading_screen.ui
-    hotkeys.cpp
-    hotkeys.h
     main.cpp
     main.h
     main.ui
@@ -147,7 +160,7 @@ endif()
 create_target_directory_groups(yuzu)
 
 target_link_libraries(yuzu PRIVATE common core input_common video_core)
-target_link_libraries(yuzu PRIVATE Boost::boost glad Qt5::OpenGL Qt5::Widgets)
+target_link_libraries(yuzu PRIVATE Boost::boost glad Qt5::Widgets)
 target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
 
 if (ENABLE_VULKAN AND NOT WIN32)

+ 4 - 4
src/yuzu/bootmanager.cpp

@@ -145,7 +145,7 @@ public:
 
         // disable vsync for any shared contexts
         auto format = share_context->format();
-        format.setSwapInterval(main_surface ? Settings::values.use_vsync : 0);
+        format.setSwapInterval(main_surface ? Settings::values.use_vsync.GetValue() : 0);
 
         context = std::make_unique<QOpenGLContext>();
         context->setShareContext(share_context);
@@ -495,7 +495,7 @@ void GRenderWindow::resizeEvent(QResizeEvent* event) {
 
 std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const {
 #ifdef HAS_OPENGL
-    if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) {
+    if (Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::OpenGL) {
         auto c = static_cast<OpenGLSharedContext*>(main_context.get());
         // Bind the shared contexts to the main surface in case the backend wants to take over
         // presentation
@@ -511,7 +511,7 @@ bool GRenderWindow::InitRenderTarget() {
 
     first_frame = false;
 
-    switch (Settings::values.renderer_backend) {
+    switch (Settings::values.renderer_backend.GetValue()) {
     case Settings::RendererBackend::OpenGL:
         if (!InitializeOpenGL()) {
             return false;
@@ -538,7 +538,7 @@ bool GRenderWindow::InitRenderTarget() {
     OnFramebufferSizeChanged();
     BackupGeometry();
 
-    if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) {
+    if (Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::OpenGL) {
         if (!LoadOpenGL()) {
             return false;
         }

+ 272 - 152
src/yuzu/configuration/config.cpp

@@ -13,17 +13,20 @@
 #include "input_common/udp/client.h"
 #include "yuzu/configuration/config.h"
 
-Config::Config() {
+Config::Config(const std::string& config_file, bool is_global) {
     // TODO: Don't hardcode the path; let the frontend decide where to put the config files.
-    qt_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "qt-config.ini";
+    qt_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + config_file;
     FileUtil::CreateFullPath(qt_config_loc);
     qt_config =
         std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat);
+    global = is_global;
     Reload();
 }
 
 Config::~Config() {
-    Save();
+    if (global) {
+        Save();
+    }
 }
 
 const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = {
@@ -402,16 +405,19 @@ void Config::ApplyDefaultProfileIfInputInvalid() {
 void Config::ReadAudioValues() {
     qt_config->beginGroup(QStringLiteral("Audio"));
 
-    Settings::values.sink_id = ReadSetting(QStringLiteral("output_engine"), QStringLiteral("auto"))
-                                   .toString()
-                                   .toStdString();
-    Settings::values.enable_audio_stretching =
-        ReadSetting(QStringLiteral("enable_audio_stretching"), true).toBool();
-    Settings::values.audio_device_id =
-        ReadSetting(QStringLiteral("output_device"), QStringLiteral("auto"))
-            .toString()
-            .toStdString();
-    Settings::values.volume = ReadSetting(QStringLiteral("volume"), 1).toFloat();
+    if (global) {
+        Settings::values.sink_id =
+            ReadSetting(QStringLiteral("output_engine"), QStringLiteral("auto"))
+                .toString()
+                .toStdString();
+        Settings::values.audio_device_id =
+            ReadSetting(QStringLiteral("output_device"), QStringLiteral("auto"))
+                .toString()
+                .toStdString();
+    }
+    ReadSettingGlobal(Settings::values.enable_audio_stretching,
+                      QStringLiteral("enable_audio_stretching"), true);
+    ReadSettingGlobal(Settings::values.volume, QStringLiteral("volume"), 1);
 
     qt_config->endGroup();
 }
@@ -440,6 +446,8 @@ void Config::ReadControlValues() {
             .toInt());
     Settings::values.udp_pad_index =
         static_cast<u8>(ReadSetting(QStringLiteral("udp_pad_index"), 0).toUInt());
+    Settings::values.use_docked_mode =
+        ReadSetting(QStringLiteral("use_docked_mode"), false).toBool();
 
     qt_config->endGroup();
 }
@@ -447,7 +455,7 @@ void Config::ReadControlValues() {
 void Config::ReadCoreValues() {
     qt_config->beginGroup(QStringLiteral("Core"));
 
-    Settings::values.use_multi_core = ReadSetting(QStringLiteral("use_multi_core"), false).toBool();
+    ReadSettingGlobal(Settings::values.use_multi_core, QStringLiteral("use_multi_core"), false);
 
     qt_config->endGroup();
 }
@@ -497,22 +505,6 @@ void Config::ReadDataStorageValues() {
         ReadSetting(QStringLiteral("gamecard_current_game"), false).toBool();
     Settings::values.gamecard_path =
         ReadSetting(QStringLiteral("gamecard_path"), QStringLiteral("")).toString().toStdString();
-    Settings::values.nand_total_size = static_cast<Settings::NANDTotalSize>(
-        ReadSetting(QStringLiteral("nand_total_size"),
-                    QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDTotalSize::S29_1GB)))
-            .toULongLong());
-    Settings::values.nand_user_size = static_cast<Settings::NANDUserSize>(
-        ReadSetting(QStringLiteral("nand_user_size"),
-                    QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDUserSize::S26GB)))
-            .toULongLong());
-    Settings::values.nand_system_size = static_cast<Settings::NANDSystemSize>(
-        ReadSetting(QStringLiteral("nand_system_size"),
-                    QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDSystemSize::S2_5GB)))
-            .toULongLong());
-    Settings::values.sdmc_size = static_cast<Settings::SDMCSize>(
-        ReadSetting(QStringLiteral("sdmc_size"),
-                    QVariant::fromValue<u64>(static_cast<u64>(Settings::SDMCSize::S16GB)))
-            .toULongLong());
 
     qt_config->endGroup();
 }
@@ -532,8 +524,6 @@ void Config::ReadDebuggingValues() {
     Settings::values.reporting_services =
         ReadSetting(QStringLiteral("reporting_services"), false).toBool();
     Settings::values.quest_flag = ReadSetting(QStringLiteral("quest_flag"), false).toBool();
-    Settings::values.disable_cpu_opt =
-        ReadSetting(QStringLiteral("disable_cpu_opt"), false).toBool();
     Settings::values.disable_macro_jit =
         ReadSetting(QStringLiteral("disable_macro_jit"), false).toBool();
 
@@ -625,35 +615,59 @@ void Config::ReadPathValues() {
     qt_config->endGroup();
 }
 
+void Config::ReadCpuValues() {
+    qt_config->beginGroup(QStringLiteral("Cpu"));
+
+    if (global) {
+        Settings::values.cpu_accuracy = static_cast<Settings::CPUAccuracy>(
+            ReadSetting(QStringLiteral("cpu_accuracy"), 0).toInt());
+
+        Settings::values.cpuopt_page_tables =
+            ReadSetting(QStringLiteral("cpuopt_page_tables"), true).toBool();
+        Settings::values.cpuopt_block_linking =
+            ReadSetting(QStringLiteral("cpuopt_block_linking"), true).toBool();
+        Settings::values.cpuopt_return_stack_buffer =
+            ReadSetting(QStringLiteral("cpuopt_return_stack_buffer"), true).toBool();
+        Settings::values.cpuopt_fast_dispatcher =
+            ReadSetting(QStringLiteral("cpuopt_fast_dispatcher"), true).toBool();
+        Settings::values.cpuopt_context_elimination =
+            ReadSetting(QStringLiteral("cpuopt_context_elimination"), true).toBool();
+        Settings::values.cpuopt_const_prop =
+            ReadSetting(QStringLiteral("cpuopt_const_prop"), true).toBool();
+        Settings::values.cpuopt_misc_ir =
+            ReadSetting(QStringLiteral("cpuopt_misc_ir"), true).toBool();
+        Settings::values.cpuopt_reduce_misalign_checks =
+            ReadSetting(QStringLiteral("cpuopt_reduce_misalign_checks"), true).toBool();
+    }
+
+    qt_config->endGroup();
+}
+
 void Config::ReadRendererValues() {
     qt_config->beginGroup(QStringLiteral("Renderer"));
 
-    Settings::values.renderer_backend =
-        static_cast<Settings::RendererBackend>(ReadSetting(QStringLiteral("backend"), 0).toInt());
-    Settings::values.renderer_debug = ReadSetting(QStringLiteral("debug"), false).toBool();
-    Settings::values.vulkan_device = ReadSetting(QStringLiteral("vulkan_device"), 0).toInt();
-    Settings::values.aspect_ratio = ReadSetting(QStringLiteral("aspect_ratio"), 0).toInt();
-    Settings::values.max_anisotropy = ReadSetting(QStringLiteral("max_anisotropy"), 0).toInt();
-    Settings::values.use_frame_limit =
-        ReadSetting(QStringLiteral("use_frame_limit"), true).toBool();
-    Settings::values.frame_limit = ReadSetting(QStringLiteral("frame_limit"), 100).toUInt();
-    Settings::values.use_disk_shader_cache =
-        ReadSetting(QStringLiteral("use_disk_shader_cache"), true).toBool();
-    const int gpu_accuracy_level = ReadSetting(QStringLiteral("gpu_accuracy"), 0).toInt();
-    Settings::values.gpu_accuracy = static_cast<Settings::GPUAccuracy>(gpu_accuracy_level);
-    Settings::values.use_asynchronous_gpu_emulation =
-        ReadSetting(QStringLiteral("use_asynchronous_gpu_emulation"), false).toBool();
-    Settings::values.use_vsync = ReadSetting(QStringLiteral("use_vsync"), true).toBool();
-    Settings::values.use_assembly_shaders =
-        ReadSetting(QStringLiteral("use_assembly_shaders"), false).toBool();
-    Settings::values.use_fast_gpu_time =
-        ReadSetting(QStringLiteral("use_fast_gpu_time"), true).toBool();
-    Settings::values.force_30fps_mode =
-        ReadSetting(QStringLiteral("force_30fps_mode"), false).toBool();
-
-    Settings::values.bg_red = ReadSetting(QStringLiteral("bg_red"), 0.0).toFloat();
-    Settings::values.bg_green = ReadSetting(QStringLiteral("bg_green"), 0.0).toFloat();
-    Settings::values.bg_blue = ReadSetting(QStringLiteral("bg_blue"), 0.0).toFloat();
+    ReadSettingGlobal(Settings::values.renderer_backend, QStringLiteral("backend"), 0);
+    ReadSettingGlobal(Settings::values.renderer_debug, QStringLiteral("debug"), false);
+    ReadSettingGlobal(Settings::values.vulkan_device, QStringLiteral("vulkan_device"), 0);
+    ReadSettingGlobal(Settings::values.aspect_ratio, QStringLiteral("aspect_ratio"), 0);
+    ReadSettingGlobal(Settings::values.max_anisotropy, QStringLiteral("max_anisotropy"), 0);
+    ReadSettingGlobal(Settings::values.use_frame_limit, QStringLiteral("use_frame_limit"), true);
+    ReadSettingGlobal(Settings::values.frame_limit, QStringLiteral("frame_limit"), 100);
+    ReadSettingGlobal(Settings::values.use_disk_shader_cache,
+                      QStringLiteral("use_disk_shader_cache"), true);
+    ReadSettingGlobal(Settings::values.gpu_accuracy, QStringLiteral("gpu_accuracy"), 0);
+    ReadSettingGlobal(Settings::values.use_asynchronous_gpu_emulation,
+                      QStringLiteral("use_asynchronous_gpu_emulation"), false);
+    ReadSettingGlobal(Settings::values.use_vsync, QStringLiteral("use_vsync"), true);
+    ReadSettingGlobal(Settings::values.use_assembly_shaders, QStringLiteral("use_assembly_shaders"),
+                      false);
+    ReadSettingGlobal(Settings::values.use_fast_gpu_time, QStringLiteral("use_fast_gpu_time"),
+                      true);
+    ReadSettingGlobal(Settings::values.force_30fps_mode, QStringLiteral("force_30fps_mode"), false);
+
+    ReadSettingGlobal(Settings::values.bg_red, QStringLiteral("bg_red"), 0.0);
+    ReadSettingGlobal(Settings::values.bg_green, QStringLiteral("bg_green"), 0.0);
+    ReadSettingGlobal(Settings::values.bg_blue, QStringLiteral("bg_blue"), 0.0);
 
     qt_config->endGroup();
 }
@@ -682,35 +696,45 @@ void Config::ReadShortcutValues() {
 void Config::ReadSystemValues() {
     qt_config->beginGroup(QStringLiteral("System"));
 
-    Settings::values.use_docked_mode =
-        ReadSetting(QStringLiteral("use_docked_mode"), false).toBool();
-
-    Settings::values.current_user = std::clamp<int>(
-        ReadSetting(QStringLiteral("current_user"), 0).toInt(), 0, Service::Account::MAX_USERS - 1);
+    ReadSettingGlobal(Settings::values.current_user, QStringLiteral("current_user"), 0);
+    Settings::values.current_user =
+        std::clamp<int>(Settings::values.current_user, 0, Service::Account::MAX_USERS - 1);
 
-    Settings::values.language_index = ReadSetting(QStringLiteral("language_index"), 1).toInt();
+    ReadSettingGlobal(Settings::values.language_index, QStringLiteral("language_index"), 1);
 
-    Settings::values.region_index = ReadSetting(QStringLiteral("region_index"), 1).toInt();
+    ReadSettingGlobal(Settings::values.region_index, QStringLiteral("region_index"), 1);
 
-    Settings::values.time_zone_index = ReadSetting(QStringLiteral("time_zone_index"), 0).toInt();
+    ReadSettingGlobal(Settings::values.time_zone_index, QStringLiteral("time_zone_index"), 0);
 
-    const auto rng_seed_enabled = ReadSetting(QStringLiteral("rng_seed_enabled"), false).toBool();
-    if (rng_seed_enabled) {
-        Settings::values.rng_seed = ReadSetting(QStringLiteral("rng_seed"), 0).toULongLong();
-    } else {
-        Settings::values.rng_seed = std::nullopt;
+    bool rng_seed_enabled;
+    ReadSettingGlobal(rng_seed_enabled, QStringLiteral("rng_seed_enabled"), false);
+    bool rng_seed_global =
+        global || qt_config->value(QStringLiteral("rng_seed/use_global"), true).toBool();
+    Settings::values.rng_seed.SetGlobal(rng_seed_global);
+    if (global || !rng_seed_global) {
+        if (rng_seed_enabled) {
+            Settings::values.rng_seed.SetValue(
+                ReadSetting(QStringLiteral("rng_seed"), 0).toULongLong());
+        } else {
+            Settings::values.rng_seed.SetValue(std::nullopt);
+        }
     }
 
-    const auto custom_rtc_enabled =
-        ReadSetting(QStringLiteral("custom_rtc_enabled"), false).toBool();
-    if (custom_rtc_enabled) {
-        Settings::values.custom_rtc =
-            std::chrono::seconds(ReadSetting(QStringLiteral("custom_rtc"), 0).toULongLong());
-    } else {
-        Settings::values.custom_rtc = std::nullopt;
+    bool custom_rtc_enabled;
+    ReadSettingGlobal(custom_rtc_enabled, QStringLiteral("custom_rtc_enabled"), false);
+    bool custom_rtc_global =
+        global || qt_config->value(QStringLiteral("custom_rtc/use_global"), true).toBool();
+    Settings::values.custom_rtc.SetGlobal(custom_rtc_global);
+    if (global || !custom_rtc_global) {
+        if (custom_rtc_enabled) {
+            Settings::values.custom_rtc.SetValue(
+                std::chrono::seconds(ReadSetting(QStringLiteral("custom_rtc"), 0).toULongLong()));
+        } else {
+            Settings::values.custom_rtc.SetValue(std::nullopt);
+        }
     }
 
-    Settings::values.sound_index = ReadSetting(QStringLiteral("sound_index"), 1).toInt();
+    ReadSettingGlobal(Settings::values.sound_index, QStringLiteral("sound_index"), 1);
 
     qt_config->endGroup();
 }
@@ -804,18 +828,21 @@ void Config::ReadWebServiceValues() {
 }
 
 void Config::ReadValues() {
-    ReadControlValues();
+    if (global) {
+        ReadControlValues();
+        ReadDataStorageValues();
+        ReadDebuggingValues();
+        ReadDisabledAddOnValues();
+        ReadServiceValues();
+        ReadUIValues();
+        ReadWebServiceValues();
+        ReadMiscellaneousValues();
+    }
     ReadCoreValues();
+    ReadCpuValues();
     ReadRendererValues();
     ReadAudioValues();
-    ReadDataStorageValues();
     ReadSystemValues();
-    ReadMiscellaneousValues();
-    ReadDebuggingValues();
-    ReadWebServiceValues();
-    ReadServiceValues();
-    ReadDisabledAddOnValues();
-    ReadUIValues();
 }
 
 void Config::SavePlayerValues() {
@@ -902,30 +929,36 @@ void Config::SaveTouchscreenValues() {
 }
 
 void Config::SaveValues() {
-    SaveControlValues();
+    if (global) {
+        SaveControlValues();
+        SaveDataStorageValues();
+        SaveDebuggingValues();
+        SaveDisabledAddOnValues();
+        SaveServiceValues();
+        SaveUIValues();
+        SaveWebServiceValues();
+        SaveMiscellaneousValues();
+    }
     SaveCoreValues();
+    SaveCpuValues();
     SaveRendererValues();
     SaveAudioValues();
-    SaveDataStorageValues();
     SaveSystemValues();
-    SaveMiscellaneousValues();
-    SaveDebuggingValues();
-    SaveWebServiceValues();
-    SaveServiceValues();
-    SaveDisabledAddOnValues();
-    SaveUIValues();
 }
 
 void Config::SaveAudioValues() {
     qt_config->beginGroup(QStringLiteral("Audio"));
 
-    WriteSetting(QStringLiteral("output_engine"), QString::fromStdString(Settings::values.sink_id),
-                 QStringLiteral("auto"));
-    WriteSetting(QStringLiteral("enable_audio_stretching"),
-                 Settings::values.enable_audio_stretching, true);
-    WriteSetting(QStringLiteral("output_device"),
-                 QString::fromStdString(Settings::values.audio_device_id), QStringLiteral("auto"));
-    WriteSetting(QStringLiteral("volume"), Settings::values.volume, 1.0f);
+    if (global) {
+        WriteSetting(QStringLiteral("output_engine"),
+                     QString::fromStdString(Settings::values.sink_id), QStringLiteral("auto"));
+        WriteSetting(QStringLiteral("output_device"),
+                     QString::fromStdString(Settings::values.audio_device_id),
+                     QStringLiteral("auto"));
+    }
+    WriteSettingGlobal(QStringLiteral("enable_audio_stretching"),
+                       Settings::values.enable_audio_stretching, true);
+    WriteSettingGlobal(QStringLiteral("volume"), Settings::values.volume, 1.0f);
 
     qt_config->endGroup();
 }
@@ -948,6 +981,7 @@ void Config::SaveControlValues() {
     WriteSetting(QStringLiteral("udp_input_port"), Settings::values.udp_input_port,
                  InputCommon::CemuhookUDP::DEFAULT_PORT);
     WriteSetting(QStringLiteral("udp_pad_index"), Settings::values.udp_pad_index, 0);
+    WriteSetting(QStringLiteral("use_docked_mode"), Settings::values.use_docked_mode, false);
 
     qt_config->endGroup();
 }
@@ -955,7 +989,7 @@ void Config::SaveControlValues() {
 void Config::SaveCoreValues() {
     qt_config->beginGroup(QStringLiteral("Core"));
 
-    WriteSetting(QStringLiteral("use_multi_core"), Settings::values.use_multi_core, false);
+    WriteSettingGlobal(QStringLiteral("use_multi_core"), Settings::values.use_multi_core, false);
 
     qt_config->endGroup();
 }
@@ -984,18 +1018,7 @@ void Config::SaveDataStorageValues() {
                  false);
     WriteSetting(QStringLiteral("gamecard_path"),
                  QString::fromStdString(Settings::values.gamecard_path), QStringLiteral(""));
-    WriteSetting(QStringLiteral("nand_total_size"),
-                 QVariant::fromValue<u64>(static_cast<u64>(Settings::values.nand_total_size)),
-                 QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDTotalSize::S29_1GB)));
-    WriteSetting(QStringLiteral("nand_user_size"),
-                 QVariant::fromValue<u64>(static_cast<u64>(Settings::values.nand_user_size)),
-                 QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDUserSize::S26GB)));
-    WriteSetting(QStringLiteral("nand_system_size"),
-                 QVariant::fromValue<u64>(static_cast<u64>(Settings::values.nand_system_size)),
-                 QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDSystemSize::S2_5GB)));
-    WriteSetting(QStringLiteral("sdmc_size"),
-                 QVariant::fromValue<u64>(static_cast<u64>(Settings::values.sdmc_size)),
-                 QVariant::fromValue<u64>(static_cast<u64>(Settings::SDMCSize::S16GB)));
+
     qt_config->endGroup();
 }
 
@@ -1011,7 +1034,6 @@ void Config::SaveDebuggingValues() {
     WriteSetting(QStringLiteral("dump_exefs"), Settings::values.dump_exefs, false);
     WriteSetting(QStringLiteral("dump_nso"), Settings::values.dump_nso, false);
     WriteSetting(QStringLiteral("quest_flag"), Settings::values.quest_flag, false);
-    WriteSetting(QStringLiteral("disable_cpu_opt"), Settings::values.disable_cpu_opt, false);
     WriteSetting(QStringLiteral("disable_macro_jit"), Settings::values.disable_macro_jit, false);
 
     qt_config->endGroup();
@@ -1075,32 +1097,63 @@ void Config::SavePathValues() {
     qt_config->endGroup();
 }
 
+void Config::SaveCpuValues() {
+    qt_config->beginGroup(QStringLiteral("Cpu"));
+
+    if (global) {
+        WriteSetting(QStringLiteral("cpu_accuracy"),
+                     static_cast<int>(Settings::values.cpu_accuracy), 0);
+
+        WriteSetting(QStringLiteral("cpuopt_page_tables"), Settings::values.cpuopt_page_tables,
+                     true);
+        WriteSetting(QStringLiteral("cpuopt_block_linking"), Settings::values.cpuopt_block_linking,
+                     true);
+        WriteSetting(QStringLiteral("cpuopt_return_stack_buffer"),
+                     Settings::values.cpuopt_return_stack_buffer, true);
+        WriteSetting(QStringLiteral("cpuopt_fast_dispatcher"),
+                     Settings::values.cpuopt_fast_dispatcher, true);
+        WriteSetting(QStringLiteral("cpuopt_context_elimination"),
+                     Settings::values.cpuopt_context_elimination, true);
+        WriteSetting(QStringLiteral("cpuopt_const_prop"), Settings::values.cpuopt_const_prop, true);
+        WriteSetting(QStringLiteral("cpuopt_misc_ir"), Settings::values.cpuopt_misc_ir, true);
+        WriteSetting(QStringLiteral("cpuopt_reduce_misalign_checks"),
+                     Settings::values.cpuopt_reduce_misalign_checks, true);
+    }
+
+    qt_config->endGroup();
+}
+
 void Config::SaveRendererValues() {
     qt_config->beginGroup(QStringLiteral("Renderer"));
 
-    WriteSetting(QStringLiteral("backend"), static_cast<int>(Settings::values.renderer_backend), 0);
+    WriteSettingGlobal(QStringLiteral("backend"),
+                       static_cast<int>(Settings::values.renderer_backend.GetValue(global)),
+                       Settings::values.renderer_backend.UsingGlobal(), 0);
     WriteSetting(QStringLiteral("debug"), Settings::values.renderer_debug, false);
-    WriteSetting(QStringLiteral("vulkan_device"), Settings::values.vulkan_device, 0);
-    WriteSetting(QStringLiteral("aspect_ratio"), Settings::values.aspect_ratio, 0);
-    WriteSetting(QStringLiteral("max_anisotropy"), Settings::values.max_anisotropy, 0);
-    WriteSetting(QStringLiteral("use_frame_limit"), Settings::values.use_frame_limit, true);
-    WriteSetting(QStringLiteral("frame_limit"), Settings::values.frame_limit, 100);
-    WriteSetting(QStringLiteral("use_disk_shader_cache"), Settings::values.use_disk_shader_cache,
-                 true);
-    WriteSetting(QStringLiteral("gpu_accuracy"), static_cast<int>(Settings::values.gpu_accuracy),
-                 0);
-    WriteSetting(QStringLiteral("use_asynchronous_gpu_emulation"),
-                 Settings::values.use_asynchronous_gpu_emulation, false);
-    WriteSetting(QStringLiteral("use_vsync"), Settings::values.use_vsync, true);
-    WriteSetting(QStringLiteral("use_assembly_shaders"), Settings::values.use_assembly_shaders,
-                 false);
-    WriteSetting(QStringLiteral("use_fast_gpu_time"), Settings::values.use_fast_gpu_time, true);
-    WriteSetting(QStringLiteral("force_30fps_mode"), Settings::values.force_30fps_mode, false);
+    WriteSettingGlobal(QStringLiteral("vulkan_device"), Settings::values.vulkan_device, 0);
+    WriteSettingGlobal(QStringLiteral("aspect_ratio"), Settings::values.aspect_ratio, 0);
+    WriteSettingGlobal(QStringLiteral("max_anisotropy"), Settings::values.max_anisotropy, 0);
+    WriteSettingGlobal(QStringLiteral("use_frame_limit"), Settings::values.use_frame_limit, true);
+    WriteSettingGlobal(QStringLiteral("frame_limit"), Settings::values.frame_limit, 100);
+    WriteSettingGlobal(QStringLiteral("use_disk_shader_cache"),
+                       Settings::values.use_disk_shader_cache, true);
+    WriteSettingGlobal(QStringLiteral("gpu_accuracy"),
+                       static_cast<int>(Settings::values.gpu_accuracy.GetValue(global)),
+                       Settings::values.gpu_accuracy.UsingGlobal(), 0);
+    WriteSettingGlobal(QStringLiteral("use_asynchronous_gpu_emulation"),
+                       Settings::values.use_asynchronous_gpu_emulation, false);
+    WriteSettingGlobal(QStringLiteral("use_vsync"), Settings::values.use_vsync, true);
+    WriteSettingGlobal(QStringLiteral("use_assembly_shaders"),
+                       Settings::values.use_assembly_shaders, false);
+    WriteSettingGlobal(QStringLiteral("use_fast_gpu_time"), Settings::values.use_fast_gpu_time,
+                       true);
+    WriteSettingGlobal(QStringLiteral("force_30fps_mode"), Settings::values.force_30fps_mode,
+                       false);
 
     // Cast to double because Qt's written float values are not human-readable
-    WriteSetting(QStringLiteral("bg_red"), static_cast<double>(Settings::values.bg_red), 0.0);
-    WriteSetting(QStringLiteral("bg_green"), static_cast<double>(Settings::values.bg_green), 0.0);
-    WriteSetting(QStringLiteral("bg_blue"), static_cast<double>(Settings::values.bg_blue), 0.0);
+    WriteSettingGlobal(QStringLiteral("bg_red"), Settings::values.bg_red, 0.0);
+    WriteSettingGlobal(QStringLiteral("bg_green"), Settings::values.bg_green, 0.0);
+    WriteSettingGlobal(QStringLiteral("bg_blue"), Settings::values.bg_blue, 0.0);
 
     qt_config->endGroup();
 }
@@ -1128,23 +1181,28 @@ void Config::SaveShortcutValues() {
 void Config::SaveSystemValues() {
     qt_config->beginGroup(QStringLiteral("System"));
 
-    WriteSetting(QStringLiteral("use_docked_mode"), Settings::values.use_docked_mode, false);
     WriteSetting(QStringLiteral("current_user"), Settings::values.current_user, 0);
-    WriteSetting(QStringLiteral("language_index"), Settings::values.language_index, 1);
-    WriteSetting(QStringLiteral("region_index"), Settings::values.region_index, 1);
-    WriteSetting(QStringLiteral("time_zone_index"), Settings::values.time_zone_index, 0);
-
-    WriteSetting(QStringLiteral("rng_seed_enabled"), Settings::values.rng_seed.has_value(), false);
-    WriteSetting(QStringLiteral("rng_seed"), Settings::values.rng_seed.value_or(0), 0);
-
-    WriteSetting(QStringLiteral("custom_rtc_enabled"), Settings::values.custom_rtc.has_value(),
-                 false);
-    WriteSetting(QStringLiteral("custom_rtc"),
-                 QVariant::fromValue<long long>(
-                     Settings::values.custom_rtc.value_or(std::chrono::seconds{}).count()),
-                 0);
-
-    WriteSetting(QStringLiteral("sound_index"), Settings::values.sound_index, 1);
+    WriteSettingGlobal(QStringLiteral("language_index"), Settings::values.language_index, 1);
+    WriteSettingGlobal(QStringLiteral("region_index"), Settings::values.region_index, 1);
+    WriteSettingGlobal(QStringLiteral("time_zone_index"), Settings::values.time_zone_index, 0);
+
+    WriteSettingGlobal(QStringLiteral("rng_seed_enabled"),
+                       Settings::values.rng_seed.GetValue(global).has_value(),
+                       Settings::values.rng_seed.UsingGlobal(), false);
+    WriteSettingGlobal(QStringLiteral("rng_seed"),
+                       Settings::values.rng_seed.GetValue(global).value_or(0),
+                       Settings::values.rng_seed.UsingGlobal(), 0);
+
+    WriteSettingGlobal(QStringLiteral("custom_rtc_enabled"),
+                       Settings::values.custom_rtc.GetValue(global).has_value(),
+                       Settings::values.custom_rtc.UsingGlobal(), false);
+    WriteSettingGlobal(
+        QStringLiteral("custom_rtc"),
+        QVariant::fromValue<long long>(
+            Settings::values.custom_rtc.GetValue(global).value_or(std::chrono::seconds{}).count()),
+        Settings::values.custom_rtc.UsingGlobal(), 0);
+
+    WriteSettingGlobal(QStringLiteral("sound_index"), Settings::values.sound_index, 1);
 
     qt_config->endGroup();
 }
@@ -1236,6 +1294,34 @@ QVariant Config::ReadSetting(const QString& name, const QVariant& default_value)
     return result;
 }
 
+template <typename Type>
+void Config::ReadSettingGlobal(Settings::Setting<Type>& setting, const QString& name) {
+    const bool use_global = qt_config->value(name + QStringLiteral("/use_global"), true).toBool();
+    setting.SetGlobal(use_global);
+    if (global || !use_global) {
+        setting.SetValue(ReadSetting(name).value<Type>());
+    }
+}
+
+template <typename Type>
+void Config::ReadSettingGlobal(Settings::Setting<Type>& setting, const QString& name,
+                               const QVariant& default_value) {
+    const bool use_global = qt_config->value(name + QStringLiteral("/use_global"), true).toBool();
+    setting.SetGlobal(use_global);
+    if (global || !use_global) {
+        setting.SetValue(ReadSetting(name, default_value).value<Type>());
+    }
+}
+
+template <typename Type>
+void Config::ReadSettingGlobal(Type& setting, const QString& name,
+                               const QVariant& default_value) const {
+    const bool use_global = qt_config->value(name + QStringLiteral("/use_global"), true).toBool();
+    if (global || !use_global) {
+        setting = ReadSetting(name, default_value).value<Type>();
+    }
+}
+
 void Config::WriteSetting(const QString& name, const QVariant& value) {
     qt_config->setValue(name, value);
 }
@@ -1246,6 +1332,40 @@ void Config::WriteSetting(const QString& name, const QVariant& value,
     qt_config->setValue(name, value);
 }
 
+template <typename Type>
+void Config::WriteSettingGlobal(const QString& name, const Settings::Setting<Type>& setting) {
+    if (!global) {
+        qt_config->setValue(name + QStringLiteral("/use_global"), setting.UsingGlobal());
+    }
+    if (global || !setting.UsingGlobal()) {
+        qt_config->setValue(name, setting.GetValue(global));
+    }
+}
+
+template <typename Type>
+void Config::WriteSettingGlobal(const QString& name, const Settings::Setting<Type>& setting,
+                                const QVariant& default_value) {
+    if (!global) {
+        qt_config->setValue(name + QStringLiteral("/use_global"), setting.UsingGlobal());
+    }
+    if (global || !setting.UsingGlobal()) {
+        qt_config->setValue(name + QStringLiteral("/default"),
+                            setting.GetValue(global) == default_value.value<Type>());
+        qt_config->setValue(name, setting.GetValue(global));
+    }
+}
+
+void Config::WriteSettingGlobal(const QString& name, const QVariant& value, bool use_global,
+                                const QVariant& default_value) {
+    if (!global) {
+        qt_config->setValue(name + QStringLiteral("/use_global"), use_global);
+    }
+    if (global || !use_global) {
+        qt_config->setValue(name + QStringLiteral("/default"), value == default_value);
+        qt_config->setValue(name, value);
+    }
+}
+
 void Config::Reload() {
     ReadValues();
     // To apply default value changes

+ 28 - 1
src/yuzu/configuration/config.h

@@ -7,6 +7,7 @@
 #include <array>
 #include <memory>
 #include <string>
+#include <QMetaType>
 #include <QVariant>
 #include "core/settings.h"
 #include "yuzu/uisettings.h"
@@ -15,7 +16,7 @@ class QSettings;
 
 class Config {
 public:
-    Config();
+    explicit Config(const std::string& config_loc = "qt-config.ini", bool is_global = true);
     ~Config();
 
     void Reload();
@@ -48,6 +49,7 @@ private:
     void ReadDisabledAddOnValues();
     void ReadMiscellaneousValues();
     void ReadPathValues();
+    void ReadCpuValues();
     void ReadRendererValues();
     void ReadShortcutValues();
     void ReadSystemValues();
@@ -72,6 +74,7 @@ private:
     void SaveDisabledAddOnValues();
     void SaveMiscellaneousValues();
     void SavePathValues();
+    void SaveCpuValues();
     void SaveRendererValues();
     void SaveShortcutValues();
     void SaveSystemValues();
@@ -82,9 +85,33 @@ private:
 
     QVariant ReadSetting(const QString& name) const;
     QVariant ReadSetting(const QString& name, const QVariant& default_value) const;
+    // Templated ReadSettingGlobal functions will also look for the use_global setting and set
+    // both the value and the global state properly
+    template <typename Type>
+    void ReadSettingGlobal(Settings::Setting<Type>& setting, const QString& name);
+    template <typename Type>
+    void ReadSettingGlobal(Settings::Setting<Type>& setting, const QString& name,
+                           const QVariant& default_value);
+    template <typename Type>
+    void ReadSettingGlobal(Type& setting, const QString& name, const QVariant& default_value) const;
+    // Templated WriteSettingGlobal functions will also write the global state if needed and will
+    // skip writing the actual setting if it defers to the global value
     void WriteSetting(const QString& name, const QVariant& value);
     void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value);
+    template <typename Type>
+    void WriteSettingGlobal(const QString& name, const Settings::Setting<Type>& setting);
+    template <typename Type>
+    void WriteSettingGlobal(const QString& name, const Settings::Setting<Type>& setting,
+                            const QVariant& default_value);
+    void WriteSettingGlobal(const QString& name, const QVariant& value, bool use_global,
+                            const QVariant& default_value);
 
     std::unique_ptr<QSettings> qt_config;
     std::string qt_config_loc;
+
+    bool global;
 };
+
+// These metatype declarations cannot be in core/settings.h because core is devoid of QT
+Q_DECLARE_METATYPE(Settings::RendererBackend);
+Q_DECLARE_METATYPE(Settings::GPUAccuracy);

+ 76 - 0
src/yuzu/configuration/configuration_shared.cpp

@@ -0,0 +1,76 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <QCheckBox>
+#include <QComboBox>
+#include "core/settings.h"
+#include "yuzu/configuration/configuration_shared.h"
+#include "yuzu/configuration/configure_per_game.h"
+
+void ConfigurationShared::ApplyPerGameSetting(Settings::Setting<bool>* setting,
+                                              const QCheckBox* checkbox) {
+    if (checkbox->checkState() == Qt::PartiallyChecked) {
+        setting->SetGlobal(true);
+    } else {
+        setting->SetGlobal(false);
+        setting->SetValue(checkbox->checkState() == Qt::Checked);
+    }
+}
+
+void ConfigurationShared::ApplyPerGameSetting(Settings::Setting<int>* setting,
+                                              const QComboBox* combobox) {
+    if (combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
+        setting->SetGlobal(true);
+    } else {
+        setting->SetGlobal(false);
+        setting->SetValue(combobox->currentIndex() - ConfigurationShared::USE_GLOBAL_OFFSET);
+    }
+}
+
+void ConfigurationShared::ApplyPerGameSetting(Settings::Setting<Settings::RendererBackend>* setting,
+                                              const QComboBox* combobox) {
+    if (combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
+        setting->SetGlobal(true);
+    } else {
+        setting->SetGlobal(false);
+        setting->SetValue(static_cast<Settings::RendererBackend>(
+            combobox->currentIndex() - ConfigurationShared::USE_GLOBAL_OFFSET));
+    }
+}
+
+void ConfigurationShared::SetPerGameSetting(QCheckBox* checkbox,
+                                            const Settings::Setting<bool>* setting) {
+    if (setting->UsingGlobal()) {
+        checkbox->setCheckState(Qt::PartiallyChecked);
+    } else {
+        checkbox->setCheckState(setting->GetValue() ? Qt::Checked : Qt::Unchecked);
+    }
+}
+
+void ConfigurationShared::SetPerGameSetting(QComboBox* combobox,
+                                            const Settings::Setting<int>* setting) {
+    combobox->setCurrentIndex(setting->UsingGlobal()
+                                  ? ConfigurationShared::USE_GLOBAL_INDEX
+                                  : setting->GetValue() + ConfigurationShared::USE_GLOBAL_OFFSET);
+}
+
+void ConfigurationShared::SetPerGameSetting(
+    QComboBox* combobox, const Settings::Setting<Settings::RendererBackend>* setting) {
+    combobox->setCurrentIndex(setting->UsingGlobal() ? ConfigurationShared::USE_GLOBAL_INDEX
+                                                     : static_cast<int>(setting->GetValue()) +
+                                                           ConfigurationShared::USE_GLOBAL_OFFSET);
+}
+
+void ConfigurationShared::SetPerGameSetting(
+    QComboBox* combobox, const Settings::Setting<Settings::GPUAccuracy>* setting) {
+    combobox->setCurrentIndex(setting->UsingGlobal() ? ConfigurationShared::USE_GLOBAL_INDEX
+                                                     : static_cast<int>(setting->GetValue()) +
+                                                           ConfigurationShared::USE_GLOBAL_OFFSET);
+}
+
+void ConfigurationShared::InsertGlobalItem(QComboBox* combobox) {
+    const QString use_global_text = ConfigurePerGame::tr("Use global configuration");
+    combobox->insertItem(ConfigurationShared::USE_GLOBAL_INDEX, use_global_text);
+    combobox->insertSeparator(ConfigurationShared::USE_GLOBAL_SEPARATOR_INDEX);
+}

+ 36 - 0
src/yuzu/configuration/configuration_shared.h

@@ -0,0 +1,36 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <QCheckBox>
+#include <QComboBox>
+#include <QString>
+#include "core/settings.h"
+
+namespace ConfigurationShared {
+
+constexpr int USE_GLOBAL_INDEX = 0;
+constexpr int USE_GLOBAL_SEPARATOR_INDEX = 1;
+constexpr int USE_GLOBAL_OFFSET = 2;
+
+// Global-aware apply and set functions
+
+void ApplyPerGameSetting(Settings::Setting<bool>* setting, const QCheckBox* checkbox);
+void ApplyPerGameSetting(Settings::Setting<int>* setting, const QComboBox* combobox);
+void ApplyPerGameSetting(Settings::Setting<Settings::RendererBackend>* setting,
+                         const QComboBox* combobox);
+void ApplyPerGameSetting(Settings::Setting<Settings::GPUAccuracy>* setting,
+                         const QComboBox* combobox);
+
+void SetPerGameSetting(QCheckBox* checkbox, const Settings::Setting<bool>* setting);
+void SetPerGameSetting(QComboBox* combobox, const Settings::Setting<int>* setting);
+void SetPerGameSetting(QComboBox* combobox,
+                       const Settings::Setting<Settings::RendererBackend>* setting);
+void SetPerGameSetting(QComboBox* combobox,
+                       const Settings::Setting<Settings::GPUAccuracy>* setting);
+
+void InsertGlobalItem(QComboBox* combobox);
+
+} // namespace ConfigurationShared

+ 22 - 0
src/yuzu/configuration/configure.ui

@@ -78,6 +78,16 @@
          <string>Hotkeys</string>
         </attribute>
        </widget>
+       <widget class="ConfigureCpu" name="cpuTab">
+        <attribute name="title">
+         <string>CPU</string>
+        </attribute>
+       </widget>
+       <widget class="ConfigureCpuDebug" name="cpuDebugTab">
+        <attribute name="title">
+         <string>Debug</string>
+        </attribute>
+       </widget>
        <widget class="ConfigureGraphics" name="graphicsTab">
         <attribute name="title">
          <string>Graphics</string>
@@ -158,6 +168,18 @@
    <header>configuration/configure_debug.h</header>
    <container>1</container>
   </customwidget>
+  <customwidget>
+   <class>ConfigureCpu</class>
+   <extends>QWidget</extends>
+   <header>configuration/configure_cpu.h</header>
+   <container>1</container>
+  </customwidget>
+  <customwidget>
+   <class>ConfigureCpuDebug</class>
+   <extends>QWidget</extends>
+   <header>configuration/configure_cpu_debug.h</header>
+   <container>1</container>
+  </customwidget>
   <customwidget>
    <class>ConfigureGraphics</class>
    <extends>QWidget</extends>

+ 71 - 11
src/yuzu/configuration/configure_audio.cpp

@@ -11,6 +11,7 @@
 #include "core/core.h"
 #include "core/settings.h"
 #include "ui_configure_audio.h"
+#include "yuzu/configuration/configuration_shared.h"
 #include "yuzu/configuration/configure_audio.h"
 
 ConfigureAudio::ConfigureAudio(QWidget* parent)
@@ -24,6 +25,11 @@ ConfigureAudio::ConfigureAudio(QWidget* parent)
     connect(ui->output_sink_combo_box, qOverload<int>(&QComboBox::currentIndexChanged), this,
             &ConfigureAudio::UpdateAudioDevices);
 
+    ui->volume_label->setVisible(Settings::configuring_global);
+    ui->volume_combo_box->setVisible(!Settings::configuring_global);
+
+    SetupPerGameUI();
+
     SetConfiguration();
 
     const bool is_powered_on = Core::System::GetInstance().IsPoweredOn();
@@ -41,8 +47,22 @@ void ConfigureAudio::SetConfiguration() {
 
     SetAudioDeviceFromDeviceID();
 
-    ui->toggle_audio_stretching->setChecked(Settings::values.enable_audio_stretching);
-    ui->volume_slider->setValue(Settings::values.volume * ui->volume_slider->maximum());
+    ui->volume_slider->setValue(Settings::values.volume.GetValue() * ui->volume_slider->maximum());
+
+    if (Settings::configuring_global) {
+        ui->toggle_audio_stretching->setChecked(
+            Settings::values.enable_audio_stretching.GetValue());
+    } else {
+        ConfigurationShared::SetPerGameSetting(ui->toggle_audio_stretching,
+                                               &Settings::values.enable_audio_stretching);
+        if (Settings::values.volume.UsingGlobal()) {
+            ui->volume_combo_box->setCurrentIndex(0);
+            ui->volume_slider->setEnabled(false);
+        } else {
+            ui->volume_combo_box->setCurrentIndex(1);
+            ui->volume_slider->setEnabled(true);
+        }
+    }
     SetVolumeIndicatorText(ui->volume_slider->sliderPosition());
 }
 
@@ -80,15 +100,36 @@ void ConfigureAudio::SetVolumeIndicatorText(int percentage) {
 }
 
 void ConfigureAudio::ApplyConfiguration() {
-    Settings::values.sink_id =
-        ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex())
-            .toStdString();
-    Settings::values.enable_audio_stretching = ui->toggle_audio_stretching->isChecked();
-    Settings::values.audio_device_id =
-        ui->audio_device_combo_box->itemText(ui->audio_device_combo_box->currentIndex())
-            .toStdString();
-    Settings::values.volume =
-        static_cast<float>(ui->volume_slider->sliderPosition()) / ui->volume_slider->maximum();
+    if (Settings::configuring_global) {
+        Settings::values.sink_id =
+            ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex())
+                .toStdString();
+        Settings::values.audio_device_id =
+            ui->audio_device_combo_box->itemText(ui->audio_device_combo_box->currentIndex())
+                .toStdString();
+
+        // Guard if during game and set to game-specific value
+        if (Settings::values.enable_audio_stretching.UsingGlobal()) {
+            Settings::values.enable_audio_stretching.SetValue(
+                ui->toggle_audio_stretching->isChecked());
+        }
+        if (Settings::values.volume.UsingGlobal()) {
+            Settings::values.volume.SetValue(
+                static_cast<float>(ui->volume_slider->sliderPosition()) /
+                ui->volume_slider->maximum());
+        }
+    } else {
+        ConfigurationShared::ApplyPerGameSetting(&Settings::values.enable_audio_stretching,
+                                                 ui->toggle_audio_stretching);
+        if (ui->volume_combo_box->currentIndex() == 0) {
+            Settings::values.volume.SetGlobal(true);
+        } else {
+            Settings::values.volume.SetGlobal(false);
+            Settings::values.volume.SetValue(
+                static_cast<float>(ui->volume_slider->sliderPosition()) /
+                ui->volume_slider->maximum());
+        }
+    }
 }
 
 void ConfigureAudio::changeEvent(QEvent* event) {
@@ -122,3 +163,22 @@ void ConfigureAudio::RetranslateUI() {
     ui->retranslateUi(this);
     SetVolumeIndicatorText(ui->volume_slider->sliderPosition());
 }
+
+void ConfigureAudio::SetupPerGameUI() {
+    if (Settings::configuring_global) {
+        ui->volume_slider->setEnabled(Settings::values.volume.UsingGlobal());
+        ui->toggle_audio_stretching->setEnabled(
+            Settings::values.enable_audio_stretching.UsingGlobal());
+
+        return;
+    }
+
+    ui->toggle_audio_stretching->setTristate(true);
+    connect(ui->volume_combo_box, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated),
+            this, [this](int index) { ui->volume_slider->setEnabled(index == 1); });
+
+    ui->output_sink_combo_box->setVisible(false);
+    ui->output_sink_label->setVisible(false);
+    ui->audio_device_combo_box->setVisible(false);
+    ui->audio_device_label->setVisible(false);
+}

+ 2 - 0
src/yuzu/configuration/configure_audio.h

@@ -34,5 +34,7 @@ private:
     void SetAudioDeviceFromDeviceID();
     void SetVolumeIndicatorText(int percentage);
 
+    void SetupPerGameUI();
+
     std::unique_ptr<Ui::ConfigureAudio> ui;
 };

+ 32 - 18
src/yuzu/configuration/configure_audio.ui

@@ -6,8 +6,8 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>188</width>
-    <height>246</height>
+    <width>367</width>
+    <height>368</height>
    </rect>
   </property>
   <layout class="QVBoxLayout">
@@ -18,9 +18,9 @@
      </property>
      <layout class="QVBoxLayout">
       <item>
-       <layout class="QHBoxLayout">
+       <layout class="QHBoxLayout" name="_3">
         <item>
-         <widget class="QLabel" name="label_1">
+         <widget class="QLabel" name="output_sink_label">
           <property name="text">
            <string>Output Engine:</string>
           </property>
@@ -31,20 +31,20 @@
         </item>
        </layout>
       </item>
-       <item>
-         <widget class="QCheckBox" name="toggle_audio_stretching">
-           <property name="toolTip">
-             <string>This post-processing effect adjusts audio speed to match emulation speed and helps prevent audio stutter. This however increases audio latency.</string>
-           </property>
-           <property name="text">
-             <string>Enable audio stretching</string>
-           </property>
-         </widget>
-       </item>
       <item>
-       <layout class="QHBoxLayout">
+       <widget class="QCheckBox" name="toggle_audio_stretching">
+        <property name="toolTip">
+         <string>This post-processing effect adjusts audio speed to match emulation speed and helps prevent audio stutter. This however increases audio latency.</string>
+        </property>
+        <property name="text">
+         <string>Enable audio stretching</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <layout class="QHBoxLayout" name="_2">
         <item>
-         <widget class="QLabel" name="label_2">
+         <widget class="QLabel" name="audio_device_label">
           <property name="text">
            <string>Audio Device:</string>
           </property>
@@ -61,7 +61,21 @@
          <number>0</number>
         </property>
         <item>
-         <widget class="QLabel" name="label_3">
+         <widget class="QComboBox" name="volume_combo_box">
+          <item>
+           <property name="text">
+            <string>Use global volume</string>
+           </property>
+          </item>
+          <item>
+           <property name="text">
+            <string>Set volume:</string>
+           </property>
+          </item>
+         </widget>
+        </item>
+        <item>
+         <widget class="QLabel" name="volume_label">
           <property name="text">
            <string>Volume:</string>
           </property>
@@ -74,7 +88,7 @@
           </property>
           <property name="sizeHint" stdset="0">
            <size>
-            <width>40</width>
+            <width>30</width>
             <height>20</height>
            </size>
           </property>

+ 61 - 0
src/yuzu/configuration/configure_cpu.cpp

@@ -0,0 +1,61 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <QComboBox>
+#include <QMessageBox>
+
+#include "common/common_types.h"
+#include "common/logging/log.h"
+#include "core/core.h"
+#include "core/settings.h"
+#include "ui_configure_cpu.h"
+#include "yuzu/configuration/configure_cpu.h"
+
+ConfigureCpu::ConfigureCpu(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureCpu) {
+    ui->setupUi(this);
+
+    SetConfiguration();
+
+    connect(ui->accuracy, qOverload<int>(&QComboBox::activated), this,
+            &ConfigureCpu::AccuracyUpdated);
+}
+
+ConfigureCpu::~ConfigureCpu() = default;
+
+void ConfigureCpu::SetConfiguration() {
+    const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn();
+
+    ui->accuracy->setEnabled(runtime_lock);
+    ui->accuracy->setCurrentIndex(static_cast<int>(Settings::values.cpu_accuracy));
+}
+
+void ConfigureCpu::AccuracyUpdated(int index) {
+    if (static_cast<Settings::CPUAccuracy>(index) == Settings::CPUAccuracy::DebugMode) {
+        const auto result = QMessageBox::warning(this, tr("Setting CPU to Debug Mode"),
+                                                 tr("CPU Debug Mode is only intended for developer "
+                                                    "use. Are you sure you want to enable this?"),
+                                                 QMessageBox::Yes | QMessageBox::No);
+        if (result == QMessageBox::No) {
+            ui->accuracy->setCurrentIndex(static_cast<int>(Settings::CPUAccuracy::Accurate));
+            return;
+        }
+    }
+}
+
+void ConfigureCpu::ApplyConfiguration() {
+    Settings::values.cpu_accuracy =
+        static_cast<Settings::CPUAccuracy>(ui->accuracy->currentIndex());
+}
+
+void ConfigureCpu::changeEvent(QEvent* event) {
+    if (event->type() == QEvent::LanguageChange) {
+        RetranslateUI();
+    }
+
+    QWidget::changeEvent(event);
+}
+
+void ConfigureCpu::RetranslateUI() {
+    ui->retranslateUi(this);
+}

+ 33 - 0
src/yuzu/configuration/configure_cpu.h

@@ -0,0 +1,33 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include <QWidget>
+#include "core/settings.h"
+
+namespace Ui {
+class ConfigureCpu;
+}
+
+class ConfigureCpu : public QWidget {
+    Q_OBJECT
+
+public:
+    explicit ConfigureCpu(QWidget* parent = nullptr);
+    ~ConfigureCpu() override;
+
+    void ApplyConfiguration();
+
+private:
+    void changeEvent(QEvent* event) override;
+    void RetranslateUI();
+
+    void AccuracyUpdated(int index);
+
+    void SetConfiguration();
+
+    std::unique_ptr<Ui::ConfigureCpu> ui;
+};

+ 92 - 0
src/yuzu/configuration/configure_cpu.ui

@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ConfigureCpu</class>
+ <widget class="QWidget" name="ConfigureCpu">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>400</width>
+    <height>321</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QVBoxLayout">
+   <item>
+    <layout class="QVBoxLayout">
+     <item>
+      <widget class="QGroupBox">
+       <property name="title">
+        <string>General</string>
+       </property>
+       <layout class="QVBoxLayout">
+        <item>
+         <layout class="QHBoxLayout">
+          <item>
+           <widget class="QLabel">
+            <property name="text">
+             <string>Accuracy:</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QComboBox" name="accuracy">
+            <item>
+             <property name="text">
+              <string>Accurate</string>
+             </property>
+            </item>
+            <item>
+             <property name="text">
+              <string>Enable Debug Mode</string>
+             </property>
+            </item>
+           </widget>
+          </item>
+         </layout>
+        </item>
+        <item>
+         <widget class="QLabel">
+          <property name="wordWrap">
+            <bool>1</bool>
+          </property>
+          <property name="text">
+           <string>We recommend setting accuracy to "Accurate".</string>
+          </property>
+         </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>
+   <item>
+    <widget class="QLabel" name="label_disable_info">
+     <property name="text">
+      <string>CPU settings are available only when game is not running.</string>
+     </property>
+     <property name="wordWrap">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 65 - 0
src/yuzu/configuration/configure_cpu_debug.cpp

@@ -0,0 +1,65 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <QComboBox>
+
+#include "common/common_types.h"
+#include "common/logging/log.h"
+#include "core/core.h"
+#include "core/settings.h"
+#include "ui_configure_cpu_debug.h"
+#include "yuzu/configuration/configure_cpu_debug.h"
+
+ConfigureCpuDebug::ConfigureCpuDebug(QWidget* parent)
+    : QWidget(parent), ui(new Ui::ConfigureCpuDebug) {
+    ui->setupUi(this);
+
+    SetConfiguration();
+}
+
+ConfigureCpuDebug::~ConfigureCpuDebug() = default;
+
+void ConfigureCpuDebug::SetConfiguration() {
+    const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn();
+
+    ui->cpuopt_page_tables->setEnabled(runtime_lock);
+    ui->cpuopt_page_tables->setChecked(Settings::values.cpuopt_page_tables);
+    ui->cpuopt_block_linking->setEnabled(runtime_lock);
+    ui->cpuopt_block_linking->setChecked(Settings::values.cpuopt_block_linking);
+    ui->cpuopt_return_stack_buffer->setEnabled(runtime_lock);
+    ui->cpuopt_return_stack_buffer->setChecked(Settings::values.cpuopt_return_stack_buffer);
+    ui->cpuopt_fast_dispatcher->setEnabled(runtime_lock);
+    ui->cpuopt_fast_dispatcher->setChecked(Settings::values.cpuopt_fast_dispatcher);
+    ui->cpuopt_context_elimination->setEnabled(runtime_lock);
+    ui->cpuopt_context_elimination->setChecked(Settings::values.cpuopt_context_elimination);
+    ui->cpuopt_const_prop->setEnabled(runtime_lock);
+    ui->cpuopt_const_prop->setChecked(Settings::values.cpuopt_const_prop);
+    ui->cpuopt_misc_ir->setEnabled(runtime_lock);
+    ui->cpuopt_misc_ir->setChecked(Settings::values.cpuopt_misc_ir);
+    ui->cpuopt_reduce_misalign_checks->setEnabled(runtime_lock);
+    ui->cpuopt_reduce_misalign_checks->setChecked(Settings::values.cpuopt_reduce_misalign_checks);
+}
+
+void ConfigureCpuDebug::ApplyConfiguration() {
+    Settings::values.cpuopt_page_tables = ui->cpuopt_page_tables->isChecked();
+    Settings::values.cpuopt_block_linking = ui->cpuopt_block_linking->isChecked();
+    Settings::values.cpuopt_return_stack_buffer = ui->cpuopt_return_stack_buffer->isChecked();
+    Settings::values.cpuopt_fast_dispatcher = ui->cpuopt_fast_dispatcher->isChecked();
+    Settings::values.cpuopt_context_elimination = ui->cpuopt_context_elimination->isChecked();
+    Settings::values.cpuopt_const_prop = ui->cpuopt_const_prop->isChecked();
+    Settings::values.cpuopt_misc_ir = ui->cpuopt_misc_ir->isChecked();
+    Settings::values.cpuopt_reduce_misalign_checks = ui->cpuopt_reduce_misalign_checks->isChecked();
+}
+
+void ConfigureCpuDebug::changeEvent(QEvent* event) {
+    if (event->type() == QEvent::LanguageChange) {
+        RetranslateUI();
+    }
+
+    QWidget::changeEvent(event);
+}
+
+void ConfigureCpuDebug::RetranslateUI() {
+    ui->retranslateUi(this);
+}

+ 31 - 0
src/yuzu/configuration/configure_cpu_debug.h

@@ -0,0 +1,31 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include <QWidget>
+#include "core/settings.h"
+
+namespace Ui {
+class ConfigureCpuDebug;
+}
+
+class ConfigureCpuDebug : public QWidget {
+    Q_OBJECT
+
+public:
+    explicit ConfigureCpuDebug(QWidget* parent = nullptr);
+    ~ConfigureCpuDebug() override;
+
+    void ApplyConfiguration();
+
+private:
+    void changeEvent(QEvent* event) override;
+    void RetranslateUI();
+
+    void SetConfiguration();
+
+    std::unique_ptr<Ui::ConfigureCpuDebug> ui;
+};

+ 174 - 0
src/yuzu/configuration/configure_cpu_debug.ui

@@ -0,0 +1,174 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ConfigureCpuDebug</class>
+ <widget class="QWidget" name="ConfigureCpuDebug">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>400</width>
+    <height>321</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QVBoxLayout">
+   <item>
+    <layout class="QVBoxLayout">
+     <item>
+      <widget class="QGroupBox">
+       <property name="title">
+        <string>Toggle CPU Optimizations</string>
+       </property>
+       <layout class="QVBoxLayout">
+        <item>
+         <widget class="QLabel">
+          <property name="wordWrap">
+            <bool>1</bool>
+          </property>
+          <property name="text">
+           <string>
+            &lt;div&gt;
+            &lt;b&gt;For debugging only.&lt;/b&gt;
+            &lt;br&gt;
+            If you're not sure what these do, keep all of these enabled.
+            &lt;br&gt;
+            These settings only take effect when CPU Accuracy is "Debug Mode".
+            &lt;/div&gt;
+           </string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QCheckBox" name="cpuopt_page_tables">
+          <property name="text">
+           <string>Enable inline page tables</string>
+          </property>
+          <property name="toolTip">
+           <string>
+            &lt;div style="white-space: nowrap"&gt;This optimization speeds up memory accesses by the guest program.&lt;/div&gt;
+            &lt;div style="white-space: nowrap"&gt;Enabling it inlines accesses to PageTable::pointers into emitted code.&lt;/div&gt;
+            &lt;div style="white-space: nowrap"&gt;Disabling this forces all memory accesses to go through the Memory::Read/Memory::Write functions.&lt;/div&gt;
+           </string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QCheckBox" name="cpuopt_block_linking">
+          <property name="text">
+           <string>Enable block linking</string>
+          </property>
+          <property name="toolTip">
+           <string>
+            &lt;div&gt;This optimization avoids dispatcher lookups by allowing emitted basic blocks to jump directly to other basic blocks if the destination PC is static.&lt;/div&gt;
+           </string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QCheckBox" name="cpuopt_return_stack_buffer">
+          <property name="text">
+           <string>Enable return stack buffer</string>
+          </property>
+          <property name="toolTip">
+           <string>
+            &lt;div&gt;This optimization avoids dispatcher lookups by keeping track potential return addresses of BL instructions. This approximates what happens with a return stack buffer on a real CPU.&lt;/div&gt;
+           </string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QCheckBox" name="cpuopt_fast_dispatcher">
+          <property name="text">
+           <string>Enable fast dispatcher</string>
+          </property>
+          <property name="toolTip">
+           <string>
+            &lt;div&gt;Enable a two-tiered dispatch system. A faster dispatcher written in assembly has a small MRU cache of jump destinations is used first. If that fails, dispatch falls back to the slower C++ dispatcher.&lt;/div&gt;
+           </string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QCheckBox" name="cpuopt_context_elimination">
+          <property name="text">
+           <string>Enable context elimination</string>
+          </property>
+          <property name="toolTip">
+           <string>
+            &lt;div&gt;Enables an IR optimization that reduces unnecessary accesses to the CPU context structure.&lt;/div&gt;
+           </string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QCheckBox" name="cpuopt_const_prop">
+          <property name="text">
+           <string>Enable constant propagation</string>
+          </property>
+          <property name="toolTip">
+           <string>
+            &lt;div&gt;Enables IR optimizations that involve constant propagation.&lt;/div&gt;
+           </string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QCheckBox" name="cpuopt_misc_ir">
+          <property name="text">
+           <string>Enable miscellaneous optimizations</string>
+          </property>
+          <property name="toolTip">
+           <string>
+            &lt;div&gt;Enables miscellaneous IR optimizations.&lt;/div&gt;
+           </string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QCheckBox" name="cpuopt_reduce_misalign_checks">
+          <property name="text">
+           <string>Enable misalignment check reduction</string>
+          </property>
+          <property name="toolTip">
+           <string>
+            &lt;div style="white-space: nowrap"&gt;When enabled, a misalignment is only triggered when an access crosses a page boundary.&lt;/div&gt;
+            &lt;div style="white-space: nowrap"&gt;When disabled, a misalignment is triggered on all misaligned accesses.&lt;/div&gt;
+           </string>
+          </property>
+         </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>
+   <item>
+    <widget class="QLabel" name="label_disable_info">
+     <property name="text">
+      <string>CPU settings are available only when game is not running.</string>
+     </property>
+     <property name="wordWrap">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 0 - 2
src/yuzu/configuration/configure_debug.cpp

@@ -36,7 +36,6 @@ void ConfigureDebug::SetConfiguration() {
     ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args));
     ui->reporting_services->setChecked(Settings::values.reporting_services);
     ui->quest_flag->setChecked(Settings::values.quest_flag);
-    ui->disable_cpu_opt->setChecked(Settings::values.disable_cpu_opt);
     ui->enable_graphics_debugging->setEnabled(!Core::System::GetInstance().IsPoweredOn());
     ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug);
     ui->disable_macro_jit->setEnabled(!Core::System::GetInstance().IsPoweredOn());
@@ -51,7 +50,6 @@ void ConfigureDebug::ApplyConfiguration() {
     Settings::values.program_args = ui->homebrew_args_edit->text().toStdString();
     Settings::values.reporting_services = ui->reporting_services->isChecked();
     Settings::values.quest_flag = ui->quest_flag->isChecked();
-    Settings::values.disable_cpu_opt = ui->disable_cpu_opt->isChecked();
     Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked();
     Settings::values.disable_macro_jit = ui->disable_macro_jit->isChecked();
     Debugger::ToggleConsole();

+ 0 - 7
src/yuzu/configuration/configure_debug.ui

@@ -228,13 +228,6 @@
         </property>
        </widget>
       </item>
-      <item>
-       <widget class="QCheckBox" name="disable_cpu_opt">
-        <property name="text">
-         <string>Disable CPU JIT optimizations</string>
-        </property>
-       </widget>
-      </item>
      </layout>
     </widget>
    </item>

+ 8 - 1
src/yuzu/configuration/configure_dialog.cpp

@@ -14,6 +14,8 @@
 
 ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry)
     : QDialog(parent), ui(new Ui::ConfigureDialog), registry(registry) {
+    Settings::configuring_global = true;
+
     ui->setupUi(this);
     ui->hotkeysTab->Populate(registry);
     setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
@@ -40,6 +42,8 @@ void ConfigureDialog::ApplyConfiguration() {
     ui->filesystemTab->applyConfiguration();
     ui->inputTab->ApplyConfiguration();
     ui->hotkeysTab->ApplyConfiguration(registry);
+    ui->cpuTab->ApplyConfiguration();
+    ui->cpuDebugTab->ApplyConfiguration();
     ui->graphicsTab->ApplyConfiguration();
     ui->graphicsAdvancedTab->ApplyConfiguration();
     ui->audioTab->ApplyConfiguration();
@@ -74,9 +78,10 @@ void ConfigureDialog::RetranslateUI() {
 Q_DECLARE_METATYPE(QList<QWidget*>);
 
 void ConfigureDialog::PopulateSelectionList() {
-    const std::array<std::pair<QString, QList<QWidget*>>, 5> items{
+    const std::array<std::pair<QString, QList<QWidget*>>, 6> items{
         {{tr("General"), {ui->generalTab, ui->webTab, ui->debugTab, ui->uiTab}},
          {tr("System"), {ui->systemTab, ui->profileManagerTab, ui->serviceTab, ui->filesystemTab}},
+         {tr("CPU"), {ui->cpuTab, ui->cpuDebugTab}},
          {tr("Graphics"), {ui->graphicsTab, ui->graphicsAdvancedTab}},
          {tr("Audio"), {ui->audioTab}},
          {tr("Controls"), {ui->inputTab, ui->hotkeysTab}}},
@@ -105,6 +110,8 @@ void ConfigureDialog::UpdateVisibleTabs() {
         {ui->profileManagerTab, tr("Profiles")},
         {ui->inputTab, tr("Input")},
         {ui->hotkeysTab, tr("Hotkeys")},
+        {ui->cpuTab, tr("CPU")},
+        {ui->cpuDebugTab, tr("Debug")},
         {ui->graphicsTab, tr("Graphics")},
         {ui->graphicsAdvancedTab, tr("Advanced")},
         {ui->audioTab, tr("Audio")},

+ 0 - 27
src/yuzu/configuration/configure_filesystem.cpp

@@ -11,19 +11,6 @@
 #include "yuzu/configuration/configure_filesystem.h"
 #include "yuzu/uisettings.h"
 
-namespace {
-
-template <typename T>
-void SetComboBoxFromData(QComboBox* combo_box, T data) {
-    const auto index = combo_box->findData(QVariant::fromValue(static_cast<u64>(data)));
-    if (index >= combo_box->count() || index < 0)
-        return;
-
-    combo_box->setCurrentIndex(index);
-}
-
-} // Anonymous namespace
-
 ConfigureFilesystem::ConfigureFilesystem(QWidget* parent)
     : QWidget(parent), ui(std::make_unique<Ui::ConfigureFilesystem>()) {
     ui->setupUi(this);
@@ -73,11 +60,6 @@ void ConfigureFilesystem::setConfiguration() {
 
     ui->cache_game_list->setChecked(UISettings::values.cache_game_list);
 
-    SetComboBoxFromData(ui->nand_size, Settings::values.nand_total_size);
-    SetComboBoxFromData(ui->usrnand_size, Settings::values.nand_user_size);
-    SetComboBoxFromData(ui->sysnand_size, Settings::values.nand_system_size);
-    SetComboBoxFromData(ui->sdmc_size, Settings::values.sdmc_size);
-
     UpdateEnabledControls();
 }
 
@@ -98,15 +80,6 @@ void ConfigureFilesystem::applyConfiguration() {
     Settings::values.dump_nso = ui->dump_nso->isChecked();
 
     UISettings::values.cache_game_list = ui->cache_game_list->isChecked();
-
-    Settings::values.nand_total_size = static_cast<Settings::NANDTotalSize>(
-        ui->nand_size->itemData(ui->nand_size->currentIndex()).toULongLong());
-    Settings::values.nand_system_size = static_cast<Settings::NANDSystemSize>(
-        ui->nand_size->itemData(ui->sysnand_size->currentIndex()).toULongLong());
-    Settings::values.nand_user_size = static_cast<Settings::NANDUserSize>(
-        ui->nand_size->itemData(ui->usrnand_size->currentIndex()).toULongLong());
-    Settings::values.sdmc_size = static_cast<Settings::SDMCSize>(
-        ui->nand_size->itemData(ui->sdmc_size->currentIndex()).toULongLong());
 }
 
 void ConfigureFilesystem::SetDirectory(DirectoryTarget target, QLineEdit* edit) {

+ 0 - 121
src/yuzu/configuration/configure_filesystem.ui

@@ -115,127 +115,6 @@
        </layout>
       </widget>
      </item>
-     <item>
-      <widget class="QGroupBox" name="groupBox_3">
-       <property name="title">
-        <string>Storage Sizes</string>
-       </property>
-       <layout class="QGridLayout" name="gridLayout_3">
-        <item row="3" column="0">
-         <widget class="QLabel" name="label_5">
-          <property name="text">
-           <string>SD Card</string>
-          </property>
-         </widget>
-        </item>
-        <item row="1" column="0">
-         <widget class="QLabel" name="label_4">
-          <property name="text">
-           <string>System NAND</string>
-          </property>
-         </widget>
-        </item>
-        <item row="1" column="1">
-         <widget class="QComboBox" name="sysnand_size">
-          <item>
-           <property name="text">
-            <string>2.5 GB</string>
-           </property>
-          </item>
-         </widget>
-        </item>
-        <item row="3" column="1">
-         <widget class="QComboBox" name="sdmc_size">
-          <property name="currentText">
-           <string>32 GB</string>
-          </property>
-          <item>
-           <property name="text">
-            <string>1 GB</string>
-           </property>
-          </item>
-          <item>
-           <property name="text">
-            <string>2 GB</string>
-           </property>
-          </item>
-          <item>
-           <property name="text">
-            <string>4 GB</string>
-           </property>
-          </item>
-          <item>
-           <property name="text">
-            <string>8 GB</string>
-           </property>
-          </item>
-          <item>
-           <property name="text">
-            <string>16 GB</string>
-           </property>
-          </item>
-          <item>
-           <property name="text">
-            <string>32 GB</string>
-           </property>
-          </item>
-          <item>
-           <property name="text">
-            <string>64 GB</string>
-           </property>
-          </item>
-          <item>
-           <property name="text">
-            <string>128 GB</string>
-           </property>
-          </item>
-          <item>
-           <property name="text">
-            <string>256 GB</string>
-           </property>
-          </item>
-          <item>
-           <property name="text">
-            <string>1 TB</string>
-           </property>
-          </item>
-         </widget>
-        </item>
-        <item row="2" column="1">
-         <widget class="QComboBox" name="usrnand_size">
-          <item>
-           <property name="text">
-            <string>26 GB</string>
-           </property>
-          </item>
-         </widget>
-        </item>
-        <item row="2" column="0">
-         <widget class="QLabel" name="label_6">
-          <property name="text">
-           <string>User NAND</string>
-          </property>
-         </widget>
-        </item>
-        <item row="0" column="0">
-         <widget class="QLabel" name="label_7">
-          <property name="text">
-           <string>NAND</string>
-          </property>
-         </widget>
-        </item>
-        <item row="0" column="1">
-         <widget class="QComboBox" name="nand_size">
-          <item>
-           <property name="text">
-            <string>29.1 GB</string>
-           </property>
-          </item>
-         </widget>
-        </item>
-       </layout>
-      </widget>
-     </item>
      <item>
       <widget class="QGroupBox" name="groupBox_4">
        <property name="title">

+ 66 - 14
src/yuzu/configuration/configure_general.cpp

@@ -7,17 +7,21 @@
 #include "core/core.h"
 #include "core/settings.h"
 #include "ui_configure_general.h"
+#include "yuzu/configuration/configuration_shared.h"
 #include "yuzu/configuration/configure_general.h"
 #include "yuzu/uisettings.h"
 
 ConfigureGeneral::ConfigureGeneral(QWidget* parent)
     : QWidget(parent), ui(new Ui::ConfigureGeneral) {
-
     ui->setupUi(this);
 
+    SetupPerGameUI();
+
     SetConfiguration();
 
-    connect(ui->toggle_frame_limit, &QCheckBox::toggled, ui->frame_limit, &QSpinBox::setEnabled);
+    connect(ui->toggle_frame_limit, &QCheckBox::stateChanged, ui->frame_limit, [this]() {
+        ui->frame_limit->setEnabled(ui->toggle_frame_limit->checkState() == Qt::Checked);
+    });
 }
 
 ConfigureGeneral::~ConfigureGeneral() = default;
@@ -26,27 +30,58 @@ void ConfigureGeneral::SetConfiguration() {
     const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn();
 
     ui->use_multi_core->setEnabled(runtime_lock);
-    ui->use_multi_core->setChecked(Settings::values.use_multi_core);
+    ui->use_multi_core->setChecked(Settings::values.use_multi_core.GetValue());
 
     ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing);
     ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot);
     ui->toggle_background_pause->setChecked(UISettings::values.pause_when_in_background);
     ui->toggle_hide_mouse->setChecked(UISettings::values.hide_mouse);
 
-    ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit);
-    ui->frame_limit->setEnabled(ui->toggle_frame_limit->isChecked());
-    ui->frame_limit->setValue(Settings::values.frame_limit);
+    ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit.GetValue());
+    ui->frame_limit->setValue(Settings::values.frame_limit.GetValue());
+
+    if (!Settings::configuring_global) {
+        if (Settings::values.use_multi_core.UsingGlobal()) {
+            ui->use_multi_core->setCheckState(Qt::PartiallyChecked);
+        }
+        if (Settings::values.use_frame_limit.UsingGlobal()) {
+            ui->toggle_frame_limit->setCheckState(Qt::PartiallyChecked);
+        }
+    }
+
+    ui->frame_limit->setEnabled(ui->toggle_frame_limit->checkState() == Qt::Checked &&
+                                ui->toggle_frame_limit->isEnabled());
 }
 
 void ConfigureGeneral::ApplyConfiguration() {
-    UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
-    UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked();
-    UISettings::values.pause_when_in_background = ui->toggle_background_pause->isChecked();
-    UISettings::values.hide_mouse = ui->toggle_hide_mouse->isChecked();
-
-    Settings::values.use_frame_limit = ui->toggle_frame_limit->isChecked();
-    Settings::values.frame_limit = ui->frame_limit->value();
-    Settings::values.use_multi_core = ui->use_multi_core->isChecked();
+    if (Settings::configuring_global) {
+        UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
+        UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked();
+        UISettings::values.pause_when_in_background = ui->toggle_background_pause->isChecked();
+        UISettings::values.hide_mouse = ui->toggle_hide_mouse->isChecked();
+
+        // Guard if during game and set to game-specific value
+        if (Settings::values.use_frame_limit.UsingGlobal()) {
+            Settings::values.use_frame_limit.SetValue(ui->toggle_frame_limit->checkState() ==
+                                                      Qt::Checked);
+            Settings::values.frame_limit.SetValue(ui->frame_limit->value());
+        }
+        if (Settings::values.use_multi_core.UsingGlobal()) {
+            Settings::values.use_multi_core.SetValue(ui->use_multi_core->isChecked());
+        }
+    } else {
+        ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_multi_core,
+                                                 ui->use_multi_core);
+
+        bool global_frame_limit = ui->toggle_frame_limit->checkState() == Qt::PartiallyChecked;
+        Settings::values.use_frame_limit.SetGlobal(global_frame_limit);
+        Settings::values.frame_limit.SetGlobal(global_frame_limit);
+        if (!global_frame_limit) {
+            Settings::values.use_frame_limit.SetValue(ui->toggle_frame_limit->checkState() ==
+                                                      Qt::Checked);
+            Settings::values.frame_limit.SetValue(ui->frame_limit->value());
+        }
+    }
 }
 
 void ConfigureGeneral::changeEvent(QEvent* event) {
@@ -60,3 +95,20 @@ void ConfigureGeneral::changeEvent(QEvent* event) {
 void ConfigureGeneral::RetranslateUI() {
     ui->retranslateUi(this);
 }
+
+void ConfigureGeneral::SetupPerGameUI() {
+    if (Settings::configuring_global) {
+        ui->toggle_frame_limit->setEnabled(Settings::values.use_frame_limit.UsingGlobal());
+        ui->frame_limit->setEnabled(Settings::values.frame_limit.UsingGlobal());
+
+        return;
+    }
+
+    ui->toggle_check_exit->setVisible(false);
+    ui->toggle_user_on_boot->setVisible(false);
+    ui->toggle_background_pause->setVisible(false);
+    ui->toggle_hide_mouse->setVisible(false);
+
+    ui->toggle_frame_limit->setTristate(true);
+    ui->use_multi_core->setTristate(true);
+}

+ 2 - 0
src/yuzu/configuration/configure_general.h

@@ -28,5 +28,7 @@ private:
 
     void SetConfiguration();
 
+    void SetupPerGameUI();
+
     std::unique_ptr<Ui::ConfigureGeneral> ui;
 };

+ 132 - 18
src/yuzu/configuration/configure_graphics.cpp

@@ -13,6 +13,7 @@
 #include "core/core.h"
 #include "core/settings.h"
 #include "ui_configure_graphics.h"
+#include "yuzu/configuration/configuration_shared.h"
 #include "yuzu/configuration/configure_graphics.h"
 
 #ifdef HAS_VULKAN
@@ -21,11 +22,13 @@
 
 ConfigureGraphics::ConfigureGraphics(QWidget* parent)
     : QWidget(parent), ui(new Ui::ConfigureGraphics) {
-    vulkan_device = Settings::values.vulkan_device;
+    vulkan_device = Settings::values.vulkan_device.GetValue();
     RetrieveVulkanDevices();
 
     ui->setupUi(this);
 
+    SetupPerGameUI();
+
     SetConfiguration();
 
     connect(ui->api, qOverload<int>(&QComboBox::currentIndexChanged), this,
@@ -40,6 +43,9 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent)
         }
         UpdateBackgroundColorButton(new_bg_color);
     });
+
+    ui->bg_label->setVisible(Settings::configuring_global);
+    ui->bg_combobox->setVisible(!Settings::configuring_global);
 }
 
 void ConfigureGraphics::UpdateDeviceSelection(int device) {
@@ -57,27 +63,95 @@ void ConfigureGraphics::SetConfiguration() {
     const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn();
 
     ui->api->setEnabled(runtime_lock);
-    ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend));
-    ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio);
-    ui->use_disk_shader_cache->setEnabled(runtime_lock);
-    ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache);
     ui->use_asynchronous_gpu_emulation->setEnabled(runtime_lock);
-    ui->use_asynchronous_gpu_emulation->setChecked(Settings::values.use_asynchronous_gpu_emulation);
-    UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green,
-                                                 Settings::values.bg_blue));
+    ui->use_disk_shader_cache->setEnabled(runtime_lock);
+
+    if (Settings::configuring_global) {
+        ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend.GetValue()));
+        ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio.GetValue());
+        ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache.GetValue());
+        ui->use_asynchronous_gpu_emulation->setChecked(
+            Settings::values.use_asynchronous_gpu_emulation.GetValue());
+    } else {
+        ConfigurationShared::SetPerGameSetting(ui->use_disk_shader_cache,
+                                               &Settings::values.use_disk_shader_cache);
+        ConfigurationShared::SetPerGameSetting(ui->use_asynchronous_gpu_emulation,
+                                               &Settings::values.use_asynchronous_gpu_emulation);
+
+        ConfigurationShared::SetPerGameSetting(ui->api, &Settings::values.renderer_backend);
+        ConfigurationShared::SetPerGameSetting(ui->aspect_ratio_combobox,
+                                               &Settings::values.aspect_ratio);
+
+        ui->bg_combobox->setCurrentIndex(Settings::values.bg_red.UsingGlobal() ? 0 : 1);
+        ui->bg_button->setEnabled(!Settings::values.bg_red.UsingGlobal());
+    }
+
+    UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red.GetValue(),
+                                                 Settings::values.bg_green.GetValue(),
+                                                 Settings::values.bg_blue.GetValue()));
     UpdateDeviceComboBox();
 }
 
 void ConfigureGraphics::ApplyConfiguration() {
-    Settings::values.renderer_backend = GetCurrentGraphicsBackend();
-    Settings::values.vulkan_device = vulkan_device;
-    Settings::values.aspect_ratio = ui->aspect_ratio_combobox->currentIndex();
-    Settings::values.use_disk_shader_cache = ui->use_disk_shader_cache->isChecked();
-    Settings::values.use_asynchronous_gpu_emulation =
-        ui->use_asynchronous_gpu_emulation->isChecked();
-    Settings::values.bg_red = static_cast<float>(bg_color.redF());
-    Settings::values.bg_green = static_cast<float>(bg_color.greenF());
-    Settings::values.bg_blue = static_cast<float>(bg_color.blueF());
+    if (Settings::configuring_global) {
+        // Guard if during game and set to game-specific value
+        if (Settings::values.renderer_backend.UsingGlobal()) {
+            Settings::values.renderer_backend.SetValue(GetCurrentGraphicsBackend());
+        }
+        if (Settings::values.vulkan_device.UsingGlobal()) {
+            Settings::values.vulkan_device.SetValue(vulkan_device);
+        }
+        if (Settings::values.aspect_ratio.UsingGlobal()) {
+            Settings::values.aspect_ratio.SetValue(ui->aspect_ratio_combobox->currentIndex());
+        }
+        if (Settings::values.use_disk_shader_cache.UsingGlobal()) {
+            Settings::values.use_disk_shader_cache.SetValue(ui->use_disk_shader_cache->isChecked());
+        }
+        if (Settings::values.use_asynchronous_gpu_emulation.UsingGlobal()) {
+            Settings::values.use_asynchronous_gpu_emulation.SetValue(
+                ui->use_asynchronous_gpu_emulation->isChecked());
+        }
+        if (Settings::values.bg_red.UsingGlobal()) {
+            Settings::values.bg_red.SetValue(static_cast<float>(bg_color.redF()));
+            Settings::values.bg_green.SetValue(static_cast<float>(bg_color.greenF()));
+            Settings::values.bg_blue.SetValue(static_cast<float>(bg_color.blueF()));
+        }
+    } else {
+        if (ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
+            Settings::values.renderer_backend.SetGlobal(true);
+            Settings::values.vulkan_device.SetGlobal(true);
+        } else {
+            Settings::values.renderer_backend.SetGlobal(false);
+            Settings::values.renderer_backend.SetValue(GetCurrentGraphicsBackend());
+            if (GetCurrentGraphicsBackend() == Settings::RendererBackend::Vulkan) {
+                Settings::values.vulkan_device.SetGlobal(false);
+                Settings::values.vulkan_device.SetValue(vulkan_device);
+            } else {
+                Settings::values.vulkan_device.SetGlobal(true);
+            }
+        }
+
+        ConfigurationShared::ApplyPerGameSetting(&Settings::values.aspect_ratio,
+                                                 ui->aspect_ratio_combobox);
+
+        ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_disk_shader_cache,
+                                                 ui->use_disk_shader_cache);
+        ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_gpu_emulation,
+                                                 ui->use_asynchronous_gpu_emulation);
+
+        if (ui->bg_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
+            Settings::values.bg_red.SetGlobal(true);
+            Settings::values.bg_green.SetGlobal(true);
+            Settings::values.bg_blue.SetGlobal(true);
+        } else {
+            Settings::values.bg_red.SetGlobal(false);
+            Settings::values.bg_green.SetGlobal(false);
+            Settings::values.bg_blue.SetGlobal(false);
+            Settings::values.bg_red.SetValue(static_cast<float>(bg_color.redF()));
+            Settings::values.bg_green.SetValue(static_cast<float>(bg_color.greenF()));
+            Settings::values.bg_blue.SetValue(static_cast<float>(bg_color.blueF()));
+        }
+    }
 }
 
 void ConfigureGraphics::changeEvent(QEvent* event) {
@@ -106,6 +180,11 @@ void ConfigureGraphics::UpdateDeviceComboBox() {
     ui->device->clear();
 
     bool enabled = false;
+
+    if (!Settings::configuring_global &&
+        ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
+        vulkan_device = Settings::values.vulkan_device.GetValue();
+    }
     switch (GetCurrentGraphicsBackend()) {
     case Settings::RendererBackend::OpenGL:
         ui->device->addItem(tr("OpenGL Graphics Device"));
@@ -119,6 +198,9 @@ void ConfigureGraphics::UpdateDeviceComboBox() {
         enabled = !vulkan_devices.empty();
         break;
     }
+    // If in per-game config and use global is selected, don't enable.
+    enabled &= !(!Settings::configuring_global &&
+                 ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX);
     ui->device->setEnabled(enabled && !Core::System::GetInstance().IsPoweredOn());
 }
 
@@ -132,5 +214,37 @@ void ConfigureGraphics::RetrieveVulkanDevices() {
 }
 
 Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const {
-    return static_cast<Settings::RendererBackend>(ui->api->currentIndex());
+    if (Settings::configuring_global) {
+        return static_cast<Settings::RendererBackend>(ui->api->currentIndex());
+    }
+
+    if (ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
+        Settings::values.renderer_backend.SetGlobal(true);
+        return Settings::values.renderer_backend.GetValue();
+    }
+    Settings::values.renderer_backend.SetGlobal(false);
+    return static_cast<Settings::RendererBackend>(ui->api->currentIndex() -
+                                                  ConfigurationShared::USE_GLOBAL_OFFSET);
+}
+
+void ConfigureGraphics::SetupPerGameUI() {
+    if (Settings::configuring_global) {
+        ui->api->setEnabled(Settings::values.renderer_backend.UsingGlobal());
+        ui->device->setEnabled(Settings::values.renderer_backend.UsingGlobal());
+        ui->aspect_ratio_combobox->setEnabled(Settings::values.aspect_ratio.UsingGlobal());
+        ui->use_asynchronous_gpu_emulation->setEnabled(
+            Settings::values.use_asynchronous_gpu_emulation.UsingGlobal());
+        ui->use_disk_shader_cache->setEnabled(Settings::values.use_disk_shader_cache.UsingGlobal());
+        ui->bg_button->setEnabled(Settings::values.bg_red.UsingGlobal());
+
+        return;
+    }
+
+    connect(ui->bg_combobox, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), this,
+            [this](int index) { ui->bg_button->setEnabled(index == 1); });
+
+    ui->use_disk_shader_cache->setTristate(true);
+    ui->use_asynchronous_gpu_emulation->setTristate(true);
+    ConfigurationShared::InsertGlobalItem(ui->aspect_ratio_combobox);
+    ConfigurationShared::InsertGlobalItem(ui->api);
 }

+ 2 - 0
src/yuzu/configuration/configure_graphics.h

@@ -35,6 +35,8 @@ private:
 
     void RetrieveVulkanDevices();
 
+    void SetupPerGameUI();
+
     Settings::RendererBackend GetCurrentGraphicsBackend() const;
 
     std::unique_ptr<Ui::ConfigureGraphics> ui;

+ 36 - 0
src/yuzu/configuration/configure_graphics.ui

@@ -121,6 +121,29 @@
         </item>
         <item>
          <layout class="QHBoxLayout" name="horizontalLayout_3">
+          <item>
+           <widget class="QComboBox" name="bg_combobox">
+            <property name="currentText">
+             <string>Use global background color</string>
+            </property>
+            <property name="currentIndex">
+             <number>0</number>
+            </property>
+            <property name="maxVisibleItems">
+             <number>10</number>
+            </property>
+            <item>
+             <property name="text">
+              <string>Use global background color</string>
+             </property>
+            </item>
+            <item>
+             <property name="text">
+              <string>Set background color:</string>
+             </property>
+            </item>
+           </widget>
+          </item>
           <item>
            <widget class="QLabel" name="bg_label">
             <property name="text">
@@ -128,6 +151,19 @@
             </property>
            </widget>
           </item>
+          <item>
+           <spacer name="horizontalSpacer">
+            <property name="orientation">
+             <enum>Qt::Horizontal</enum>
+            </property>
+            <property name="sizeHint" stdset="0">
+             <size>
+              <width>40</width>
+              <height>20</height>
+             </size>
+            </property>
+           </spacer>
+          </item>
           <item>
            <widget class="QPushButton" name="bg_button">
             <property name="maximumSize">

+ 93 - 13
src/yuzu/configuration/configure_graphics_advanced.cpp

@@ -5,6 +5,7 @@
 #include "core/core.h"
 #include "core/settings.h"
 #include "ui_configure_graphics_advanced.h"
+#include "yuzu/configuration/configuration_shared.h"
 #include "yuzu/configuration/configure_graphics_advanced.h"
 
 ConfigureGraphicsAdvanced::ConfigureGraphicsAdvanced(QWidget* parent)
@@ -12,6 +13,8 @@ ConfigureGraphicsAdvanced::ConfigureGraphicsAdvanced(QWidget* parent)
 
     ui->setupUi(this);
 
+    SetupPerGameUI();
+
     SetConfiguration();
 }
 
@@ -19,26 +22,81 @@ ConfigureGraphicsAdvanced::~ConfigureGraphicsAdvanced() = default;
 
 void ConfigureGraphicsAdvanced::SetConfiguration() {
     const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn();
-    ui->gpu_accuracy->setCurrentIndex(static_cast<int>(Settings::values.gpu_accuracy));
     ui->use_vsync->setEnabled(runtime_lock);
-    ui->use_vsync->setChecked(Settings::values.use_vsync);
     ui->use_assembly_shaders->setEnabled(runtime_lock);
-    ui->use_assembly_shaders->setChecked(Settings::values.use_assembly_shaders);
-    ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time);
     ui->force_30fps_mode->setEnabled(runtime_lock);
-    ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode);
     ui->anisotropic_filtering_combobox->setEnabled(runtime_lock);
-    ui->anisotropic_filtering_combobox->setCurrentIndex(Settings::values.max_anisotropy);
+
+    if (Settings::configuring_global) {
+        ui->gpu_accuracy->setCurrentIndex(
+            static_cast<int>(Settings::values.gpu_accuracy.GetValue()));
+        ui->use_vsync->setChecked(Settings::values.use_vsync.GetValue());
+        ui->use_assembly_shaders->setChecked(Settings::values.use_assembly_shaders.GetValue());
+        ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue());
+        ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode.GetValue());
+        ui->anisotropic_filtering_combobox->setCurrentIndex(
+            Settings::values.max_anisotropy.GetValue());
+    } else {
+        ConfigurationShared::SetPerGameSetting(ui->gpu_accuracy, &Settings::values.gpu_accuracy);
+        ConfigurationShared::SetPerGameSetting(ui->use_vsync, &Settings::values.use_vsync);
+        ConfigurationShared::SetPerGameSetting(ui->use_assembly_shaders,
+                                               &Settings::values.use_assembly_shaders);
+        ConfigurationShared::SetPerGameSetting(ui->use_fast_gpu_time,
+                                               &Settings::values.use_fast_gpu_time);
+        ConfigurationShared::SetPerGameSetting(ui->force_30fps_mode,
+                                               &Settings::values.force_30fps_mode);
+        ConfigurationShared::SetPerGameSetting(ui->anisotropic_filtering_combobox,
+                                               &Settings::values.max_anisotropy);
+    }
 }
 
 void ConfigureGraphicsAdvanced::ApplyConfiguration() {
-    auto gpu_accuracy = static_cast<Settings::GPUAccuracy>(ui->gpu_accuracy->currentIndex());
-    Settings::values.gpu_accuracy = gpu_accuracy;
-    Settings::values.use_vsync = ui->use_vsync->isChecked();
-    Settings::values.use_assembly_shaders = ui->use_assembly_shaders->isChecked();
-    Settings::values.use_fast_gpu_time = ui->use_fast_gpu_time->isChecked();
-    Settings::values.force_30fps_mode = ui->force_30fps_mode->isChecked();
-    Settings::values.max_anisotropy = ui->anisotropic_filtering_combobox->currentIndex();
+    // Subtract 2 if configuring per-game (separator and "use global configuration" take 2 slots)
+    const auto gpu_accuracy = static_cast<Settings::GPUAccuracy>(
+        ui->gpu_accuracy->currentIndex() -
+        ((Settings::configuring_global) ? 0 : ConfigurationShared::USE_GLOBAL_OFFSET));
+
+    if (Settings::configuring_global) {
+        // Must guard in case of a during-game configuration when set to be game-specific.
+        if (Settings::values.gpu_accuracy.UsingGlobal()) {
+            Settings::values.gpu_accuracy.SetValue(gpu_accuracy);
+        }
+        if (Settings::values.use_vsync.UsingGlobal()) {
+            Settings::values.use_vsync.SetValue(ui->use_vsync->isChecked());
+        }
+        if (Settings::values.use_assembly_shaders.UsingGlobal()) {
+            Settings::values.use_assembly_shaders.SetValue(ui->use_assembly_shaders->isChecked());
+        }
+        if (Settings::values.use_fast_gpu_time.UsingGlobal()) {
+            Settings::values.use_fast_gpu_time.SetValue(ui->use_fast_gpu_time->isChecked());
+        }
+        if (Settings::values.force_30fps_mode.UsingGlobal()) {
+            Settings::values.force_30fps_mode.SetValue(ui->force_30fps_mode->isChecked());
+        }
+        if (Settings::values.max_anisotropy.UsingGlobal()) {
+            Settings::values.max_anisotropy.SetValue(
+                ui->anisotropic_filtering_combobox->currentIndex());
+        }
+    } else {
+        ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy,
+                                                 ui->anisotropic_filtering_combobox);
+        ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vsync, ui->use_vsync);
+        ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_assembly_shaders,
+                                                 ui->use_assembly_shaders);
+        ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_fast_gpu_time,
+                                                 ui->use_fast_gpu_time);
+        ConfigurationShared::ApplyPerGameSetting(&Settings::values.force_30fps_mode,
+                                                 ui->force_30fps_mode);
+        ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy,
+                                                 ui->anisotropic_filtering_combobox);
+
+        if (ui->gpu_accuracy->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
+            Settings::values.gpu_accuracy.SetGlobal(true);
+        } else {
+            Settings::values.gpu_accuracy.SetGlobal(false);
+            Settings::values.gpu_accuracy.SetValue(gpu_accuracy);
+        }
+    }
 }
 
 void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) {
@@ -52,3 +110,25 @@ void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) {
 void ConfigureGraphicsAdvanced::RetranslateUI() {
     ui->retranslateUi(this);
 }
+
+void ConfigureGraphicsAdvanced::SetupPerGameUI() {
+    // Disable if not global (only happens during game)
+    if (Settings::configuring_global) {
+        ui->gpu_accuracy->setEnabled(Settings::values.gpu_accuracy.UsingGlobal());
+        ui->use_vsync->setEnabled(Settings::values.use_vsync.UsingGlobal());
+        ui->use_assembly_shaders->setEnabled(Settings::values.use_assembly_shaders.UsingGlobal());
+        ui->use_fast_gpu_time->setEnabled(Settings::values.use_fast_gpu_time.UsingGlobal());
+        ui->force_30fps_mode->setEnabled(Settings::values.force_30fps_mode.UsingGlobal());
+        ui->anisotropic_filtering_combobox->setEnabled(
+            Settings::values.max_anisotropy.UsingGlobal());
+
+        return;
+    }
+
+    ConfigurationShared::InsertGlobalItem(ui->gpu_accuracy);
+    ui->use_vsync->setTristate(true);
+    ui->use_assembly_shaders->setTristate(true);
+    ui->use_fast_gpu_time->setTristate(true);
+    ui->force_30fps_mode->setTristate(true);
+    ConfigurationShared::InsertGlobalItem(ui->anisotropic_filtering_combobox);
+}

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor