summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/audio_core/cubeb_sink.cpp2
-rw-r--r--src/audio_core/stream.cpp7
-rw-r--r--src/core/core.cpp6
-rw-r--r--src/core/frontend/framebuffer_layout.cpp2
-rw-r--r--src/core/hle/kernel/process.cpp2
-rw-r--r--src/core/hle/service/am/am.cpp8
-rw-r--r--src/core/hle/service/ns/ns.cpp3
-rw-r--r--src/core/hle/service/set/set.cpp10
-rw-r--r--src/core/hle/service/spl/module.cpp2
-rw-r--r--src/core/hle/service/vi/vi.cpp20
-rw-r--r--src/core/perf_stats.cpp5
-rw-r--r--src/core/settings.cpp84
-rw-r--r--src/core/settings.h124
-rw-r--r--src/core/telemetry_session.cpp27
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h6
-rw-r--r--src/video_core/gpu.cpp2
-rw-r--r--src/video_core/query_cache.h2
-rw-r--r--src/video_core/renderer_base.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_device.cpp8
-rw-r--r--src/video_core/renderer_opengl/gl_shader_disk_cache.cpp2
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp8
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp2
-rw-r--r--src/video_core/texture_cache/texture_cache.h2
-rw-r--r--src/video_core/textures/texture.cpp2
-rw-r--r--src/video_core/video_core.cpp8
-rw-r--r--src/yuzu/CMakeLists.txt11
-rw-r--r--src/yuzu/bootmanager.cpp8
-rw-r--r--src/yuzu/configuration/config.cpp336
-rw-r--r--src/yuzu/configuration/config.h27
-rw-r--r--src/yuzu/configuration/configuration_shared.cpp76
-rw-r--r--src/yuzu/configuration/configuration_shared.h36
-rw-r--r--src/yuzu/configuration/configure_audio.cpp82
-rw-r--r--src/yuzu/configuration/configure_audio.h2
-rw-r--r--src/yuzu/configuration/configure_audio.ui50
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp2
-rw-r--r--src/yuzu/configuration/configure_general.cpp78
-rw-r--r--src/yuzu/configuration/configure_general.h2
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp150
-rw-r--r--src/yuzu/configuration/configure_graphics.h2
-rw-r--r--src/yuzu/configuration/configure_graphics.ui36
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.cpp106
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.h2
-rw-r--r--src/yuzu/configuration/configure_per_game.cpp140
-rw-r--r--src/yuzu/configuration/configure_per_game.h51
-rw-r--r--src/yuzu/configuration/configure_per_game.ui350
-rw-r--r--src/yuzu/configuration/configure_per_game_addons.cpp (renamed from src/yuzu/configuration/configure_per_general.cpp)95
-rw-r--r--src/yuzu/configuration/configure_per_game_addons.h (renamed from src/yuzu/configuration/configure_per_general.h)16
-rw-r--r--src/yuzu/configuration/configure_per_game_addons.ui38
-rw-r--r--src/yuzu/configuration/configure_per_general.ui276
-rw-r--r--src/yuzu/configuration/configure_system.cpp190
-rw-r--r--src/yuzu/configuration/configure_system.h2
-rw-r--r--src/yuzu/main.cpp116
-rw-r--r--src/yuzu/main.h1
-rw-r--r--src/yuzu_cmd/config.cpp83
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp2
-rw-r--r--src/yuzu_cmd/yuzu.cpp2
-rw-r--r--src/yuzu_tester/config.cpp63
57 files changed, 1942 insertions, 835 deletions
diff --git a/src/audio_core/cubeb_sink.cpp b/src/audio_core/cubeb_sink.cpp
index c4e0e30fe..41bf5cd4d 100644
--- a/src/audio_core/cubeb_sink.cpp
+++ b/src/audio_core/cubeb_sink.cpp
@@ -193,7 +193,7 @@ long CubebSinkStream::DataCallback(cubeb_stream* stream, void* user_data, const
193 const std::size_t samples_to_write = num_channels * num_frames; 193 const std::size_t samples_to_write = num_channels * num_frames;
194 std::size_t samples_written; 194 std::size_t samples_written;
195 195
196 if (Settings::values.enable_audio_stretching) { 196 if (Settings::values.enable_audio_stretching.GetValue()) {
197 const std::vector<s16> in{impl->queue.Pop()}; 197 const std::vector<s16> in{impl->queue.Pop()};
198 const std::size_t num_in{in.size() / num_channels}; 198 const std::size_t num_in{in.size() / num_channels};
199 s16* const out{reinterpret_cast<s16*>(buffer)}; 199 s16* const out{reinterpret_cast<s16*>(buffer)};
diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp
index 22e8df373..aab3e979a 100644
--- a/src/audio_core/stream.cpp
+++ b/src/audio_core/stream.cpp
@@ -105,9 +105,10 @@ void Stream::PlayNextBuffer(s64 cycles_late) {
105 105
106 sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples()); 106 sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples());
107 107
108 core_timing.ScheduleEvent(GetBufferReleaseNS(*active_buffer) - 108 core_timing.ScheduleEvent(
109 (Settings::values.enable_audio_stretching ? 0 : cycles_late), 109 GetBufferReleaseNS(*active_buffer) -
110 release_event, {}); 110 (Settings::values.enable_audio_stretching.GetValue() ? 0 : cycles_late),
111 release_event, {});
111} 112}
112 113
113void Stream::ReleaseActiveBuffer(s64 cycles_late) { 114void Stream::ReleaseActiveBuffer(s64 cycles_late) {
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 1a243c515..69a1aa0a5 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -147,8 +147,8 @@ struct System::Impl {
147 147
148 device_memory = std::make_unique<Core::DeviceMemory>(system); 148 device_memory = std::make_unique<Core::DeviceMemory>(system);
149 149
150 is_multicore = Settings::values.use_multi_core; 150 is_multicore = Settings::values.use_multi_core.GetValue();
151 is_async_gpu = is_multicore || Settings::values.use_asynchronous_gpu_emulation; 151 is_async_gpu = is_multicore || Settings::values.use_asynchronous_gpu_emulation.GetValue();
152 152
153 kernel.SetMulticore(is_multicore); 153 kernel.SetMulticore(is_multicore);
154 cpu_manager.SetMulticore(is_multicore); 154 cpu_manager.SetMulticore(is_multicore);
@@ -162,7 +162,7 @@ struct System::Impl {
162 const auto current_time = std::chrono::duration_cast<std::chrono::seconds>( 162 const auto current_time = std::chrono::duration_cast<std::chrono::seconds>(
163 std::chrono::system_clock::now().time_since_epoch()); 163 std::chrono::system_clock::now().time_since_epoch());
164 Settings::values.custom_rtc_differential = 164 Settings::values.custom_rtc_differential =
165 Settings::values.custom_rtc.value_or(current_time) - current_time; 165 Settings::values.custom_rtc.GetValue().value_or(current_time) - current_time;
166 166
167 // Create a default fs if one doesn't already exist. 167 // Create a default fs if one doesn't already exist.
168 if (virtual_filesystem == nullptr) 168 if (virtual_filesystem == nullptr)
diff --git a/src/core/frontend/framebuffer_layout.cpp b/src/core/frontend/framebuffer_layout.cpp
index d0c43447c..c1fbc235b 100644
--- a/src/core/frontend/framebuffer_layout.cpp
+++ b/src/core/frontend/framebuffer_layout.cpp
@@ -29,7 +29,7 @@ FramebufferLayout DefaultFrameLayout(u32 width, u32 height) {
29 29
30 const float window_aspect_ratio = static_cast<float>(height) / width; 30 const float window_aspect_ratio = static_cast<float>(height) / width;
31 const float emulation_aspect_ratio = EmulationAspectRatio( 31 const float emulation_aspect_ratio = EmulationAspectRatio(
32 static_cast<AspectRatio>(Settings::values.aspect_ratio), window_aspect_ratio); 32 static_cast<AspectRatio>(Settings::values.aspect_ratio.GetValue()), window_aspect_ratio);
33 33
34 const Common::Rectangle<u32> screen_window_area{0, 0, width, height}; 34 const Common::Rectangle<u32> screen_window_area{0, 0, width, height};
35 Common::Rectangle<u32> screen = MaxRectangle(screen_window_area, emulation_aspect_ratio); 35 Common::Rectangle<u32> screen = MaxRectangle(screen_window_area, emulation_aspect_ratio);
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index f9d7c024d..c6fcb56ad 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -123,7 +123,7 @@ std::shared_ptr<Process> Process::Create(Core::System& system, std::string name,
123 : kernel.CreateNewUserProcessID(); 123 : kernel.CreateNewUserProcessID();
124 process->capabilities.InitializeForMetadatalessProcess(); 124 process->capabilities.InitializeForMetadatalessProcess();
125 125
126 std::mt19937 rng(Settings::values.rng_seed.value_or(0)); 126 std::mt19937 rng(Settings::values.rng_seed.GetValue().value_or(0));
127 std::uniform_int_distribution<u64> distribution; 127 std::uniform_int_distribution<u64> distribution;
128 std::generate(process->random_entropy.begin(), process->random_entropy.end(), 128 std::generate(process->random_entropy.begin(), process->random_entropy.end(),
129 [&] { return distribution(rng); }); 129 [&] { return distribution(rng); });
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index c688d6d98..256449aa7 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -749,14 +749,14 @@ void ICommonStateGetter::GetDefaultDisplayResolution(Kernel::HLERequestContext&
749 749
750 if (Settings::values.use_docked_mode) { 750 if (Settings::values.use_docked_mode) {
751 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth) * 751 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth) *
752 static_cast<u32>(Settings::values.resolution_factor)); 752 static_cast<u32>(Settings::values.resolution_factor.GetValue()));
753 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight) * 753 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight) *
754 static_cast<u32>(Settings::values.resolution_factor)); 754 static_cast<u32>(Settings::values.resolution_factor.GetValue()));
755 } else { 755 } else {
756 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth) * 756 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth) *
757 static_cast<u32>(Settings::values.resolution_factor)); 757 static_cast<u32>(Settings::values.resolution_factor.GetValue()));
758 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight) * 758 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight) *
759 static_cast<u32>(Settings::values.resolution_factor)); 759 static_cast<u32>(Settings::values.resolution_factor.GetValue()));
760 } 760 }
761} 761}
762 762
diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp
index 7e5ceccdb..6cfa9666d 100644
--- a/src/core/hle/service/ns/ns.cpp
+++ b/src/core/hle/service/ns/ns.cpp
@@ -366,7 +366,8 @@ ResultVal<u8> IApplicationManagerInterface::GetApplicationDesiredLanguage(
366 LOG_DEBUG(Service_NS, "called with supported_languages={:08X}", supported_languages); 366 LOG_DEBUG(Service_NS, "called with supported_languages={:08X}", supported_languages);
367 367
368 // Get language code from settings 368 // Get language code from settings
369 const auto language_code = Set::GetLanguageCodeFromIndex(Settings::values.language_index); 369 const auto language_code =
370 Set::GetLanguageCodeFromIndex(Settings::values.language_index.GetValue());
370 371
371 // Convert to application language, get priority list 372 // Convert to application language, get priority list
372 const auto application_language = ConvertToApplicationLanguage(language_code); 373 const auto application_language = ConvertToApplicationLanguage(language_code);
diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp
index e5cfd2101..34fe2fd82 100644
--- a/src/core/hle/service/set/set.cpp
+++ b/src/core/hle/service/set/set.cpp
@@ -91,7 +91,7 @@ void GetAvailableLanguageCodesImpl(Kernel::HLERequestContext& ctx, std::size_t m
91} 91}
92 92
93void GetKeyCodeMapImpl(Kernel::HLERequestContext& ctx) { 93void GetKeyCodeMapImpl(Kernel::HLERequestContext& ctx) {
94 const auto language_code = available_language_codes[Settings::values.language_index]; 94 const auto language_code = available_language_codes[Settings::values.language_index.GetValue()];
95 const auto key_code = 95 const auto key_code =
96 std::find_if(language_to_layout.cbegin(), language_to_layout.cend(), 96 std::find_if(language_to_layout.cbegin(), language_to_layout.cend(),
97 [=](const auto& element) { return element.first == language_code; }); 97 [=](const auto& element) { return element.first == language_code; });
@@ -99,7 +99,7 @@ void GetKeyCodeMapImpl(Kernel::HLERequestContext& ctx) {
99 if (key_code == language_to_layout.cend()) { 99 if (key_code == language_to_layout.cend()) {
100 LOG_ERROR(Service_SET, 100 LOG_ERROR(Service_SET,
101 "Could not find keyboard layout for language index {}, defaulting to English us", 101 "Could not find keyboard layout for language index {}, defaulting to English us",
102 Settings::values.language_index); 102 Settings::values.language_index.GetValue());
103 } else { 103 } else {
104 layout = key_code->second; 104 layout = key_code->second;
105 } 105 }
@@ -163,11 +163,11 @@ void SET::GetQuestFlag(Kernel::HLERequestContext& ctx) {
163} 163}
164 164
165void SET::GetLanguageCode(Kernel::HLERequestContext& ctx) { 165void SET::GetLanguageCode(Kernel::HLERequestContext& ctx) {
166 LOG_DEBUG(Service_SET, "called {}", Settings::values.language_index); 166 LOG_DEBUG(Service_SET, "called {}", Settings::values.language_index.GetValue());
167 167
168 IPC::ResponseBuilder rb{ctx, 4}; 168 IPC::ResponseBuilder rb{ctx, 4};
169 rb.Push(RESULT_SUCCESS); 169 rb.Push(RESULT_SUCCESS);
170 rb.PushEnum(available_language_codes[Settings::values.language_index]); 170 rb.PushEnum(available_language_codes[Settings::values.language_index.GetValue()]);
171} 171}
172 172
173void SET::GetRegionCode(Kernel::HLERequestContext& ctx) { 173void SET::GetRegionCode(Kernel::HLERequestContext& ctx) {
@@ -175,7 +175,7 @@ void SET::GetRegionCode(Kernel::HLERequestContext& ctx) {
175 175
176 IPC::ResponseBuilder rb{ctx, 3}; 176 IPC::ResponseBuilder rb{ctx, 3};
177 rb.Push(RESULT_SUCCESS); 177 rb.Push(RESULT_SUCCESS);
178 rb.Push(Settings::values.region_index); 178 rb.Push(Settings::values.region_index.GetValue());
179} 179}
180 180
181void SET::GetKeyCodeMap(Kernel::HLERequestContext& ctx) { 181void SET::GetKeyCodeMap(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/spl/module.cpp b/src/core/hle/service/spl/module.cpp
index e724d4ab8..865ed3b91 100644
--- a/src/core/hle/service/spl/module.cpp
+++ b/src/core/hle/service/spl/module.cpp
@@ -19,7 +19,7 @@ namespace Service::SPL {
19 19
20Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) 20Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
21 : ServiceFramework(name), module(std::move(module)), 21 : ServiceFramework(name), module(std::move(module)),
22 rng(Settings::values.rng_seed.value_or(std::time(nullptr))) {} 22 rng(Settings::values.rng_seed.GetValue().value_or(std::time(nullptr))) {}
23 23
24Module::Interface::~Interface() = default; 24Module::Interface::~Interface() = default;
25 25
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index 157092074..552a5e4ef 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -519,9 +519,9 @@ private:
519 IGBPConnectRequestParcel request{ctx.ReadBuffer()}; 519 IGBPConnectRequestParcel request{ctx.ReadBuffer()};
520 IGBPConnectResponseParcel response{ 520 IGBPConnectResponseParcel response{
521 static_cast<u32>(static_cast<u32>(DisplayResolution::UndockedWidth) * 521 static_cast<u32>(static_cast<u32>(DisplayResolution::UndockedWidth) *
522 Settings::values.resolution_factor), 522 Settings::values.resolution_factor.GetValue()),
523 static_cast<u32>(static_cast<u32>(DisplayResolution::UndockedHeight) * 523 static_cast<u32>(static_cast<u32>(DisplayResolution::UndockedHeight) *
524 Settings::values.resolution_factor)}; 524 Settings::values.resolution_factor.GetValue())};
525 ctx.WriteBuffer(response.Serialize()); 525 ctx.WriteBuffer(response.Serialize());
526 break; 526 break;
527 } 527 }
@@ -748,14 +748,14 @@ private:
748 748
749 if (Settings::values.use_docked_mode) { 749 if (Settings::values.use_docked_mode) {
750 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth) * 750 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth) *
751 static_cast<u32>(Settings::values.resolution_factor)); 751 static_cast<u32>(Settings::values.resolution_factor.GetValue()));
752 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight) * 752 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight) *
753 static_cast<u32>(Settings::values.resolution_factor)); 753 static_cast<u32>(Settings::values.resolution_factor.GetValue()));
754 } else { 754 } else {
755 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth) * 755 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth) *
756 static_cast<u32>(Settings::values.resolution_factor)); 756 static_cast<u32>(Settings::values.resolution_factor.GetValue()));
757 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight) * 757 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight) *
758 static_cast<u32>(Settings::values.resolution_factor)); 758 static_cast<u32>(Settings::values.resolution_factor.GetValue()));
759 } 759 }
760 760
761 rb.PushRaw<float>(60.0f); // This wouldn't seem to be correct for 30 fps games. 761 rb.PushRaw<float>(60.0f); // This wouldn't seem to be correct for 30 fps games.
@@ -1029,9 +1029,9 @@ private:
1029 // between docked and undocked dimensions. We take the liberty of applying 1029 // between docked and undocked dimensions. We take the liberty of applying
1030 // the resolution scaling factor here. 1030 // the resolution scaling factor here.
1031 rb.Push(static_cast<u64>(DisplayResolution::UndockedWidth) * 1031 rb.Push(static_cast<u64>(DisplayResolution::UndockedWidth) *
1032 static_cast<u32>(Settings::values.resolution_factor)); 1032 static_cast<u32>(Settings::values.resolution_factor.GetValue()));
1033 rb.Push(static_cast<u64>(DisplayResolution::UndockedHeight) * 1033 rb.Push(static_cast<u64>(DisplayResolution::UndockedHeight) *
1034 static_cast<u32>(Settings::values.resolution_factor)); 1034 static_cast<u32>(Settings::values.resolution_factor.GetValue()));
1035 } 1035 }
1036 1036
1037 void SetLayerScalingMode(Kernel::HLERequestContext& ctx) { 1037 void SetLayerScalingMode(Kernel::HLERequestContext& ctx) {
@@ -1064,8 +1064,8 @@ private:
1064 LOG_WARNING(Service_VI, "(STUBBED) called"); 1064 LOG_WARNING(Service_VI, "(STUBBED) called");
1065 1065
1066 DisplayInfo display_info; 1066 DisplayInfo display_info;
1067 display_info.width *= static_cast<u64>(Settings::values.resolution_factor); 1067 display_info.width *= static_cast<u64>(Settings::values.resolution_factor.GetValue());
1068 display_info.height *= static_cast<u64>(Settings::values.resolution_factor); 1068 display_info.height *= static_cast<u64>(Settings::values.resolution_factor.GetValue());
1069 ctx.WriteBuffer(&display_info, sizeof(DisplayInfo)); 1069 ctx.WriteBuffer(&display_info, sizeof(DisplayInfo));
1070 IPC::ResponseBuilder rb{ctx, 4}; 1070 IPC::ResponseBuilder rb{ctx, 4};
1071 rb.Push(RESULT_SUCCESS); 1071 rb.Push(RESULT_SUCCESS);
diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp
index 9f3a6b811..29339ead7 100644
--- a/src/core/perf_stats.cpp
+++ b/src/core/perf_stats.cpp
@@ -119,13 +119,14 @@ double PerfStats::GetLastFrameTimeScale() {
119} 119}
120 120
121void FrameLimiter::DoFrameLimiting(microseconds current_system_time_us) { 121void FrameLimiter::DoFrameLimiting(microseconds current_system_time_us) {
122 if (!Settings::values.use_frame_limit || Settings::values.use_multi_core) { 122 if (!Settings::values.use_frame_limit.GetValue() ||
123 Settings::values.use_multi_core.GetValue()) {
123 return; 124 return;
124 } 125 }
125 126
126 auto now = Clock::now(); 127 auto now = Clock::now();
127 128
128 const double sleep_scale = Settings::values.frame_limit / 100.0; 129 const double sleep_scale = Settings::values.frame_limit.GetValue() / 100.0;
129 130
130 // Max lag caused by slow frames. Shouldn't be more than the length of a frame at the current 131 // Max lag caused by slow frames. Shouldn't be more than the length of a frame at the current
131 // speed percent or it will clamp too much and prevent this from properly limiting to that 132 // speed percent or it will clamp too much and prevent this from properly limiting to that
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index 56df5e925..d3886c4ec 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -62,6 +62,7 @@ const std::array<const char*, NumMouseButtons> mapping = {{
62} 62}
63 63
64Values values = {}; 64Values values = {};
65bool configuring_global = true;
65 66
66std::string GetTimeZoneString() { 67std::string GetTimeZoneString() {
67 static constexpr std::array<const char*, 46> timezones{{ 68 static constexpr std::array<const char*, 46> timezones{{
@@ -73,9 +74,9 @@ std::string GetTimeZoneString() {
73 "UCT", "Universal", "UTC", "W-SU", "WET", "Zulu", 74 "UCT", "Universal", "UTC", "W-SU", "WET", "Zulu",
74 }}; 75 }};
75 76
76 ASSERT(Settings::values.time_zone_index < timezones.size()); 77 ASSERT(Settings::values.time_zone_index.GetValue() < timezones.size());
77 78
78 return timezones[Settings::values.time_zone_index]; 79 return timezones[Settings::values.time_zone_index.GetValue()];
79} 80}
80 81
81void Apply() { 82void Apply() {
@@ -97,25 +98,25 @@ void LogSetting(const std::string& name, const T& value) {
97 98
98void LogSettings() { 99void LogSettings() {
99 LOG_INFO(Config, "yuzu Configuration:"); 100 LOG_INFO(Config, "yuzu Configuration:");
100 LogSetting("System_UseDockedMode", Settings::values.use_docked_mode); 101 LogSetting("Controls_UseDockedMode", Settings::values.use_docked_mode);
101 LogSetting("System_RngSeed", Settings::values.rng_seed.value_or(0)); 102 LogSetting("System_RngSeed", Settings::values.rng_seed.GetValue().value_or(0));
102 LogSetting("System_CurrentUser", Settings::values.current_user); 103 LogSetting("System_CurrentUser", Settings::values.current_user);
103 LogSetting("System_LanguageIndex", Settings::values.language_index); 104 LogSetting("System_LanguageIndex", Settings::values.language_index.GetValue());
104 LogSetting("System_RegionIndex", Settings::values.region_index); 105 LogSetting("System_RegionIndex", Settings::values.region_index.GetValue());
105 LogSetting("System_TimeZoneIndex", Settings::values.time_zone_index); 106 LogSetting("System_TimeZoneIndex", Settings::values.time_zone_index.GetValue());
106 LogSetting("Core_UseMultiCore", Settings::values.use_multi_core); 107 LogSetting("Core_UseMultiCore", Settings::values.use_multi_core.GetValue());
107 LogSetting("Renderer_UseResolutionFactor", Settings::values.resolution_factor); 108 LogSetting("Renderer_UseResolutionFactor", Settings::values.resolution_factor.GetValue());
108 LogSetting("Renderer_UseFrameLimit", Settings::values.use_frame_limit); 109 LogSetting("Renderer_UseFrameLimit", Settings::values.use_frame_limit.GetValue());
109 LogSetting("Renderer_FrameLimit", Settings::values.frame_limit); 110 LogSetting("Renderer_FrameLimit", Settings::values.frame_limit.GetValue());
110 LogSetting("Renderer_UseDiskShaderCache", Settings::values.use_disk_shader_cache); 111 LogSetting("Renderer_UseDiskShaderCache", Settings::values.use_disk_shader_cache.GetValue());
111 LogSetting("Renderer_GPUAccuracyLevel", Settings::values.gpu_accuracy); 112 LogSetting("Renderer_GPUAccuracyLevel", Settings::values.gpu_accuracy.GetValue());
112 LogSetting("Renderer_UseAsynchronousGpuEmulation", 113 LogSetting("Renderer_UseAsynchronousGpuEmulation",
113 Settings::values.use_asynchronous_gpu_emulation); 114 Settings::values.use_asynchronous_gpu_emulation.GetValue());
114 LogSetting("Renderer_UseVsync", Settings::values.use_vsync); 115 LogSetting("Renderer_UseVsync", Settings::values.use_vsync.GetValue());
115 LogSetting("Renderer_UseAssemblyShaders", Settings::values.use_assembly_shaders); 116 LogSetting("Renderer_UseAssemblyShaders", Settings::values.use_assembly_shaders.GetValue());
116 LogSetting("Renderer_AnisotropicFilteringLevel", Settings::values.max_anisotropy); 117 LogSetting("Renderer_AnisotropicFilteringLevel", Settings::values.max_anisotropy.GetValue());
117 LogSetting("Audio_OutputEngine", Settings::values.sink_id); 118 LogSetting("Audio_OutputEngine", Settings::values.sink_id);
118 LogSetting("Audio_EnableAudioStretching", Settings::values.enable_audio_stretching); 119 LogSetting("Audio_EnableAudioStretching", Settings::values.enable_audio_stretching.GetValue());
119 LogSetting("Audio_OutputDevice", Settings::values.audio_device_id); 120 LogSetting("Audio_OutputDevice", Settings::values.audio_device_id);
120 LogSetting("DataStorage_UseVirtualSd", Settings::values.use_virtual_sd); 121 LogSetting("DataStorage_UseVirtualSd", Settings::values.use_virtual_sd);
121 LogSetting("DataStorage_NandDir", FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)); 122 LogSetting("DataStorage_NandDir", FileUtil::GetUserPath(FileUtil::UserPath::NANDDir));
@@ -131,15 +132,56 @@ float Volume() {
131 if (values.audio_muted) { 132 if (values.audio_muted) {
132 return 0.0f; 133 return 0.0f;
133 } 134 }
134 return values.volume; 135 return values.volume.GetValue();
135} 136}
136 137
137bool IsGPULevelExtreme() { 138bool IsGPULevelExtreme() {
138 return values.gpu_accuracy == GPUAccuracy::Extreme; 139 return values.gpu_accuracy.GetValue() == GPUAccuracy::Extreme;
139} 140}
140 141
141bool IsGPULevelHigh() { 142bool IsGPULevelHigh() {
142 return values.gpu_accuracy == GPUAccuracy::Extreme || values.gpu_accuracy == GPUAccuracy::High; 143 return values.gpu_accuracy.GetValue() == GPUAccuracy::Extreme ||
144 values.gpu_accuracy.GetValue() == GPUAccuracy::High;
145}
146
147void RestoreGlobalState() {
148 // If a game is running, DO NOT restore the global settings state
149 if (Core::System::GetInstance().IsPoweredOn()) {
150 return;
151 }
152
153 // Audio
154 values.enable_audio_stretching.SetGlobal(true);
155 values.volume.SetGlobal(true);
156
157 // Core
158 values.use_multi_core.SetGlobal(true);
159
160 // Renderer
161 values.renderer_backend.SetGlobal(true);
162 values.vulkan_device.SetGlobal(true);
163 values.aspect_ratio.SetGlobal(true);
164 values.max_anisotropy.SetGlobal(true);
165 values.use_frame_limit.SetGlobal(true);
166 values.frame_limit.SetGlobal(true);
167 values.use_disk_shader_cache.SetGlobal(true);
168 values.gpu_accuracy.SetGlobal(true);
169 values.use_asynchronous_gpu_emulation.SetGlobal(true);
170 values.use_vsync.SetGlobal(true);
171 values.use_assembly_shaders.SetGlobal(true);
172 values.use_fast_gpu_time.SetGlobal(true);
173 values.force_30fps_mode.SetGlobal(true);
174 values.bg_red.SetGlobal(true);
175 values.bg_green.SetGlobal(true);
176 values.bg_blue.SetGlobal(true);
177
178 // System
179 values.language_index.SetGlobal(true);
180 values.region_index.SetGlobal(true);
181 values.time_zone_index.SetGlobal(true);
182 values.rng_seed.SetGlobal(true);
183 values.custom_rtc.SetGlobal(true);
184 values.sound_index.SetGlobal(true);
143} 185}
144 186
145} // namespace Settings 187} // namespace Settings
diff --git a/src/core/settings.h b/src/core/settings.h
index a598ccbc1..850ca4072 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -382,20 +382,85 @@ enum class GPUAccuracy : u32 {
382 Extreme = 2, 382 Extreme = 2,
383}; 383};
384 384
385extern bool configuring_global;
386
387template <typename Type>
388class Setting final {
389public:
390 Setting() = default;
391 explicit Setting(Type val) : global{val} {}
392 ~Setting() = default;
393 void SetGlobal(bool to_global) {
394 use_global = to_global;
395 }
396 bool UsingGlobal() const {
397 return use_global;
398 }
399 Type GetValue(bool need_global = false) const {
400 if (use_global || need_global) {
401 return global;
402 }
403 return local;
404 }
405 void SetValue(const Type& value) {
406 if (use_global) {
407 global = value;
408 } else {
409 local = value;
410 }
411 }
412
413private:
414 bool use_global = true;
415 Type global{};
416 Type local{};
417};
418
385struct Values { 419struct Values {
420 // Audio
421 std::string audio_device_id;
422 std::string sink_id;
423 bool audio_muted;
424 Setting<bool> enable_audio_stretching;
425 Setting<float> volume;
426
427 // Core
428 Setting<bool> use_multi_core;
429
430 // Renderer
431 Setting<RendererBackend> renderer_backend;
432 bool renderer_debug;
433 Setting<int> vulkan_device;
434
435 Setting<u16> resolution_factor = Setting(static_cast<u16>(1));
436 Setting<int> aspect_ratio;
437 Setting<int> max_anisotropy;
438 Setting<bool> use_frame_limit;
439 Setting<u16> frame_limit;
440 Setting<bool> use_disk_shader_cache;
441 Setting<GPUAccuracy> gpu_accuracy;
442 Setting<bool> use_asynchronous_gpu_emulation;
443 Setting<bool> use_vsync;
444 Setting<bool> use_assembly_shaders;
445 Setting<bool> force_30fps_mode;
446 Setting<bool> use_fast_gpu_time;
447
448 Setting<float> bg_red;
449 Setting<float> bg_green;
450 Setting<float> bg_blue;
451
386 // System 452 // System
387 bool use_docked_mode; 453 Setting<std::optional<u32>> rng_seed;
388 std::optional<u32> rng_seed;
389 // Measured in seconds since epoch 454 // Measured in seconds since epoch
390 std::optional<std::chrono::seconds> custom_rtc; 455 Setting<std::optional<std::chrono::seconds>> custom_rtc;
391 // Set on game boot, reset on stop. Seconds difference between current time and `custom_rtc` 456 // Set on game boot, reset on stop. Seconds difference between current time and `custom_rtc`
392 std::chrono::seconds custom_rtc_differential; 457 std::chrono::seconds custom_rtc_differential;
393 458
394 s32 current_user; 459 s32 current_user;
395 s32 language_index; 460 Setting<s32> language_index;
396 s32 region_index; 461 Setting<s32> region_index;
397 s32 time_zone_index; 462 Setting<s32> time_zone_index;
398 s32 sound_index; 463 Setting<s32> sound_index;
399 464
400 // Controls 465 // Controls
401 std::array<PlayerInput, 10> players; 466 std::array<PlayerInput, 10> players;
@@ -419,8 +484,7 @@ struct Values {
419 u16 udp_input_port; 484 u16 udp_input_port;
420 u8 udp_pad_index; 485 u8 udp_pad_index;
421 486
422 // Core 487 bool use_docked_mode;
423 bool use_multi_core;
424 488
425 // Data Storage 489 // Data Storage
426 bool use_virtual_sd; 490 bool use_virtual_sd;
@@ -432,39 +496,6 @@ struct Values {
432 NANDUserSize nand_user_size; 496 NANDUserSize nand_user_size;
433 SDMCSize sdmc_size; 497 SDMCSize sdmc_size;
434 498
435 // Renderer
436 RendererBackend renderer_backend;
437 bool renderer_debug;
438 int vulkan_device;
439
440 u16 resolution_factor{1};
441 int aspect_ratio;
442 int max_anisotropy;
443 bool use_frame_limit;
444 u16 frame_limit;
445 bool use_disk_shader_cache;
446 GPUAccuracy gpu_accuracy;
447 bool use_asynchronous_gpu_emulation;
448 bool use_vsync;
449 bool use_assembly_shaders;
450 bool force_30fps_mode;
451 bool use_fast_gpu_time;
452
453 float bg_red;
454 float bg_green;
455 float bg_blue;
456
457 std::string log_filter;
458
459 bool use_dev_keys;
460
461 // Audio
462 bool audio_muted;
463 std::string sink_id;
464 bool enable_audio_stretching;
465 std::string audio_device_id;
466 float volume;
467
468 // Debugging 499 // Debugging
469 bool record_frame_times; 500 bool record_frame_times;
470 bool use_gdbstub; 501 bool use_gdbstub;
@@ -477,7 +508,11 @@ struct Values {
477 bool disable_cpu_opt; 508 bool disable_cpu_opt;
478 bool disable_macro_jit; 509 bool disable_macro_jit;
479 510
480 // BCAT 511 // Misceallaneous
512 std::string log_filter;
513 bool use_dev_keys;
514
515 // Services
481 std::string bcat_backend; 516 std::string bcat_backend;
482 bool bcat_boxcat_local; 517 bool bcat_boxcat_local;
483 518
@@ -501,4 +536,7 @@ std::string GetTimeZoneString();
501void Apply(); 536void Apply();
502void LogSettings(); 537void LogSettings();
503 538
539// Restore the global state of all applicable settings in the Values struct
540void RestoreGlobalState();
541
504} // namespace Settings 542} // namespace Settings
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index c781b3cfc..78915e6db 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -189,19 +189,24 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) {
189 // Log user configuration information 189 // Log user configuration information
190 constexpr auto field_type = Telemetry::FieldType::UserConfig; 190 constexpr auto field_type = Telemetry::FieldType::UserConfig;
191 AddField(field_type, "Audio_SinkId", Settings::values.sink_id); 191 AddField(field_type, "Audio_SinkId", Settings::values.sink_id);
192 AddField(field_type, "Audio_EnableAudioStretching", Settings::values.enable_audio_stretching); 192 AddField(field_type, "Audio_EnableAudioStretching",
193 AddField(field_type, "Core_UseMultiCore", Settings::values.use_multi_core); 193 Settings::values.enable_audio_stretching.GetValue());
194 AddField(field_type, "Renderer_Backend", TranslateRenderer(Settings::values.renderer_backend)); 194 AddField(field_type, "Core_UseMultiCore", Settings::values.use_multi_core.GetValue());
195 AddField(field_type, "Renderer_ResolutionFactor", Settings::values.resolution_factor); 195 AddField(field_type, "Renderer_Backend",
196 AddField(field_type, "Renderer_UseFrameLimit", Settings::values.use_frame_limit); 196 TranslateRenderer(Settings::values.renderer_backend.GetValue()));
197 AddField(field_type, "Renderer_FrameLimit", Settings::values.frame_limit); 197 AddField(field_type, "Renderer_ResolutionFactor",
198 AddField(field_type, "Renderer_UseDiskShaderCache", Settings::values.use_disk_shader_cache); 198 Settings::values.resolution_factor.GetValue());
199 AddField(field_type, "Renderer_UseFrameLimit", Settings::values.use_frame_limit.GetValue());
200 AddField(field_type, "Renderer_FrameLimit", Settings::values.frame_limit.GetValue());
201 AddField(field_type, "Renderer_UseDiskShaderCache",
202 Settings::values.use_disk_shader_cache.GetValue());
199 AddField(field_type, "Renderer_GPUAccuracyLevel", 203 AddField(field_type, "Renderer_GPUAccuracyLevel",
200 TranslateGPUAccuracyLevel(Settings::values.gpu_accuracy)); 204 TranslateGPUAccuracyLevel(Settings::values.gpu_accuracy.GetValue()));
201 AddField(field_type, "Renderer_UseAsynchronousGpuEmulation", 205 AddField(field_type, "Renderer_UseAsynchronousGpuEmulation",
202 Settings::values.use_asynchronous_gpu_emulation); 206 Settings::values.use_asynchronous_gpu_emulation.GetValue());
203 AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync); 207 AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync.GetValue());
204 AddField(field_type, "Renderer_UseAssemblyShaders", Settings::values.use_assembly_shaders); 208 AddField(field_type, "Renderer_UseAssemblyShaders",
209 Settings::values.use_assembly_shaders.GetValue());
205 AddField(field_type, "System_UseDockedMode", Settings::values.use_docked_mode); 210 AddField(field_type, "System_UseDockedMode", Settings::values.use_docked_mode);
206} 211}
207 212
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index c6479af9f..dd7ce8c99 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -96,7 +96,8 @@ public:
96 } 96 }
97 if (is_written) { 97 if (is_written) {
98 map->MarkAsModified(true, GetModifiedTicks()); 98 map->MarkAsModified(true, GetModifiedTicks());
99 if (Settings::IsGPULevelHigh() && Settings::values.use_asynchronous_gpu_emulation) { 99 if (Settings::IsGPULevelHigh() &&
100 Settings::values.use_asynchronous_gpu_emulation.GetValue()) {
100 MarkForAsyncFlush(map); 101 MarkForAsyncFlush(map);
101 } 102 }
102 if (!map->is_written) { 103 if (!map->is_written) {
@@ -369,7 +370,8 @@ private:
369 } 370 }
370 if (modified_inheritance) { 371 if (modified_inheritance) {
371 map->MarkAsModified(true, GetModifiedTicks()); 372 map->MarkAsModified(true, GetModifiedTicks());
372 if (Settings::IsGPULevelHigh() && Settings::values.use_asynchronous_gpu_emulation) { 373 if (Settings::IsGPULevelHigh() &&
374 Settings::values.use_asynchronous_gpu_emulation.GetValue()) {
373 MarkForAsyncFlush(map); 375 MarkForAsyncFlush(map);
374 } 376 }
375 } 377 }
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 482e49711..758bfe148 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -157,7 +157,7 @@ u64 GPU::GetTicks() const {
157 constexpr u64 gpu_ticks_den = 625; 157 constexpr u64 gpu_ticks_den = 625;
158 158
159 u64 nanoseconds = system.CoreTiming().GetGlobalTimeNs().count(); 159 u64 nanoseconds = system.CoreTiming().GetGlobalTimeNs().count();
160 if (Settings::values.use_fast_gpu_time) { 160 if (Settings::values.use_fast_gpu_time.GetValue()) {
161 nanoseconds /= 256; 161 nanoseconds /= 256;
162 } 162 }
163 const u64 nanoseconds_num = nanoseconds / gpu_ticks_den; 163 const u64 nanoseconds_num = nanoseconds / gpu_ticks_den;
diff --git a/src/video_core/query_cache.h b/src/video_core/query_cache.h
index e12dab899..0d3a88765 100644
--- a/src/video_core/query_cache.h
+++ b/src/video_core/query_cache.h
@@ -132,7 +132,7 @@ public:
132 } 132 }
133 133
134 query->BindCounter(Stream(type).Current(), timestamp); 134 query->BindCounter(Stream(type).Current(), timestamp);
135 if (Settings::values.use_asynchronous_gpu_emulation) { 135 if (Settings::values.use_asynchronous_gpu_emulation.GetValue()) {
136 AsyncFlushQuery(cpu_addr); 136 AsyncFlushQuery(cpu_addr);
137 } 137 }
138 } 138 }
diff --git a/src/video_core/renderer_base.cpp b/src/video_core/renderer_base.cpp
index 919d1f2d4..dfb06e87e 100644
--- a/src/video_core/renderer_base.cpp
+++ b/src/video_core/renderer_base.cpp
@@ -18,7 +18,7 @@ RendererBase::~RendererBase() = default;
18void RendererBase::RefreshBaseSettings() { 18void RendererBase::RefreshBaseSettings() {
19 UpdateCurrentFramebufferLayout(); 19 UpdateCurrentFramebufferLayout();
20 20
21 renderer_settings.use_framelimiter = Settings::values.use_frame_limit; 21 renderer_settings.use_framelimiter = Settings::values.use_frame_limit.GetValue();
22 renderer_settings.set_background_color = true; 22 renderer_settings.set_background_color = true;
23} 23}
24 24
diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp
index 208fc6167..c1f20f0ab 100644
--- a/src/video_core/renderer_opengl/gl_device.cpp
+++ b/src/video_core/renderer_opengl/gl_device.cpp
@@ -229,15 +229,15 @@ Device::Device()
229 // uniform buffers as "push constants" 229 // uniform buffers as "push constants"
230 has_fast_buffer_sub_data = is_nvidia && !disable_fast_buffer_sub_data; 230 has_fast_buffer_sub_data = is_nvidia && !disable_fast_buffer_sub_data;
231 231
232 use_assembly_shaders = Settings::values.use_assembly_shaders && GLAD_GL_NV_gpu_program5 && 232 use_assembly_shaders = Settings::values.use_assembly_shaders.GetValue() &&
233 GLAD_GL_NV_compute_program5 && GLAD_GL_NV_transform_feedback && 233 GLAD_GL_NV_gpu_program5 && GLAD_GL_NV_compute_program5 &&
234 GLAD_GL_NV_transform_feedback2; 234 GLAD_GL_NV_transform_feedback && GLAD_GL_NV_transform_feedback2;
235 235
236 LOG_INFO(Render_OpenGL, "Renderer_VariableAOFFI: {}", has_variable_aoffi); 236 LOG_INFO(Render_OpenGL, "Renderer_VariableAOFFI: {}", has_variable_aoffi);
237 LOG_INFO(Render_OpenGL, "Renderer_ComponentIndexingBug: {}", has_component_indexing_bug); 237 LOG_INFO(Render_OpenGL, "Renderer_ComponentIndexingBug: {}", has_component_indexing_bug);
238 LOG_INFO(Render_OpenGL, "Renderer_PreciseBug: {}", has_precise_bug); 238 LOG_INFO(Render_OpenGL, "Renderer_PreciseBug: {}", has_precise_bug);
239 239
240 if (Settings::values.use_assembly_shaders && !use_assembly_shaders) { 240 if (Settings::values.use_assembly_shaders.GetValue() && !use_assembly_shaders) {
241 LOG_ERROR(Render_OpenGL, "Assembly shaders enabled but not supported"); 241 LOG_ERROR(Render_OpenGL, "Assembly shaders enabled but not supported");
242 } 242 }
243} 243}
diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
index 653c3f2f9..2dcc2b0eb 100644
--- a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
@@ -213,7 +213,7 @@ ShaderDiskCacheOpenGL::~ShaderDiskCacheOpenGL() = default;
213std::optional<std::vector<ShaderDiskCacheEntry>> ShaderDiskCacheOpenGL::LoadTransferable() { 213std::optional<std::vector<ShaderDiskCacheEntry>> ShaderDiskCacheOpenGL::LoadTransferable() {
214 // Skip games without title id 214 // Skip games without title id
215 const bool has_title_id = system.CurrentProcess()->GetTitleID() != 0; 215 const bool has_title_id = system.CurrentProcess()->GetTitleID() != 0;
216 if (!Settings::values.use_disk_shader_cache || !has_title_id) { 216 if (!Settings::values.use_disk_shader_cache.GetValue() || !has_title_id) {
217 return {}; 217 return {};
218 } 218 }
219 219
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index c40adb6e7..e66cdc083 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -455,8 +455,8 @@ void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color
455void RendererOpenGL::InitOpenGLObjects() { 455void RendererOpenGL::InitOpenGLObjects() {
456 frame_mailbox = std::make_unique<FrameMailbox>(); 456 frame_mailbox = std::make_unique<FrameMailbox>();
457 457
458 glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue, 458 glClearColor(Settings::values.bg_red.GetValue(), Settings::values.bg_green.GetValue(),
459 0.0f); 459 Settings::values.bg_blue.GetValue(), 0.0f);
460 460
461 // Create shader programs 461 // Create shader programs
462 OGLShader vertex_shader; 462 OGLShader vertex_shader;
@@ -561,8 +561,8 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
561void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) { 561void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
562 if (renderer_settings.set_background_color) { 562 if (renderer_settings.set_background_color) {
563 // Update background color before drawing 563 // Update background color before drawing
564 glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue, 564 glClearColor(Settings::values.bg_red.GetValue(), Settings::values.bg_green.GetValue(),
565 0.0f); 565 Settings::values.bg_blue.GetValue(), 0.0f);
566 } 566 }
567 567
568 // Set projection matrix 568 // Set projection matrix
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index 2d9b18ed9..2258479f5 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -409,7 +409,7 @@ bool RendererVulkan::PickDevices() {
409 return false; 409 return false;
410 } 410 }
411 411
412 const s32 device_index = Settings::values.vulkan_device; 412 const s32 device_index = Settings::values.vulkan_device.GetValue();
413 if (device_index < 0 || device_index >= static_cast<s32>(devices->size())) { 413 if (device_index < 0 || device_index >= static_cast<s32>(devices->size())) {
414 LOG_ERROR(Render_Vulkan, "Invalid device index {}!", device_index); 414 LOG_ERROR(Render_Vulkan, "Invalid device index {}!", device_index);
415 return false; 415 return false;
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index 6207d8dfe..cdcddb225 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -249,7 +249,7 @@ public:
249 auto& surface = render_targets[index].target; 249 auto& surface = render_targets[index].target;
250 surface->MarkAsRenderTarget(false, NO_RT); 250 surface->MarkAsRenderTarget(false, NO_RT);
251 const auto& cr_params = surface->GetSurfaceParams(); 251 const auto& cr_params = surface->GetSurfaceParams();
252 if (!cr_params.is_tiled && Settings::values.use_asynchronous_gpu_emulation) { 252 if (!cr_params.is_tiled && Settings::values.use_asynchronous_gpu_emulation.GetValue()) {
253 AsyncFlushSurface(surface); 253 AsyncFlushSurface(surface);
254 } 254 }
255 } 255 }
diff --git a/src/video_core/textures/texture.cpp b/src/video_core/textures/texture.cpp
index d1939d744..4171e3ef2 100644
--- a/src/video_core/textures/texture.cpp
+++ b/src/video_core/textures/texture.cpp
@@ -48,7 +48,7 @@ constexpr std::array<float, 256> SRGB_CONVERSION_LUT = {
48}; 48};
49 49
50unsigned SettingsMinimumAnisotropy() noexcept { 50unsigned SettingsMinimumAnisotropy() noexcept {
51 switch (static_cast<Anisotropy>(Settings::values.max_anisotropy)) { 51 switch (static_cast<Anisotropy>(Settings::values.max_anisotropy.GetValue())) {
52 default: 52 default:
53 case Anisotropy::Default: 53 case Anisotropy::Default:
54 return 1U; 54 return 1U;
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp
index f60bdc60a..45f360bdd 100644
--- a/src/video_core/video_core.cpp
+++ b/src/video_core/video_core.cpp
@@ -19,7 +19,7 @@ namespace {
19std::unique_ptr<VideoCore::RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window, 19std::unique_ptr<VideoCore::RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window,
20 Core::System& system, 20 Core::System& system,
21 Core::Frontend::GraphicsContext& context) { 21 Core::Frontend::GraphicsContext& context) {
22 switch (Settings::values.renderer_backend) { 22 switch (Settings::values.renderer_backend.GetValue()) {
23 case Settings::RendererBackend::OpenGL: 23 case Settings::RendererBackend::OpenGL:
24 return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system, context); 24 return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system, context);
25#ifdef HAS_VULKAN 25#ifdef HAS_VULKAN
@@ -42,7 +42,7 @@ std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Cor
42 return nullptr; 42 return nullptr;
43 } 43 }
44 44
45 if (Settings::values.use_asynchronous_gpu_emulation) { 45 if (Settings::values.use_asynchronous_gpu_emulation.GetValue()) {
46 return std::make_unique<VideoCommon::GPUAsynch>(system, std::move(renderer), 46 return std::make_unique<VideoCommon::GPUAsynch>(system, std::move(renderer),
47 std::move(context)); 47 std::move(context));
48 } 48 }
@@ -51,8 +51,8 @@ std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Cor
51 51
52u16 GetResolutionScaleFactor(const RendererBase& renderer) { 52u16 GetResolutionScaleFactor(const RendererBase& renderer) {
53 return static_cast<u16>( 53 return static_cast<u16>(
54 Settings::values.resolution_factor != 0 54 Settings::values.resolution_factor.GetValue() != 0
55 ? Settings::values.resolution_factor 55 ? Settings::values.resolution_factor.GetValue()
56 : renderer.GetRenderWindow().GetFramebufferLayout().GetScalingRatio()); 56 : renderer.GetRenderWindow().GetFramebufferLayout().GetScalingRatio());
57} 57}
58 58
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 75c27e39e..742b72856 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -24,6 +24,8 @@ add_executable(yuzu
24 compatibility_list.h 24 compatibility_list.h
25 configuration/config.cpp 25 configuration/config.cpp
26 configuration/config.h 26 configuration/config.h
27 configuration/configuration_shared.cpp
28 configuration/configuration_shared.h
27 configuration/configure.ui 29 configuration/configure.ui
28 configuration/configure_audio.cpp 30 configuration/configure_audio.cpp
29 configuration/configure_audio.h 31 configuration/configure_audio.h
@@ -60,9 +62,12 @@ add_executable(yuzu
60 configuration/configure_mouse_advanced.cpp 62 configuration/configure_mouse_advanced.cpp
61 configuration/configure_mouse_advanced.h 63 configuration/configure_mouse_advanced.h
62 configuration/configure_mouse_advanced.ui 64 configuration/configure_mouse_advanced.ui
63 configuration/configure_per_general.cpp 65 configuration/configure_per_game.cpp
64 configuration/configure_per_general.h 66 configuration/configure_per_game.h
65 configuration/configure_per_general.ui 67 configuration/configure_per_game.ui
68 configuration/configure_per_game_addons.cpp
69 configuration/configure_per_game_addons.h
70 configuration/configure_per_game_addons.ui
66 configuration/configure_profile_manager.cpp 71 configuration/configure_profile_manager.cpp
67 configuration/configure_profile_manager.h 72 configuration/configure_profile_manager.h
68 configuration/configure_profile_manager.ui 73 configuration/configure_profile_manager.ui
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 4bfce48a4..5738787ac 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -145,7 +145,7 @@ public:
145 145
146 // disable vsync for any shared contexts 146 // disable vsync for any shared contexts
147 auto format = share_context->format(); 147 auto format = share_context->format();
148 format.setSwapInterval(main_surface ? Settings::values.use_vsync : 0); 148 format.setSwapInterval(main_surface ? Settings::values.use_vsync.GetValue() : 0);
149 149
150 context = std::make_unique<QOpenGLContext>(); 150 context = std::make_unique<QOpenGLContext>();
151 context->setShareContext(share_context); 151 context->setShareContext(share_context);
@@ -495,7 +495,7 @@ void GRenderWindow::resizeEvent(QResizeEvent* event) {
495 495
496std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const { 496std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const {
497#ifdef HAS_OPENGL 497#ifdef HAS_OPENGL
498 if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) { 498 if (Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::OpenGL) {
499 auto c = static_cast<OpenGLSharedContext*>(main_context.get()); 499 auto c = static_cast<OpenGLSharedContext*>(main_context.get());
500 // Bind the shared contexts to the main surface in case the backend wants to take over 500 // Bind the shared contexts to the main surface in case the backend wants to take over
501 // presentation 501 // presentation
@@ -511,7 +511,7 @@ bool GRenderWindow::InitRenderTarget() {
511 511
512 first_frame = false; 512 first_frame = false;
513 513
514 switch (Settings::values.renderer_backend) { 514 switch (Settings::values.renderer_backend.GetValue()) {
515 case Settings::RendererBackend::OpenGL: 515 case Settings::RendererBackend::OpenGL:
516 if (!InitializeOpenGL()) { 516 if (!InitializeOpenGL()) {
517 return false; 517 return false;
@@ -538,7 +538,7 @@ bool GRenderWindow::InitRenderTarget() {
538 OnFramebufferSizeChanged(); 538 OnFramebufferSizeChanged();
539 BackupGeometry(); 539 BackupGeometry();
540 540
541 if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) { 541 if (Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::OpenGL) {
542 if (!LoadOpenGL()) { 542 if (!LoadOpenGL()) {
543 return false; 543 return false;
544 } 544 }
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 5e0d0e7af..1b2b1b2bb 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -13,17 +13,20 @@
13#include "input_common/udp/client.h" 13#include "input_common/udp/client.h"
14#include "yuzu/configuration/config.h" 14#include "yuzu/configuration/config.h"
15 15
16Config::Config() { 16Config::Config(const std::string& config_file, bool is_global) {
17 // TODO: Don't hardcode the path; let the frontend decide where to put the config files. 17 // TODO: Don't hardcode the path; let the frontend decide where to put the config files.
18 qt_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "qt-config.ini"; 18 qt_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + config_file;
19 FileUtil::CreateFullPath(qt_config_loc); 19 FileUtil::CreateFullPath(qt_config_loc);
20 qt_config = 20 qt_config =
21 std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat); 21 std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat);
22 global = is_global;
22 Reload(); 23 Reload();
23} 24}
24 25
25Config::~Config() { 26Config::~Config() {
26 Save(); 27 if (global) {
28 Save();
29 }
27} 30}
28 31
29const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = { 32const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = {
@@ -402,16 +405,19 @@ void Config::ApplyDefaultProfileIfInputInvalid() {
402void Config::ReadAudioValues() { 405void Config::ReadAudioValues() {
403 qt_config->beginGroup(QStringLiteral("Audio")); 406 qt_config->beginGroup(QStringLiteral("Audio"));
404 407
405 Settings::values.sink_id = ReadSetting(QStringLiteral("output_engine"), QStringLiteral("auto")) 408 if (global) {
406 .toString() 409 Settings::values.sink_id =
407 .toStdString(); 410 ReadSetting(QStringLiteral("output_engine"), QStringLiteral("auto"))
408 Settings::values.enable_audio_stretching = 411 .toString()
409 ReadSetting(QStringLiteral("enable_audio_stretching"), true).toBool(); 412 .toStdString();
410 Settings::values.audio_device_id = 413 Settings::values.audio_device_id =
411 ReadSetting(QStringLiteral("output_device"), QStringLiteral("auto")) 414 ReadSetting(QStringLiteral("output_device"), QStringLiteral("auto"))
412 .toString() 415 .toString()
413 .toStdString(); 416 .toStdString();
414 Settings::values.volume = ReadSetting(QStringLiteral("volume"), 1).toFloat(); 417 }
418 ReadSettingGlobal(Settings::values.enable_audio_stretching,
419 QStringLiteral("enable_audio_stretching"), true);
420 ReadSettingGlobal(Settings::values.volume, QStringLiteral("volume"), 1);
415 421
416 qt_config->endGroup(); 422 qt_config->endGroup();
417} 423}
@@ -440,6 +446,8 @@ void Config::ReadControlValues() {
440 .toInt()); 446 .toInt());
441 Settings::values.udp_pad_index = 447 Settings::values.udp_pad_index =
442 static_cast<u8>(ReadSetting(QStringLiteral("udp_pad_index"), 0).toUInt()); 448 static_cast<u8>(ReadSetting(QStringLiteral("udp_pad_index"), 0).toUInt());
449 Settings::values.use_docked_mode =
450 ReadSetting(QStringLiteral("use_docked_mode"), false).toBool();
443 451
444 qt_config->endGroup(); 452 qt_config->endGroup();
445} 453}
@@ -447,7 +455,7 @@ void Config::ReadControlValues() {
447void Config::ReadCoreValues() { 455void Config::ReadCoreValues() {
448 qt_config->beginGroup(QStringLiteral("Core")); 456 qt_config->beginGroup(QStringLiteral("Core"));
449 457
450 Settings::values.use_multi_core = ReadSetting(QStringLiteral("use_multi_core"), false).toBool(); 458 ReadSettingGlobal(Settings::values.use_multi_core, QStringLiteral("use_multi_core"), false);
451 459
452 qt_config->endGroup(); 460 qt_config->endGroup();
453} 461}
@@ -628,32 +636,28 @@ void Config::ReadPathValues() {
628void Config::ReadRendererValues() { 636void Config::ReadRendererValues() {
629 qt_config->beginGroup(QStringLiteral("Renderer")); 637 qt_config->beginGroup(QStringLiteral("Renderer"));
630 638
631 Settings::values.renderer_backend = 639 ReadSettingGlobal(Settings::values.renderer_backend, QStringLiteral("backend"), 0);
632 static_cast<Settings::RendererBackend>(ReadSetting(QStringLiteral("backend"), 0).toInt()); 640 ReadSettingGlobal(Settings::values.renderer_debug, QStringLiteral("debug"), false);
633 Settings::values.renderer_debug = ReadSetting(QStringLiteral("debug"), false).toBool(); 641 ReadSettingGlobal(Settings::values.vulkan_device, QStringLiteral("vulkan_device"), 0);
634 Settings::values.vulkan_device = ReadSetting(QStringLiteral("vulkan_device"), 0).toInt(); 642 ReadSettingGlobal(Settings::values.aspect_ratio, QStringLiteral("aspect_ratio"), 0);
635 Settings::values.aspect_ratio = ReadSetting(QStringLiteral("aspect_ratio"), 0).toInt(); 643 ReadSettingGlobal(Settings::values.max_anisotropy, QStringLiteral("max_anisotropy"), 0);
636 Settings::values.max_anisotropy = ReadSetting(QStringLiteral("max_anisotropy"), 0).toInt(); 644 ReadSettingGlobal(Settings::values.use_frame_limit, QStringLiteral("use_frame_limit"), true);
637 Settings::values.use_frame_limit = 645 ReadSettingGlobal(Settings::values.frame_limit, QStringLiteral("frame_limit"), 100);
638 ReadSetting(QStringLiteral("use_frame_limit"), true).toBool(); 646 ReadSettingGlobal(Settings::values.use_disk_shader_cache,
639 Settings::values.frame_limit = ReadSetting(QStringLiteral("frame_limit"), 100).toUInt(); 647 QStringLiteral("use_disk_shader_cache"), true);
640 Settings::values.use_disk_shader_cache = 648 ReadSettingGlobal(Settings::values.gpu_accuracy, QStringLiteral("gpu_accuracy"), 0);
641 ReadSetting(QStringLiteral("use_disk_shader_cache"), true).toBool(); 649 ReadSettingGlobal(Settings::values.use_asynchronous_gpu_emulation,
642 const int gpu_accuracy_level = ReadSetting(QStringLiteral("gpu_accuracy"), 0).toInt(); 650 QStringLiteral("use_asynchronous_gpu_emulation"), false);
643 Settings::values.gpu_accuracy = static_cast<Settings::GPUAccuracy>(gpu_accuracy_level); 651 ReadSettingGlobal(Settings::values.use_vsync, QStringLiteral("use_vsync"), true);
644 Settings::values.use_asynchronous_gpu_emulation = 652 ReadSettingGlobal(Settings::values.use_assembly_shaders, QStringLiteral("use_assembly_shaders"),
645 ReadSetting(QStringLiteral("use_asynchronous_gpu_emulation"), false).toBool(); 653 false);
646 Settings::values.use_vsync = ReadSetting(QStringLiteral("use_vsync"), true).toBool(); 654 ReadSettingGlobal(Settings::values.use_fast_gpu_time, QStringLiteral("use_fast_gpu_time"),
647 Settings::values.use_assembly_shaders = 655 true);
648 ReadSetting(QStringLiteral("use_assembly_shaders"), false).toBool(); 656 ReadSettingGlobal(Settings::values.force_30fps_mode, QStringLiteral("force_30fps_mode"), false);
649 Settings::values.use_fast_gpu_time = 657
650 ReadSetting(QStringLiteral("use_fast_gpu_time"), true).toBool(); 658 ReadSettingGlobal(Settings::values.bg_red, QStringLiteral("bg_red"), 0.0);
651 Settings::values.force_30fps_mode = 659 ReadSettingGlobal(Settings::values.bg_green, QStringLiteral("bg_green"), 0.0);
652 ReadSetting(QStringLiteral("force_30fps_mode"), false).toBool(); 660 ReadSettingGlobal(Settings::values.bg_blue, QStringLiteral("bg_blue"), 0.0);
653
654 Settings::values.bg_red = ReadSetting(QStringLiteral("bg_red"), 0.0).toFloat();
655 Settings::values.bg_green = ReadSetting(QStringLiteral("bg_green"), 0.0).toFloat();
656 Settings::values.bg_blue = ReadSetting(QStringLiteral("bg_blue"), 0.0).toFloat();
657 661
658 qt_config->endGroup(); 662 qt_config->endGroup();
659} 663}
@@ -682,35 +686,45 @@ void Config::ReadShortcutValues() {
682void Config::ReadSystemValues() { 686void Config::ReadSystemValues() {
683 qt_config->beginGroup(QStringLiteral("System")); 687 qt_config->beginGroup(QStringLiteral("System"));
684 688
685 Settings::values.use_docked_mode = 689 ReadSettingGlobal(Settings::values.current_user, QStringLiteral("current_user"), 0);
686 ReadSetting(QStringLiteral("use_docked_mode"), false).toBool(); 690 Settings::values.current_user =
687 691 std::clamp<int>(Settings::values.current_user, 0, Service::Account::MAX_USERS - 1);
688 Settings::values.current_user = std::clamp<int>(
689 ReadSetting(QStringLiteral("current_user"), 0).toInt(), 0, Service::Account::MAX_USERS - 1);
690 692
691 Settings::values.language_index = ReadSetting(QStringLiteral("language_index"), 1).toInt(); 693 ReadSettingGlobal(Settings::values.language_index, QStringLiteral("language_index"), 1);
692 694
693 Settings::values.region_index = ReadSetting(QStringLiteral("region_index"), 1).toInt(); 695 ReadSettingGlobal(Settings::values.region_index, QStringLiteral("region_index"), 1);
694 696
695 Settings::values.time_zone_index = ReadSetting(QStringLiteral("time_zone_index"), 0).toInt(); 697 ReadSettingGlobal(Settings::values.time_zone_index, QStringLiteral("time_zone_index"), 0);
696 698
697 const auto rng_seed_enabled = ReadSetting(QStringLiteral("rng_seed_enabled"), false).toBool(); 699 bool rng_seed_enabled;
698 if (rng_seed_enabled) { 700 ReadSettingGlobal(rng_seed_enabled, QStringLiteral("rng_seed_enabled"), false);
699 Settings::values.rng_seed = ReadSetting(QStringLiteral("rng_seed"), 0).toULongLong(); 701 bool rng_seed_global =
700 } else { 702 global || qt_config->value(QStringLiteral("rng_seed/use_global"), true).toBool();
701 Settings::values.rng_seed = std::nullopt; 703 Settings::values.rng_seed.SetGlobal(rng_seed_global);
704 if (global || !rng_seed_global) {
705 if (rng_seed_enabled) {
706 Settings::values.rng_seed.SetValue(
707 ReadSetting(QStringLiteral("rng_seed"), 0).toULongLong());
708 } else {
709 Settings::values.rng_seed.SetValue(std::nullopt);
710 }
702 } 711 }
703 712
704 const auto custom_rtc_enabled = 713 bool custom_rtc_enabled;
705 ReadSetting(QStringLiteral("custom_rtc_enabled"), false).toBool(); 714 ReadSettingGlobal(custom_rtc_enabled, QStringLiteral("custom_rtc_enabled"), false);
706 if (custom_rtc_enabled) { 715 bool custom_rtc_global =
707 Settings::values.custom_rtc = 716 global || qt_config->value(QStringLiteral("custom_rtc/use_global"), true).toBool();
708 std::chrono::seconds(ReadSetting(QStringLiteral("custom_rtc"), 0).toULongLong()); 717 Settings::values.custom_rtc.SetGlobal(custom_rtc_global);
709 } else { 718 if (global || !custom_rtc_global) {
710 Settings::values.custom_rtc = std::nullopt; 719 if (custom_rtc_enabled) {
720 Settings::values.custom_rtc.SetValue(
721 std::chrono::seconds(ReadSetting(QStringLiteral("custom_rtc"), 0).toULongLong()));
722 } else {
723 Settings::values.custom_rtc.SetValue(std::nullopt);
724 }
711 } 725 }
712 726
713 Settings::values.sound_index = ReadSetting(QStringLiteral("sound_index"), 1).toInt(); 727 ReadSettingGlobal(Settings::values.sound_index, QStringLiteral("sound_index"), 1);
714 728
715 qt_config->endGroup(); 729 qt_config->endGroup();
716} 730}
@@ -804,18 +818,20 @@ void Config::ReadWebServiceValues() {
804} 818}
805 819
806void Config::ReadValues() { 820void Config::ReadValues() {
807 ReadControlValues(); 821 if (global) {
822 ReadControlValues();
823 ReadDataStorageValues();
824 ReadDebuggingValues();
825 ReadDisabledAddOnValues();
826 ReadServiceValues();
827 ReadUIValues();
828 ReadWebServiceValues();
829 ReadMiscellaneousValues();
830 }
808 ReadCoreValues(); 831 ReadCoreValues();
809 ReadRendererValues(); 832 ReadRendererValues();
810 ReadAudioValues(); 833 ReadAudioValues();
811 ReadDataStorageValues();
812 ReadSystemValues(); 834 ReadSystemValues();
813 ReadMiscellaneousValues();
814 ReadDebuggingValues();
815 ReadWebServiceValues();
816 ReadServiceValues();
817 ReadDisabledAddOnValues();
818 ReadUIValues();
819} 835}
820 836
821void Config::SavePlayerValues() { 837void Config::SavePlayerValues() {
@@ -902,30 +918,35 @@ void Config::SaveTouchscreenValues() {
902} 918}
903 919
904void Config::SaveValues() { 920void Config::SaveValues() {
905 SaveControlValues(); 921 if (global) {
922 SaveControlValues();
923 SaveDataStorageValues();
924 SaveDebuggingValues();
925 SaveDisabledAddOnValues();
926 SaveServiceValues();
927 SaveUIValues();
928 SaveWebServiceValues();
929 SaveMiscellaneousValues();
930 }
906 SaveCoreValues(); 931 SaveCoreValues();
907 SaveRendererValues(); 932 SaveRendererValues();
908 SaveAudioValues(); 933 SaveAudioValues();
909 SaveDataStorageValues();
910 SaveSystemValues(); 934 SaveSystemValues();
911 SaveMiscellaneousValues();
912 SaveDebuggingValues();
913 SaveWebServiceValues();
914 SaveServiceValues();
915 SaveDisabledAddOnValues();
916 SaveUIValues();
917} 935}
918 936
919void Config::SaveAudioValues() { 937void Config::SaveAudioValues() {
920 qt_config->beginGroup(QStringLiteral("Audio")); 938 qt_config->beginGroup(QStringLiteral("Audio"));
921 939
922 WriteSetting(QStringLiteral("output_engine"), QString::fromStdString(Settings::values.sink_id), 940 if (global) {
923 QStringLiteral("auto")); 941 WriteSetting(QStringLiteral("output_engine"),
924 WriteSetting(QStringLiteral("enable_audio_stretching"), 942 QString::fromStdString(Settings::values.sink_id), QStringLiteral("auto"));
925 Settings::values.enable_audio_stretching, true); 943 WriteSetting(QStringLiteral("output_device"),
926 WriteSetting(QStringLiteral("output_device"), 944 QString::fromStdString(Settings::values.audio_device_id),
927 QString::fromStdString(Settings::values.audio_device_id), QStringLiteral("auto")); 945 QStringLiteral("auto"));
928 WriteSetting(QStringLiteral("volume"), Settings::values.volume, 1.0f); 946 }
947 WriteSettingGlobal(QStringLiteral("enable_audio_stretching"),
948 Settings::values.enable_audio_stretching, true);
949 WriteSettingGlobal(QStringLiteral("volume"), Settings::values.volume, 1.0f);
929 950
930 qt_config->endGroup(); 951 qt_config->endGroup();
931} 952}
@@ -948,6 +969,7 @@ void Config::SaveControlValues() {
948 WriteSetting(QStringLiteral("udp_input_port"), Settings::values.udp_input_port, 969 WriteSetting(QStringLiteral("udp_input_port"), Settings::values.udp_input_port,
949 InputCommon::CemuhookUDP::DEFAULT_PORT); 970 InputCommon::CemuhookUDP::DEFAULT_PORT);
950 WriteSetting(QStringLiteral("udp_pad_index"), Settings::values.udp_pad_index, 0); 971 WriteSetting(QStringLiteral("udp_pad_index"), Settings::values.udp_pad_index, 0);
972 WriteSetting(QStringLiteral("use_docked_mode"), Settings::values.use_docked_mode, false);
951 973
952 qt_config->endGroup(); 974 qt_config->endGroup();
953} 975}
@@ -955,7 +977,7 @@ void Config::SaveControlValues() {
955void Config::SaveCoreValues() { 977void Config::SaveCoreValues() {
956 qt_config->beginGroup(QStringLiteral("Core")); 978 qt_config->beginGroup(QStringLiteral("Core"));
957 979
958 WriteSetting(QStringLiteral("use_multi_core"), Settings::values.use_multi_core, false); 980 WriteSettingGlobal(QStringLiteral("use_multi_core"), Settings::values.use_multi_core, false);
959 981
960 qt_config->endGroup(); 982 qt_config->endGroup();
961} 983}
@@ -1078,29 +1100,34 @@ void Config::SavePathValues() {
1078void Config::SaveRendererValues() { 1100void Config::SaveRendererValues() {
1079 qt_config->beginGroup(QStringLiteral("Renderer")); 1101 qt_config->beginGroup(QStringLiteral("Renderer"));
1080 1102
1081 WriteSetting(QStringLiteral("backend"), static_cast<int>(Settings::values.renderer_backend), 0); 1103 WriteSettingGlobal(QStringLiteral("backend"),
1104 static_cast<int>(Settings::values.renderer_backend.GetValue(global)),
1105 Settings::values.renderer_backend.UsingGlobal(), 0);
1082 WriteSetting(QStringLiteral("debug"), Settings::values.renderer_debug, false); 1106 WriteSetting(QStringLiteral("debug"), Settings::values.renderer_debug, false);
1083 WriteSetting(QStringLiteral("vulkan_device"), Settings::values.vulkan_device, 0); 1107 WriteSettingGlobal(QStringLiteral("vulkan_device"), Settings::values.vulkan_device, 0);
1084 WriteSetting(QStringLiteral("aspect_ratio"), Settings::values.aspect_ratio, 0); 1108 WriteSettingGlobal(QStringLiteral("aspect_ratio"), Settings::values.aspect_ratio, 0);
1085 WriteSetting(QStringLiteral("max_anisotropy"), Settings::values.max_anisotropy, 0); 1109 WriteSettingGlobal(QStringLiteral("max_anisotropy"), Settings::values.max_anisotropy, 0);
1086 WriteSetting(QStringLiteral("use_frame_limit"), Settings::values.use_frame_limit, true); 1110 WriteSettingGlobal(QStringLiteral("use_frame_limit"), Settings::values.use_frame_limit, true);
1087 WriteSetting(QStringLiteral("frame_limit"), Settings::values.frame_limit, 100); 1111 WriteSettingGlobal(QStringLiteral("frame_limit"), Settings::values.frame_limit, 100);
1088 WriteSetting(QStringLiteral("use_disk_shader_cache"), Settings::values.use_disk_shader_cache, 1112 WriteSettingGlobal(QStringLiteral("use_disk_shader_cache"),
1089 true); 1113 Settings::values.use_disk_shader_cache, true);
1090 WriteSetting(QStringLiteral("gpu_accuracy"), static_cast<int>(Settings::values.gpu_accuracy), 1114 WriteSettingGlobal(QStringLiteral("gpu_accuracy"),
1091 0); 1115 static_cast<int>(Settings::values.gpu_accuracy.GetValue(global)),
1092 WriteSetting(QStringLiteral("use_asynchronous_gpu_emulation"), 1116 Settings::values.gpu_accuracy.UsingGlobal(), 0);
1093 Settings::values.use_asynchronous_gpu_emulation, false); 1117 WriteSettingGlobal(QStringLiteral("use_asynchronous_gpu_emulation"),
1094 WriteSetting(QStringLiteral("use_vsync"), Settings::values.use_vsync, true); 1118 Settings::values.use_asynchronous_gpu_emulation, false);
1095 WriteSetting(QStringLiteral("use_assembly_shaders"), Settings::values.use_assembly_shaders, 1119 WriteSettingGlobal(QStringLiteral("use_vsync"), Settings::values.use_vsync, true);
1096 false); 1120 WriteSettingGlobal(QStringLiteral("use_assembly_shaders"),
1097 WriteSetting(QStringLiteral("use_fast_gpu_time"), Settings::values.use_fast_gpu_time, true); 1121 Settings::values.use_assembly_shaders, false);
1098 WriteSetting(QStringLiteral("force_30fps_mode"), Settings::values.force_30fps_mode, false); 1122 WriteSettingGlobal(QStringLiteral("use_fast_gpu_time"), Settings::values.use_fast_gpu_time,
1123 true);
1124 WriteSettingGlobal(QStringLiteral("force_30fps_mode"), Settings::values.force_30fps_mode,
1125 false);
1099 1126
1100 // Cast to double because Qt's written float values are not human-readable 1127 // Cast to double because Qt's written float values are not human-readable
1101 WriteSetting(QStringLiteral("bg_red"), static_cast<double>(Settings::values.bg_red), 0.0); 1128 WriteSettingGlobal(QStringLiteral("bg_red"), Settings::values.bg_red, 0.0);
1102 WriteSetting(QStringLiteral("bg_green"), static_cast<double>(Settings::values.bg_green), 0.0); 1129 WriteSettingGlobal(QStringLiteral("bg_green"), Settings::values.bg_green, 0.0);
1103 WriteSetting(QStringLiteral("bg_blue"), static_cast<double>(Settings::values.bg_blue), 0.0); 1130 WriteSettingGlobal(QStringLiteral("bg_blue"), Settings::values.bg_blue, 0.0);
1104 1131
1105 qt_config->endGroup(); 1132 qt_config->endGroup();
1106} 1133}
@@ -1128,23 +1155,28 @@ void Config::SaveShortcutValues() {
1128void Config::SaveSystemValues() { 1155void Config::SaveSystemValues() {
1129 qt_config->beginGroup(QStringLiteral("System")); 1156 qt_config->beginGroup(QStringLiteral("System"));
1130 1157
1131 WriteSetting(QStringLiteral("use_docked_mode"), Settings::values.use_docked_mode, false);
1132 WriteSetting(QStringLiteral("current_user"), Settings::values.current_user, 0); 1158 WriteSetting(QStringLiteral("current_user"), Settings::values.current_user, 0);
1133 WriteSetting(QStringLiteral("language_index"), Settings::values.language_index, 1); 1159 WriteSettingGlobal(QStringLiteral("language_index"), Settings::values.language_index, 1);
1134 WriteSetting(QStringLiteral("region_index"), Settings::values.region_index, 1); 1160 WriteSettingGlobal(QStringLiteral("region_index"), Settings::values.region_index, 1);
1135 WriteSetting(QStringLiteral("time_zone_index"), Settings::values.time_zone_index, 0); 1161 WriteSettingGlobal(QStringLiteral("time_zone_index"), Settings::values.time_zone_index, 0);
1136 1162
1137 WriteSetting(QStringLiteral("rng_seed_enabled"), Settings::values.rng_seed.has_value(), false); 1163 WriteSettingGlobal(QStringLiteral("rng_seed_enabled"),
1138 WriteSetting(QStringLiteral("rng_seed"), Settings::values.rng_seed.value_or(0), 0); 1164 Settings::values.rng_seed.GetValue(global).has_value(),
1139 1165 Settings::values.rng_seed.UsingGlobal(), false);
1140 WriteSetting(QStringLiteral("custom_rtc_enabled"), Settings::values.custom_rtc.has_value(), 1166 WriteSettingGlobal(QStringLiteral("rng_seed"),
1141 false); 1167 Settings::values.rng_seed.GetValue(global).value_or(0),
1142 WriteSetting(QStringLiteral("custom_rtc"), 1168 Settings::values.rng_seed.UsingGlobal(), 0);
1143 QVariant::fromValue<long long>( 1169
1144 Settings::values.custom_rtc.value_or(std::chrono::seconds{}).count()), 1170 WriteSettingGlobal(QStringLiteral("custom_rtc_enabled"),
1145 0); 1171 Settings::values.custom_rtc.GetValue(global).has_value(),
1146 1172 Settings::values.custom_rtc.UsingGlobal(), false);
1147 WriteSetting(QStringLiteral("sound_index"), Settings::values.sound_index, 1); 1173 WriteSettingGlobal(
1174 QStringLiteral("custom_rtc"),
1175 QVariant::fromValue<long long>(
1176 Settings::values.custom_rtc.GetValue(global).value_or(std::chrono::seconds{}).count()),
1177 Settings::values.custom_rtc.UsingGlobal(), 0);
1178
1179 WriteSettingGlobal(QStringLiteral("sound_index"), Settings::values.sound_index, 1);
1148 1180
1149 qt_config->endGroup(); 1181 qt_config->endGroup();
1150} 1182}
@@ -1236,6 +1268,34 @@ QVariant Config::ReadSetting(const QString& name, const QVariant& default_value)
1236 return result; 1268 return result;
1237} 1269}
1238 1270
1271template <typename Type>
1272void Config::ReadSettingGlobal(Settings::Setting<Type>& setting, const QString& name) {
1273 const bool use_global = qt_config->value(name + QStringLiteral("/use_global"), true).toBool();
1274 setting.SetGlobal(use_global);
1275 if (global || !use_global) {
1276 setting.SetValue(ReadSetting(name).value<Type>());
1277 }
1278}
1279
1280template <typename Type>
1281void Config::ReadSettingGlobal(Settings::Setting<Type>& setting, const QString& name,
1282 const QVariant& default_value) {
1283 const bool use_global = qt_config->value(name + QStringLiteral("/use_global"), true).toBool();
1284 setting.SetGlobal(use_global);
1285 if (global || !use_global) {
1286 setting.SetValue(ReadSetting(name, default_value).value<Type>());
1287 }
1288}
1289
1290template <typename Type>
1291void Config::ReadSettingGlobal(Type& setting, const QString& name,
1292 const QVariant& default_value) const {
1293 const bool use_global = qt_config->value(name + QStringLiteral("/use_global"), true).toBool();
1294 if (global || !use_global) {
1295 setting = ReadSetting(name, default_value).value<Type>();
1296 }
1297}
1298
1239void Config::WriteSetting(const QString& name, const QVariant& value) { 1299void Config::WriteSetting(const QString& name, const QVariant& value) {
1240 qt_config->setValue(name, value); 1300 qt_config->setValue(name, value);
1241} 1301}
@@ -1246,6 +1306,40 @@ void Config::WriteSetting(const QString& name, const QVariant& value,
1246 qt_config->setValue(name, value); 1306 qt_config->setValue(name, value);
1247} 1307}
1248 1308
1309template <typename Type>
1310void Config::WriteSettingGlobal(const QString& name, const Settings::Setting<Type>& setting) {
1311 if (!global) {
1312 qt_config->setValue(name + QStringLiteral("/use_global"), setting.UsingGlobal());
1313 }
1314 if (global || !setting.UsingGlobal()) {
1315 qt_config->setValue(name, setting.GetValue(global));
1316 }
1317}
1318
1319template <typename Type>
1320void Config::WriteSettingGlobal(const QString& name, const Settings::Setting<Type>& setting,
1321 const QVariant& default_value) {
1322 if (!global) {
1323 qt_config->setValue(name + QStringLiteral("/use_global"), setting.UsingGlobal());
1324 }
1325 if (global || !setting.UsingGlobal()) {
1326 qt_config->setValue(name + QStringLiteral("/default"),
1327 setting.GetValue(global) == default_value.value<Type>());
1328 qt_config->setValue(name, setting.GetValue(global));
1329 }
1330}
1331
1332void Config::WriteSettingGlobal(const QString& name, const QVariant& value, bool use_global,
1333 const QVariant& default_value) {
1334 if (!global) {
1335 qt_config->setValue(name + QStringLiteral("/use_global"), use_global);
1336 }
1337 if (global || !use_global) {
1338 qt_config->setValue(name + QStringLiteral("/default"), value == default_value);
1339 qt_config->setValue(name, value);
1340 }
1341}
1342
1249void Config::Reload() { 1343void Config::Reload() {
1250 ReadValues(); 1344 ReadValues();
1251 // To apply default value changes 1345 // To apply default value changes
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index 09316382c..681f0bca5 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -7,6 +7,7 @@
7#include <array> 7#include <array>
8#include <memory> 8#include <memory>
9#include <string> 9#include <string>
10#include <QMetaType>
10#include <QVariant> 11#include <QVariant>
11#include "core/settings.h" 12#include "core/settings.h"
12#include "yuzu/uisettings.h" 13#include "yuzu/uisettings.h"
@@ -15,7 +16,7 @@ class QSettings;
15 16
16class Config { 17class Config {
17public: 18public:
18 Config(); 19 explicit Config(const std::string& config_loc = "qt-config.ini", bool is_global = true);
19 ~Config(); 20 ~Config();
20 21
21 void Reload(); 22 void Reload();
@@ -82,9 +83,33 @@ private:
82 83
83 QVariant ReadSetting(const QString& name) const; 84 QVariant ReadSetting(const QString& name) const;
84 QVariant ReadSetting(const QString& name, const QVariant& default_value) const; 85 QVariant ReadSetting(const QString& name, const QVariant& default_value) const;
86 // Templated ReadSettingGlobal functions will also look for the use_global setting and set
87 // both the value and the global state properly
88 template <typename Type>
89 void ReadSettingGlobal(Settings::Setting<Type>& setting, const QString& name);
90 template <typename Type>
91 void ReadSettingGlobal(Settings::Setting<Type>& setting, const QString& name,
92 const QVariant& default_value);
93 template <typename Type>
94 void ReadSettingGlobal(Type& setting, const QString& name, const QVariant& default_value) const;
95 // Templated WriteSettingGlobal functions will also write the global state if needed and will
96 // skip writing the actual setting if it defers to the global value
85 void WriteSetting(const QString& name, const QVariant& value); 97 void WriteSetting(const QString& name, const QVariant& value);
86 void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value); 98 void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value);
99 template <typename Type>
100 void WriteSettingGlobal(const QString& name, const Settings::Setting<Type>& setting);
101 template <typename Type>
102 void WriteSettingGlobal(const QString& name, const Settings::Setting<Type>& setting,
103 const QVariant& default_value);
104 void WriteSettingGlobal(const QString& name, const QVariant& value, bool use_global,
105 const QVariant& default_value);
87 106
88 std::unique_ptr<QSettings> qt_config; 107 std::unique_ptr<QSettings> qt_config;
89 std::string qt_config_loc; 108 std::string qt_config_loc;
109
110 bool global;
90}; 111};
112
113// These metatype declarations cannot be in core/settings.h because core is devoid of QT
114Q_DECLARE_METATYPE(Settings::RendererBackend);
115Q_DECLARE_METATYPE(Settings::GPUAccuracy);
diff --git a/src/yuzu/configuration/configuration_shared.cpp b/src/yuzu/configuration/configuration_shared.cpp
new file mode 100644
index 000000000..bb47c3933
--- /dev/null
+++ b/src/yuzu/configuration/configuration_shared.cpp
@@ -0,0 +1,76 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <QCheckBox>
6#include <QComboBox>
7#include "core/settings.h"
8#include "yuzu/configuration/configuration_shared.h"
9#include "yuzu/configuration/configure_per_game.h"
10
11void ConfigurationShared::ApplyPerGameSetting(Settings::Setting<bool>* setting,
12 const QCheckBox* checkbox) {
13 if (checkbox->checkState() == Qt::PartiallyChecked) {
14 setting->SetGlobal(true);
15 } else {
16 setting->SetGlobal(false);
17 setting->SetValue(checkbox->checkState() == Qt::Checked);
18 }
19}
20
21void ConfigurationShared::ApplyPerGameSetting(Settings::Setting<int>* setting,
22 const QComboBox* combobox) {
23 if (combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
24 setting->SetGlobal(true);
25 } else {
26 setting->SetGlobal(false);
27 setting->SetValue(combobox->currentIndex() - ConfigurationShared::USE_GLOBAL_OFFSET);
28 }
29}
30
31void ConfigurationShared::ApplyPerGameSetting(Settings::Setting<Settings::RendererBackend>* setting,
32 const QComboBox* combobox) {
33 if (combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
34 setting->SetGlobal(true);
35 } else {
36 setting->SetGlobal(false);
37 setting->SetValue(static_cast<Settings::RendererBackend>(
38 combobox->currentIndex() - ConfigurationShared::USE_GLOBAL_OFFSET));
39 }
40}
41
42void ConfigurationShared::SetPerGameSetting(QCheckBox* checkbox,
43 const Settings::Setting<bool>* setting) {
44 if (setting->UsingGlobal()) {
45 checkbox->setCheckState(Qt::PartiallyChecked);
46 } else {
47 checkbox->setCheckState(setting->GetValue() ? Qt::Checked : Qt::Unchecked);
48 }
49}
50
51void ConfigurationShared::SetPerGameSetting(QComboBox* combobox,
52 const Settings::Setting<int>* setting) {
53 combobox->setCurrentIndex(setting->UsingGlobal()
54 ? ConfigurationShared::USE_GLOBAL_INDEX
55 : setting->GetValue() + ConfigurationShared::USE_GLOBAL_OFFSET);
56}
57
58void ConfigurationShared::SetPerGameSetting(
59 QComboBox* combobox, const Settings::Setting<Settings::RendererBackend>* setting) {
60 combobox->setCurrentIndex(setting->UsingGlobal() ? ConfigurationShared::USE_GLOBAL_INDEX
61 : static_cast<int>(setting->GetValue()) +
62 ConfigurationShared::USE_GLOBAL_OFFSET);
63}
64
65void ConfigurationShared::SetPerGameSetting(
66 QComboBox* combobox, const Settings::Setting<Settings::GPUAccuracy>* setting) {
67 combobox->setCurrentIndex(setting->UsingGlobal() ? ConfigurationShared::USE_GLOBAL_INDEX
68 : static_cast<int>(setting->GetValue()) +
69 ConfigurationShared::USE_GLOBAL_OFFSET);
70}
71
72void ConfigurationShared::InsertGlobalItem(QComboBox* combobox) {
73 const QString use_global_text = ConfigurePerGame::tr("Use global configuration");
74 combobox->insertItem(ConfigurationShared::USE_GLOBAL_INDEX, use_global_text);
75 combobox->insertSeparator(ConfigurationShared::USE_GLOBAL_SEPARATOR_INDEX);
76}
diff --git a/src/yuzu/configuration/configuration_shared.h b/src/yuzu/configuration/configuration_shared.h
new file mode 100644
index 000000000..b11b1b950
--- /dev/null
+++ b/src/yuzu/configuration/configuration_shared.h
@@ -0,0 +1,36 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <QCheckBox>
8#include <QComboBox>
9#include <QString>
10#include "core/settings.h"
11
12namespace ConfigurationShared {
13
14constexpr int USE_GLOBAL_INDEX = 0;
15constexpr int USE_GLOBAL_SEPARATOR_INDEX = 1;
16constexpr int USE_GLOBAL_OFFSET = 2;
17
18// Global-aware apply and set functions
19
20void ApplyPerGameSetting(Settings::Setting<bool>* setting, const QCheckBox* checkbox);
21void ApplyPerGameSetting(Settings::Setting<int>* setting, const QComboBox* combobox);
22void ApplyPerGameSetting(Settings::Setting<Settings::RendererBackend>* setting,
23 const QComboBox* combobox);
24void ApplyPerGameSetting(Settings::Setting<Settings::GPUAccuracy>* setting,
25 const QComboBox* combobox);
26
27void SetPerGameSetting(QCheckBox* checkbox, const Settings::Setting<bool>* setting);
28void SetPerGameSetting(QComboBox* combobox, const Settings::Setting<int>* setting);
29void SetPerGameSetting(QComboBox* combobox,
30 const Settings::Setting<Settings::RendererBackend>* setting);
31void SetPerGameSetting(QComboBox* combobox,
32 const Settings::Setting<Settings::GPUAccuracy>* setting);
33
34void InsertGlobalItem(QComboBox* combobox);
35
36} // namespace ConfigurationShared
diff --git a/src/yuzu/configuration/configure_audio.cpp b/src/yuzu/configuration/configure_audio.cpp
index f370c690f..cc021beec 100644
--- a/src/yuzu/configuration/configure_audio.cpp
+++ b/src/yuzu/configuration/configure_audio.cpp
@@ -11,6 +11,7 @@
11#include "core/core.h" 11#include "core/core.h"
12#include "core/settings.h" 12#include "core/settings.h"
13#include "ui_configure_audio.h" 13#include "ui_configure_audio.h"
14#include "yuzu/configuration/configuration_shared.h"
14#include "yuzu/configuration/configure_audio.h" 15#include "yuzu/configuration/configure_audio.h"
15 16
16ConfigureAudio::ConfigureAudio(QWidget* parent) 17ConfigureAudio::ConfigureAudio(QWidget* parent)
@@ -24,6 +25,11 @@ ConfigureAudio::ConfigureAudio(QWidget* parent)
24 connect(ui->output_sink_combo_box, qOverload<int>(&QComboBox::currentIndexChanged), this, 25 connect(ui->output_sink_combo_box, qOverload<int>(&QComboBox::currentIndexChanged), this,
25 &ConfigureAudio::UpdateAudioDevices); 26 &ConfigureAudio::UpdateAudioDevices);
26 27
28 ui->volume_label->setVisible(Settings::configuring_global);
29 ui->volume_combo_box->setVisible(!Settings::configuring_global);
30
31 SetupPerGameUI();
32
27 SetConfiguration(); 33 SetConfiguration();
28 34
29 const bool is_powered_on = Core::System::GetInstance().IsPoweredOn(); 35 const bool is_powered_on = Core::System::GetInstance().IsPoweredOn();
@@ -41,8 +47,22 @@ void ConfigureAudio::SetConfiguration() {
41 47
42 SetAudioDeviceFromDeviceID(); 48 SetAudioDeviceFromDeviceID();
43 49
44 ui->toggle_audio_stretching->setChecked(Settings::values.enable_audio_stretching); 50 ui->volume_slider->setValue(Settings::values.volume.GetValue() * ui->volume_slider->maximum());
45 ui->volume_slider->setValue(Settings::values.volume * ui->volume_slider->maximum()); 51
52 if (Settings::configuring_global) {
53 ui->toggle_audio_stretching->setChecked(
54 Settings::values.enable_audio_stretching.GetValue());
55 } else {
56 ConfigurationShared::SetPerGameSetting(ui->toggle_audio_stretching,
57 &Settings::values.enable_audio_stretching);
58 if (Settings::values.volume.UsingGlobal()) {
59 ui->volume_combo_box->setCurrentIndex(0);
60 ui->volume_slider->setEnabled(false);
61 } else {
62 ui->volume_combo_box->setCurrentIndex(1);
63 ui->volume_slider->setEnabled(true);
64 }
65 }
46 SetVolumeIndicatorText(ui->volume_slider->sliderPosition()); 66 SetVolumeIndicatorText(ui->volume_slider->sliderPosition());
47} 67}
48 68
@@ -80,15 +100,36 @@ void ConfigureAudio::SetVolumeIndicatorText(int percentage) {
80} 100}
81 101
82void ConfigureAudio::ApplyConfiguration() { 102void ConfigureAudio::ApplyConfiguration() {
83 Settings::values.sink_id = 103 if (Settings::configuring_global) {
84 ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex()) 104 Settings::values.sink_id =
85 .toStdString(); 105 ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex())
86 Settings::values.enable_audio_stretching = ui->toggle_audio_stretching->isChecked(); 106 .toStdString();
87 Settings::values.audio_device_id = 107 Settings::values.audio_device_id =
88 ui->audio_device_combo_box->itemText(ui->audio_device_combo_box->currentIndex()) 108 ui->audio_device_combo_box->itemText(ui->audio_device_combo_box->currentIndex())
89 .toStdString(); 109 .toStdString();
90 Settings::values.volume = 110
91 static_cast<float>(ui->volume_slider->sliderPosition()) / ui->volume_slider->maximum(); 111 // Guard if during game and set to game-specific value
112 if (Settings::values.enable_audio_stretching.UsingGlobal()) {
113 Settings::values.enable_audio_stretching.SetValue(
114 ui->toggle_audio_stretching->isChecked());
115 }
116 if (Settings::values.volume.UsingGlobal()) {
117 Settings::values.volume.SetValue(
118 static_cast<float>(ui->volume_slider->sliderPosition()) /
119 ui->volume_slider->maximum());
120 }
121 } else {
122 ConfigurationShared::ApplyPerGameSetting(&Settings::values.enable_audio_stretching,
123 ui->toggle_audio_stretching);
124 if (ui->volume_combo_box->currentIndex() == 0) {
125 Settings::values.volume.SetGlobal(true);
126 } else {
127 Settings::values.volume.SetGlobal(false);
128 Settings::values.volume.SetValue(
129 static_cast<float>(ui->volume_slider->sliderPosition()) /
130 ui->volume_slider->maximum());
131 }
132 }
92} 133}
93 134
94void ConfigureAudio::changeEvent(QEvent* event) { 135void ConfigureAudio::changeEvent(QEvent* event) {
@@ -122,3 +163,22 @@ void ConfigureAudio::RetranslateUI() {
122 ui->retranslateUi(this); 163 ui->retranslateUi(this);
123 SetVolumeIndicatorText(ui->volume_slider->sliderPosition()); 164 SetVolumeIndicatorText(ui->volume_slider->sliderPosition());
124} 165}
166
167void ConfigureAudio::SetupPerGameUI() {
168 if (Settings::configuring_global) {
169 ui->volume_slider->setEnabled(Settings::values.volume.UsingGlobal());
170 ui->toggle_audio_stretching->setEnabled(
171 Settings::values.enable_audio_stretching.UsingGlobal());
172
173 return;
174 }
175
176 ui->toggle_audio_stretching->setTristate(true);
177 connect(ui->volume_combo_box, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated),
178 this, [this](int index) { ui->volume_slider->setEnabled(index == 1); });
179
180 ui->output_sink_combo_box->setVisible(false);
181 ui->output_sink_label->setVisible(false);
182 ui->audio_device_combo_box->setVisible(false);
183 ui->audio_device_label->setVisible(false);
184}
diff --git a/src/yuzu/configuration/configure_audio.h b/src/yuzu/configuration/configure_audio.h
index ea83bd72d..d84f4a682 100644
--- a/src/yuzu/configuration/configure_audio.h
+++ b/src/yuzu/configuration/configure_audio.h
@@ -34,5 +34,7 @@ private:
34 void SetAudioDeviceFromDeviceID(); 34 void SetAudioDeviceFromDeviceID();
35 void SetVolumeIndicatorText(int percentage); 35 void SetVolumeIndicatorText(int percentage);
36 36
37 void SetupPerGameUI();
38
37 std::unique_ptr<Ui::ConfigureAudio> ui; 39 std::unique_ptr<Ui::ConfigureAudio> ui;
38}; 40};
diff --git a/src/yuzu/configuration/configure_audio.ui b/src/yuzu/configuration/configure_audio.ui
index a098b9acc..862ccb988 100644
--- a/src/yuzu/configuration/configure_audio.ui
+++ b/src/yuzu/configuration/configure_audio.ui
@@ -6,8 +6,8 @@
6 <rect> 6 <rect>
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>188</width> 9 <width>367</width>
10 <height>246</height> 10 <height>368</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <layout class="QVBoxLayout"> 13 <layout class="QVBoxLayout">
@@ -18,9 +18,9 @@
18 </property> 18 </property>
19 <layout class="QVBoxLayout"> 19 <layout class="QVBoxLayout">
20 <item> 20 <item>
21 <layout class="QHBoxLayout"> 21 <layout class="QHBoxLayout" name="_3">
22 <item> 22 <item>
23 <widget class="QLabel" name="label_1"> 23 <widget class="QLabel" name="output_sink_label">
24 <property name="text"> 24 <property name="text">
25 <string>Output Engine:</string> 25 <string>Output Engine:</string>
26 </property> 26 </property>
@@ -31,20 +31,20 @@
31 </item> 31 </item>
32 </layout> 32 </layout>
33 </item> 33 </item>
34 <item>
35 <widget class="QCheckBox" name="toggle_audio_stretching">
36 <property name="toolTip">
37 <string>This post-processing effect adjusts audio speed to match emulation speed and helps prevent audio stutter. This however increases audio latency.</string>
38 </property>
39 <property name="text">
40 <string>Enable audio stretching</string>
41 </property>
42 </widget>
43 </item>
44 <item> 34 <item>
45 <layout class="QHBoxLayout"> 35 <widget class="QCheckBox" name="toggle_audio_stretching">
36 <property name="toolTip">
37 <string>This post-processing effect adjusts audio speed to match emulation speed and helps prevent audio stutter. This however increases audio latency.</string>
38 </property>
39 <property name="text">
40 <string>Enable audio stretching</string>
41 </property>
42 </widget>
43 </item>
44 <item>
45 <layout class="QHBoxLayout" name="_2">
46 <item> 46 <item>
47 <widget class="QLabel" name="label_2"> 47 <widget class="QLabel" name="audio_device_label">
48 <property name="text"> 48 <property name="text">
49 <string>Audio Device:</string> 49 <string>Audio Device:</string>
50 </property> 50 </property>
@@ -61,7 +61,21 @@
61 <number>0</number> 61 <number>0</number>
62 </property> 62 </property>
63 <item> 63 <item>
64 <widget class="QLabel" name="label_3"> 64 <widget class="QComboBox" name="volume_combo_box">
65 <item>
66 <property name="text">
67 <string>Use global volume</string>
68 </property>
69 </item>
70 <item>
71 <property name="text">
72 <string>Set volume:</string>
73 </property>
74 </item>
75 </widget>
76 </item>
77 <item>
78 <widget class="QLabel" name="volume_label">
65 <property name="text"> 79 <property name="text">
66 <string>Volume:</string> 80 <string>Volume:</string>
67 </property> 81 </property>
@@ -74,7 +88,7 @@
74 </property> 88 </property>
75 <property name="sizeHint" stdset="0"> 89 <property name="sizeHint" stdset="0">
76 <size> 90 <size>
77 <width>40</width> 91 <width>30</width>
78 <height>20</height> 92 <height>20</height>
79 </size> 93 </size>
80 </property> 94 </property>
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index df4473b46..5918e9972 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -14,6 +14,8 @@
14 14
15ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry) 15ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry)
16 : QDialog(parent), ui(new Ui::ConfigureDialog), registry(registry) { 16 : QDialog(parent), ui(new Ui::ConfigureDialog), registry(registry) {
17 Settings::configuring_global = true;
18
17 ui->setupUi(this); 19 ui->setupUi(this);
18 ui->hotkeysTab->Populate(registry); 20 ui->hotkeysTab->Populate(registry);
19 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); 21 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp
index 74b2ad537..1fb62d1cf 100644
--- a/src/yuzu/configuration/configure_general.cpp
+++ b/src/yuzu/configuration/configure_general.cpp
@@ -7,17 +7,21 @@
7#include "core/core.h" 7#include "core/core.h"
8#include "core/settings.h" 8#include "core/settings.h"
9#include "ui_configure_general.h" 9#include "ui_configure_general.h"
10#include "yuzu/configuration/configuration_shared.h"
10#include "yuzu/configuration/configure_general.h" 11#include "yuzu/configuration/configure_general.h"
11#include "yuzu/uisettings.h" 12#include "yuzu/uisettings.h"
12 13
13ConfigureGeneral::ConfigureGeneral(QWidget* parent) 14ConfigureGeneral::ConfigureGeneral(QWidget* parent)
14 : QWidget(parent), ui(new Ui::ConfigureGeneral) { 15 : QWidget(parent), ui(new Ui::ConfigureGeneral) {
15
16 ui->setupUi(this); 16 ui->setupUi(this);
17 17
18 SetupPerGameUI();
19
18 SetConfiguration(); 20 SetConfiguration();
19 21
20 connect(ui->toggle_frame_limit, &QCheckBox::toggled, ui->frame_limit, &QSpinBox::setEnabled); 22 connect(ui->toggle_frame_limit, &QCheckBox::stateChanged, ui->frame_limit, [this]() {
23 ui->frame_limit->setEnabled(ui->toggle_frame_limit->checkState() == Qt::Checked);
24 });
21} 25}
22 26
23ConfigureGeneral::~ConfigureGeneral() = default; 27ConfigureGeneral::~ConfigureGeneral() = default;
@@ -26,27 +30,56 @@ void ConfigureGeneral::SetConfiguration() {
26 const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn(); 30 const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn();
27 31
28 ui->use_multi_core->setEnabled(runtime_lock); 32 ui->use_multi_core->setEnabled(runtime_lock);
29 ui->use_multi_core->setChecked(Settings::values.use_multi_core); 33 ui->use_multi_core->setChecked(Settings::values.use_multi_core.GetValue());
30 34
31 ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing); 35 ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing);
32 ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot); 36 ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot);
33 ui->toggle_background_pause->setChecked(UISettings::values.pause_when_in_background); 37 ui->toggle_background_pause->setChecked(UISettings::values.pause_when_in_background);
34 ui->toggle_hide_mouse->setChecked(UISettings::values.hide_mouse); 38 ui->toggle_hide_mouse->setChecked(UISettings::values.hide_mouse);
35 39
36 ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit); 40 ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit.GetValue());
37 ui->frame_limit->setEnabled(ui->toggle_frame_limit->isChecked()); 41 ui->frame_limit->setValue(Settings::values.frame_limit.GetValue());
38 ui->frame_limit->setValue(Settings::values.frame_limit); 42
43 if (!Settings::configuring_global) {
44 if (Settings::values.use_multi_core.UsingGlobal()) {
45 ui->use_multi_core->setCheckState(Qt::PartiallyChecked);
46 }
47 if (Settings::values.use_frame_limit.UsingGlobal()) {
48 ui->toggle_frame_limit->setCheckState(Qt::PartiallyChecked);
49 }
50 }
51
52 ui->frame_limit->setEnabled(ui->toggle_frame_limit->checkState() == Qt::Checked &&
53 ui->toggle_frame_limit->isEnabled());
39} 54}
40 55
41void ConfigureGeneral::ApplyConfiguration() { 56void ConfigureGeneral::ApplyConfiguration() {
42 UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked(); 57 if (Settings::configuring_global) {
43 UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked(); 58 UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
44 UISettings::values.pause_when_in_background = ui->toggle_background_pause->isChecked(); 59 UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked();
45 UISettings::values.hide_mouse = ui->toggle_hide_mouse->isChecked(); 60 UISettings::values.pause_when_in_background = ui->toggle_background_pause->isChecked();
46 61 UISettings::values.hide_mouse = ui->toggle_hide_mouse->isChecked();
47 Settings::values.use_frame_limit = ui->toggle_frame_limit->isChecked(); 62
48 Settings::values.frame_limit = ui->frame_limit->value(); 63 // Guard if during game and set to game-specific value
49 Settings::values.use_multi_core = ui->use_multi_core->isChecked(); 64 if (Settings::values.use_frame_limit.UsingGlobal()) {
65 Settings::values.use_frame_limit.SetValue(ui->toggle_frame_limit->checkState() ==
66 Qt::Checked);
67 Settings::values.frame_limit.SetValue(ui->frame_limit->value());
68 Settings::values.use_multi_core.SetValue(ui->use_multi_core->isChecked());
69 }
70 } else {
71 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_multi_core,
72 ui->use_multi_core);
73
74 bool global_frame_limit = ui->toggle_frame_limit->checkState() == Qt::PartiallyChecked;
75 Settings::values.use_frame_limit.SetGlobal(global_frame_limit);
76 Settings::values.frame_limit.SetGlobal(global_frame_limit);
77 if (!global_frame_limit) {
78 Settings::values.use_frame_limit.SetValue(ui->toggle_frame_limit->checkState() ==
79 Qt::Checked);
80 Settings::values.frame_limit.SetValue(ui->frame_limit->value());
81 }
82 }
50} 83}
51 84
52void ConfigureGeneral::changeEvent(QEvent* event) { 85void ConfigureGeneral::changeEvent(QEvent* event) {
@@ -60,3 +93,20 @@ void ConfigureGeneral::changeEvent(QEvent* event) {
60void ConfigureGeneral::RetranslateUI() { 93void ConfigureGeneral::RetranslateUI() {
61 ui->retranslateUi(this); 94 ui->retranslateUi(this);
62} 95}
96
97void ConfigureGeneral::SetupPerGameUI() {
98 if (Settings::configuring_global) {
99 ui->toggle_frame_limit->setEnabled(Settings::values.use_frame_limit.UsingGlobal());
100 ui->frame_limit->setEnabled(Settings::values.frame_limit.UsingGlobal());
101
102 return;
103 }
104
105 ui->toggle_check_exit->setVisible(false);
106 ui->toggle_user_on_boot->setVisible(false);
107 ui->toggle_background_pause->setVisible(false);
108 ui->toggle_hide_mouse->setVisible(false);
109
110 ui->toggle_frame_limit->setTristate(true);
111 ui->use_multi_core->setTristate(true);
112}
diff --git a/src/yuzu/configuration/configure_general.h b/src/yuzu/configuration/configure_general.h
index ef05ce065..9c785c22e 100644
--- a/src/yuzu/configuration/configure_general.h
+++ b/src/yuzu/configuration/configure_general.h
@@ -28,5 +28,7 @@ private:
28 28
29 void SetConfiguration(); 29 void SetConfiguration();
30 30
31 void SetupPerGameUI();
32
31 std::unique_ptr<Ui::ConfigureGeneral> ui; 33 std::unique_ptr<Ui::ConfigureGeneral> ui;
32}; 34};
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index 431f51d73..cb4706bd6 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -13,6 +13,7 @@
13#include "core/core.h" 13#include "core/core.h"
14#include "core/settings.h" 14#include "core/settings.h"
15#include "ui_configure_graphics.h" 15#include "ui_configure_graphics.h"
16#include "yuzu/configuration/configuration_shared.h"
16#include "yuzu/configuration/configure_graphics.h" 17#include "yuzu/configuration/configure_graphics.h"
17 18
18#ifdef HAS_VULKAN 19#ifdef HAS_VULKAN
@@ -21,11 +22,13 @@
21 22
22ConfigureGraphics::ConfigureGraphics(QWidget* parent) 23ConfigureGraphics::ConfigureGraphics(QWidget* parent)
23 : QWidget(parent), ui(new Ui::ConfigureGraphics) { 24 : QWidget(parent), ui(new Ui::ConfigureGraphics) {
24 vulkan_device = Settings::values.vulkan_device; 25 vulkan_device = Settings::values.vulkan_device.GetValue();
25 RetrieveVulkanDevices(); 26 RetrieveVulkanDevices();
26 27
27 ui->setupUi(this); 28 ui->setupUi(this);
28 29
30 SetupPerGameUI();
31
29 SetConfiguration(); 32 SetConfiguration();
30 33
31 connect(ui->api, qOverload<int>(&QComboBox::currentIndexChanged), this, 34 connect(ui->api, qOverload<int>(&QComboBox::currentIndexChanged), this,
@@ -40,6 +43,9 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent)
40 } 43 }
41 UpdateBackgroundColorButton(new_bg_color); 44 UpdateBackgroundColorButton(new_bg_color);
42 }); 45 });
46
47 ui->bg_label->setVisible(Settings::configuring_global);
48 ui->bg_combobox->setVisible(!Settings::configuring_global);
43} 49}
44 50
45void ConfigureGraphics::UpdateDeviceSelection(int device) { 51void ConfigureGraphics::UpdateDeviceSelection(int device) {
@@ -57,27 +63,95 @@ void ConfigureGraphics::SetConfiguration() {
57 const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn(); 63 const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn();
58 64
59 ui->api->setEnabled(runtime_lock); 65 ui->api->setEnabled(runtime_lock);
60 ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend));
61 ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio);
62 ui->use_disk_shader_cache->setEnabled(runtime_lock);
63 ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache);
64 ui->use_asynchronous_gpu_emulation->setEnabled(runtime_lock); 66 ui->use_asynchronous_gpu_emulation->setEnabled(runtime_lock);
65 ui->use_asynchronous_gpu_emulation->setChecked(Settings::values.use_asynchronous_gpu_emulation); 67 ui->use_disk_shader_cache->setEnabled(runtime_lock);
66 UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green, 68
67 Settings::values.bg_blue)); 69 if (Settings::configuring_global) {
70 ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend.GetValue()));
71 ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio.GetValue());
72 ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache.GetValue());
73 ui->use_asynchronous_gpu_emulation->setChecked(
74 Settings::values.use_asynchronous_gpu_emulation.GetValue());
75 } else {
76 ConfigurationShared::SetPerGameSetting(ui->use_disk_shader_cache,
77 &Settings::values.use_disk_shader_cache);
78 ConfigurationShared::SetPerGameSetting(ui->use_asynchronous_gpu_emulation,
79 &Settings::values.use_asynchronous_gpu_emulation);
80
81 ConfigurationShared::SetPerGameSetting(ui->api, &Settings::values.renderer_backend);
82 ConfigurationShared::SetPerGameSetting(ui->aspect_ratio_combobox,
83 &Settings::values.aspect_ratio);
84
85 ui->bg_combobox->setCurrentIndex(Settings::values.bg_red.UsingGlobal() ? 0 : 1);
86 ui->bg_button->setEnabled(!Settings::values.bg_red.UsingGlobal());
87 }
88
89 UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red.GetValue(),
90 Settings::values.bg_green.GetValue(),
91 Settings::values.bg_blue.GetValue()));
68 UpdateDeviceComboBox(); 92 UpdateDeviceComboBox();
69} 93}
70 94
71void ConfigureGraphics::ApplyConfiguration() { 95void ConfigureGraphics::ApplyConfiguration() {
72 Settings::values.renderer_backend = GetCurrentGraphicsBackend(); 96 if (Settings::configuring_global) {
73 Settings::values.vulkan_device = vulkan_device; 97 // Guard if during game and set to game-specific value
74 Settings::values.aspect_ratio = ui->aspect_ratio_combobox->currentIndex(); 98 if (Settings::values.renderer_backend.UsingGlobal()) {
75 Settings::values.use_disk_shader_cache = ui->use_disk_shader_cache->isChecked(); 99 Settings::values.renderer_backend.SetValue(GetCurrentGraphicsBackend());
76 Settings::values.use_asynchronous_gpu_emulation = 100 }
77 ui->use_asynchronous_gpu_emulation->isChecked(); 101 if (Settings::values.vulkan_device.UsingGlobal()) {
78 Settings::values.bg_red = static_cast<float>(bg_color.redF()); 102 Settings::values.vulkan_device.SetValue(vulkan_device);
79 Settings::values.bg_green = static_cast<float>(bg_color.greenF()); 103 }
80 Settings::values.bg_blue = static_cast<float>(bg_color.blueF()); 104 if (Settings::values.aspect_ratio.UsingGlobal()) {
105 Settings::values.aspect_ratio.SetValue(ui->aspect_ratio_combobox->currentIndex());
106 }
107 if (Settings::values.use_disk_shader_cache.UsingGlobal()) {
108 Settings::values.use_disk_shader_cache.SetValue(ui->use_disk_shader_cache->isChecked());
109 }
110 if (Settings::values.use_asynchronous_gpu_emulation.UsingGlobal()) {
111 Settings::values.use_asynchronous_gpu_emulation.SetValue(
112 ui->use_asynchronous_gpu_emulation->isChecked());
113 }
114 if (Settings::values.bg_red.UsingGlobal()) {
115 Settings::values.bg_red.SetValue(static_cast<float>(bg_color.redF()));
116 Settings::values.bg_green.SetValue(static_cast<float>(bg_color.greenF()));
117 Settings::values.bg_blue.SetValue(static_cast<float>(bg_color.blueF()));
118 }
119 } else {
120 if (ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
121 Settings::values.renderer_backend.SetGlobal(true);
122 Settings::values.vulkan_device.SetGlobal(true);
123 } else {
124 Settings::values.renderer_backend.SetGlobal(false);
125 Settings::values.renderer_backend.SetValue(GetCurrentGraphicsBackend());
126 if (GetCurrentGraphicsBackend() == Settings::RendererBackend::Vulkan) {
127 Settings::values.vulkan_device.SetGlobal(false);
128 Settings::values.vulkan_device.SetValue(vulkan_device);
129 } else {
130 Settings::values.vulkan_device.SetGlobal(true);
131 }
132 }
133
134 ConfigurationShared::ApplyPerGameSetting(&Settings::values.aspect_ratio,
135 ui->aspect_ratio_combobox);
136
137 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_disk_shader_cache,
138 ui->use_disk_shader_cache);
139 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_gpu_emulation,
140 ui->use_asynchronous_gpu_emulation);
141
142 if (ui->bg_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
143 Settings::values.bg_red.SetGlobal(true);
144 Settings::values.bg_green.SetGlobal(true);
145 Settings::values.bg_blue.SetGlobal(true);
146 } else {
147 Settings::values.bg_red.SetGlobal(false);
148 Settings::values.bg_green.SetGlobal(false);
149 Settings::values.bg_blue.SetGlobal(false);
150 Settings::values.bg_red.SetValue(static_cast<float>(bg_color.redF()));
151 Settings::values.bg_green.SetValue(static_cast<float>(bg_color.greenF()));
152 Settings::values.bg_blue.SetValue(static_cast<float>(bg_color.blueF()));
153 }
154 }
81} 155}
82 156
83void ConfigureGraphics::changeEvent(QEvent* event) { 157void ConfigureGraphics::changeEvent(QEvent* event) {
@@ -106,6 +180,11 @@ void ConfigureGraphics::UpdateDeviceComboBox() {
106 ui->device->clear(); 180 ui->device->clear();
107 181
108 bool enabled = false; 182 bool enabled = false;
183
184 if (!Settings::configuring_global &&
185 ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
186 vulkan_device = Settings::values.vulkan_device.GetValue();
187 }
109 switch (GetCurrentGraphicsBackend()) { 188 switch (GetCurrentGraphicsBackend()) {
110 case Settings::RendererBackend::OpenGL: 189 case Settings::RendererBackend::OpenGL:
111 ui->device->addItem(tr("OpenGL Graphics Device")); 190 ui->device->addItem(tr("OpenGL Graphics Device"));
@@ -119,6 +198,9 @@ void ConfigureGraphics::UpdateDeviceComboBox() {
119 enabled = !vulkan_devices.empty(); 198 enabled = !vulkan_devices.empty();
120 break; 199 break;
121 } 200 }
201 // If in per-game config and use global is selected, don't enable.
202 enabled &= !(!Settings::configuring_global &&
203 ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX);
122 ui->device->setEnabled(enabled && !Core::System::GetInstance().IsPoweredOn()); 204 ui->device->setEnabled(enabled && !Core::System::GetInstance().IsPoweredOn());
123} 205}
124 206
@@ -132,5 +214,37 @@ void ConfigureGraphics::RetrieveVulkanDevices() {
132} 214}
133 215
134Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const { 216Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const {
135 return static_cast<Settings::RendererBackend>(ui->api->currentIndex()); 217 if (Settings::configuring_global) {
218 return static_cast<Settings::RendererBackend>(ui->api->currentIndex());
219 }
220
221 if (ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
222 Settings::values.renderer_backend.SetGlobal(true);
223 return Settings::values.renderer_backend.GetValue();
224 }
225 Settings::values.renderer_backend.SetGlobal(false);
226 return static_cast<Settings::RendererBackend>(ui->api->currentIndex() -
227 ConfigurationShared::USE_GLOBAL_OFFSET);
228}
229
230void ConfigureGraphics::SetupPerGameUI() {
231 if (Settings::configuring_global) {
232 ui->api->setEnabled(Settings::values.renderer_backend.UsingGlobal());
233 ui->device->setEnabled(Settings::values.renderer_backend.UsingGlobal());
234 ui->aspect_ratio_combobox->setEnabled(Settings::values.aspect_ratio.UsingGlobal());
235 ui->use_asynchronous_gpu_emulation->setEnabled(
236 Settings::values.use_asynchronous_gpu_emulation.UsingGlobal());
237 ui->use_disk_shader_cache->setEnabled(Settings::values.use_disk_shader_cache.UsingGlobal());
238 ui->bg_button->setEnabled(Settings::values.bg_red.UsingGlobal());
239
240 return;
241 }
242
243 connect(ui->bg_combobox, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), this,
244 [this](int index) { ui->bg_button->setEnabled(index == 1); });
245
246 ui->use_disk_shader_cache->setTristate(true);
247 ui->use_asynchronous_gpu_emulation->setTristate(true);
248 ConfigurationShared::InsertGlobalItem(ui->aspect_ratio_combobox);
249 ConfigurationShared::InsertGlobalItem(ui->api);
136} 250}
diff --git a/src/yuzu/configuration/configure_graphics.h b/src/yuzu/configuration/configure_graphics.h
index 7e0596d9c..24f01c739 100644
--- a/src/yuzu/configuration/configure_graphics.h
+++ b/src/yuzu/configuration/configure_graphics.h
@@ -35,6 +35,8 @@ private:
35 35
36 void RetrieveVulkanDevices(); 36 void RetrieveVulkanDevices();
37 37
38 void SetupPerGameUI();
39
38 Settings::RendererBackend GetCurrentGraphicsBackend() const; 40 Settings::RendererBackend GetCurrentGraphicsBackend() const;
39 41
40 std::unique_ptr<Ui::ConfigureGraphics> ui; 42 std::unique_ptr<Ui::ConfigureGraphics> ui;
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui
index 6e75447a5..62418fc14 100644
--- a/src/yuzu/configuration/configure_graphics.ui
+++ b/src/yuzu/configuration/configure_graphics.ui
@@ -122,6 +122,29 @@
122 <item> 122 <item>
123 <layout class="QHBoxLayout" name="horizontalLayout_3"> 123 <layout class="QHBoxLayout" name="horizontalLayout_3">
124 <item> 124 <item>
125 <widget class="QComboBox" name="bg_combobox">
126 <property name="currentText">
127 <string>Use global background color</string>
128 </property>
129 <property name="currentIndex">
130 <number>0</number>
131 </property>
132 <property name="maxVisibleItems">
133 <number>10</number>
134 </property>
135 <item>
136 <property name="text">
137 <string>Use global background color</string>
138 </property>
139 </item>
140 <item>
141 <property name="text">
142 <string>Set background color:</string>
143 </property>
144 </item>
145 </widget>
146 </item>
147 <item>
125 <widget class="QLabel" name="bg_label"> 148 <widget class="QLabel" name="bg_label">
126 <property name="text"> 149 <property name="text">
127 <string>Background Color:</string> 150 <string>Background Color:</string>
@@ -129,6 +152,19 @@
129 </widget> 152 </widget>
130 </item> 153 </item>
131 <item> 154 <item>
155 <spacer name="horizontalSpacer">
156 <property name="orientation">
157 <enum>Qt::Horizontal</enum>
158 </property>
159 <property name="sizeHint" stdset="0">
160 <size>
161 <width>40</width>
162 <height>20</height>
163 </size>
164 </property>
165 </spacer>
166 </item>
167 <item>
132 <widget class="QPushButton" name="bg_button"> 168 <widget class="QPushButton" name="bg_button">
133 <property name="maximumSize"> 169 <property name="maximumSize">
134 <size> 170 <size>
diff --git a/src/yuzu/configuration/configure_graphics_advanced.cpp b/src/yuzu/configuration/configure_graphics_advanced.cpp
index be5006ad3..7c0fa7ec5 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.cpp
+++ b/src/yuzu/configuration/configure_graphics_advanced.cpp
@@ -5,6 +5,7 @@
5#include "core/core.h" 5#include "core/core.h"
6#include "core/settings.h" 6#include "core/settings.h"
7#include "ui_configure_graphics_advanced.h" 7#include "ui_configure_graphics_advanced.h"
8#include "yuzu/configuration/configuration_shared.h"
8#include "yuzu/configuration/configure_graphics_advanced.h" 9#include "yuzu/configuration/configure_graphics_advanced.h"
9 10
10ConfigureGraphicsAdvanced::ConfigureGraphicsAdvanced(QWidget* parent) 11ConfigureGraphicsAdvanced::ConfigureGraphicsAdvanced(QWidget* parent)
@@ -12,6 +13,8 @@ ConfigureGraphicsAdvanced::ConfigureGraphicsAdvanced(QWidget* parent)
12 13
13 ui->setupUi(this); 14 ui->setupUi(this);
14 15
16 SetupPerGameUI();
17
15 SetConfiguration(); 18 SetConfiguration();
16} 19}
17 20
@@ -19,26 +22,81 @@ ConfigureGraphicsAdvanced::~ConfigureGraphicsAdvanced() = default;
19 22
20void ConfigureGraphicsAdvanced::SetConfiguration() { 23void ConfigureGraphicsAdvanced::SetConfiguration() {
21 const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn(); 24 const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn();
22 ui->gpu_accuracy->setCurrentIndex(static_cast<int>(Settings::values.gpu_accuracy));
23 ui->use_vsync->setEnabled(runtime_lock); 25 ui->use_vsync->setEnabled(runtime_lock);
24 ui->use_vsync->setChecked(Settings::values.use_vsync);
25 ui->use_assembly_shaders->setEnabled(runtime_lock); 26 ui->use_assembly_shaders->setEnabled(runtime_lock);
26 ui->use_assembly_shaders->setChecked(Settings::values.use_assembly_shaders);
27 ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time);
28 ui->force_30fps_mode->setEnabled(runtime_lock); 27 ui->force_30fps_mode->setEnabled(runtime_lock);
29 ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode);
30 ui->anisotropic_filtering_combobox->setEnabled(runtime_lock); 28 ui->anisotropic_filtering_combobox->setEnabled(runtime_lock);
31 ui->anisotropic_filtering_combobox->setCurrentIndex(Settings::values.max_anisotropy); 29
30 if (Settings::configuring_global) {
31 ui->gpu_accuracy->setCurrentIndex(
32 static_cast<int>(Settings::values.gpu_accuracy.GetValue()));
33 ui->use_vsync->setChecked(Settings::values.use_vsync.GetValue());
34 ui->use_assembly_shaders->setChecked(Settings::values.use_assembly_shaders.GetValue());
35 ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue());
36 ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode.GetValue());
37 ui->anisotropic_filtering_combobox->setCurrentIndex(
38 Settings::values.max_anisotropy.GetValue());
39 } else {
40 ConfigurationShared::SetPerGameSetting(ui->gpu_accuracy, &Settings::values.gpu_accuracy);
41 ConfigurationShared::SetPerGameSetting(ui->use_vsync, &Settings::values.use_vsync);
42 ConfigurationShared::SetPerGameSetting(ui->use_assembly_shaders,
43 &Settings::values.use_assembly_shaders);
44 ConfigurationShared::SetPerGameSetting(ui->use_fast_gpu_time,
45 &Settings::values.use_fast_gpu_time);
46 ConfigurationShared::SetPerGameSetting(ui->force_30fps_mode,
47 &Settings::values.force_30fps_mode);
48 ConfigurationShared::SetPerGameSetting(ui->anisotropic_filtering_combobox,
49 &Settings::values.max_anisotropy);
50 }
32} 51}
33 52
34void ConfigureGraphicsAdvanced::ApplyConfiguration() { 53void ConfigureGraphicsAdvanced::ApplyConfiguration() {
35 auto gpu_accuracy = static_cast<Settings::GPUAccuracy>(ui->gpu_accuracy->currentIndex()); 54 // Subtract 2 if configuring per-game (separator and "use global configuration" take 2 slots)
36 Settings::values.gpu_accuracy = gpu_accuracy; 55 const auto gpu_accuracy = static_cast<Settings::GPUAccuracy>(
37 Settings::values.use_vsync = ui->use_vsync->isChecked(); 56 ui->gpu_accuracy->currentIndex() -
38 Settings::values.use_assembly_shaders = ui->use_assembly_shaders->isChecked(); 57 ((Settings::configuring_global) ? 0 : ConfigurationShared::USE_GLOBAL_OFFSET));
39 Settings::values.use_fast_gpu_time = ui->use_fast_gpu_time->isChecked(); 58
40 Settings::values.force_30fps_mode = ui->force_30fps_mode->isChecked(); 59 if (Settings::configuring_global) {
41 Settings::values.max_anisotropy = ui->anisotropic_filtering_combobox->currentIndex(); 60 // Must guard in case of a during-game configuration when set to be game-specific.
61 if (Settings::values.gpu_accuracy.UsingGlobal()) {
62 Settings::values.gpu_accuracy.SetValue(gpu_accuracy);
63 }
64 if (Settings::values.use_vsync.UsingGlobal()) {
65 Settings::values.use_vsync.SetValue(ui->use_vsync->isChecked());
66 }
67 if (Settings::values.use_assembly_shaders.UsingGlobal()) {
68 Settings::values.use_assembly_shaders.SetValue(ui->use_assembly_shaders->isChecked());
69 }
70 if (Settings::values.use_fast_gpu_time.UsingGlobal()) {
71 Settings::values.use_fast_gpu_time.SetValue(ui->use_fast_gpu_time->isChecked());
72 }
73 if (Settings::values.force_30fps_mode.UsingGlobal()) {
74 Settings::values.force_30fps_mode.SetValue(ui->force_30fps_mode->isChecked());
75 }
76 if (Settings::values.max_anisotropy.UsingGlobal()) {
77 Settings::values.max_anisotropy.SetValue(
78 ui->anisotropic_filtering_combobox->currentIndex());
79 }
80 } else {
81 ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy,
82 ui->anisotropic_filtering_combobox);
83 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vsync, ui->use_vsync);
84 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_assembly_shaders,
85 ui->use_assembly_shaders);
86 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_fast_gpu_time,
87 ui->use_fast_gpu_time);
88 ConfigurationShared::ApplyPerGameSetting(&Settings::values.force_30fps_mode,
89 ui->force_30fps_mode);
90 ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy,
91 ui->anisotropic_filtering_combobox);
92
93 if (ui->gpu_accuracy->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
94 Settings::values.gpu_accuracy.SetGlobal(true);
95 } else {
96 Settings::values.gpu_accuracy.SetGlobal(false);
97 Settings::values.gpu_accuracy.SetValue(gpu_accuracy);
98 }
99 }
42} 100}
43 101
44void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) { 102void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) {
@@ -52,3 +110,25 @@ void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) {
52void ConfigureGraphicsAdvanced::RetranslateUI() { 110void ConfigureGraphicsAdvanced::RetranslateUI() {
53 ui->retranslateUi(this); 111 ui->retranslateUi(this);
54} 112}
113
114void ConfigureGraphicsAdvanced::SetupPerGameUI() {
115 // Disable if not global (only happens during game)
116 if (Settings::configuring_global) {
117 ui->gpu_accuracy->setEnabled(Settings::values.gpu_accuracy.UsingGlobal());
118 ui->use_vsync->setEnabled(Settings::values.use_vsync.UsingGlobal());
119 ui->use_assembly_shaders->setEnabled(Settings::values.use_assembly_shaders.UsingGlobal());
120 ui->use_fast_gpu_time->setEnabled(Settings::values.use_fast_gpu_time.UsingGlobal());
121 ui->force_30fps_mode->setEnabled(Settings::values.force_30fps_mode.UsingGlobal());
122 ui->anisotropic_filtering_combobox->setEnabled(
123 Settings::values.max_anisotropy.UsingGlobal());
124
125 return;
126 }
127
128 ConfigurationShared::InsertGlobalItem(ui->gpu_accuracy);
129 ui->use_vsync->setTristate(true);
130 ui->use_assembly_shaders->setTristate(true);
131 ui->use_fast_gpu_time->setTristate(true);
132 ui->force_30fps_mode->setTristate(true);
133 ConfigurationShared::InsertGlobalItem(ui->anisotropic_filtering_combobox);
134}
diff --git a/src/yuzu/configuration/configure_graphics_advanced.h b/src/yuzu/configuration/configure_graphics_advanced.h
index bbc9d4355..c043588ff 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.h
+++ b/src/yuzu/configuration/configure_graphics_advanced.h
@@ -26,5 +26,7 @@ private:
26 26
27 void SetConfiguration(); 27 void SetConfiguration();
28 28
29 void SetupPerGameUI();
30
29 std::unique_ptr<Ui::ConfigureGraphicsAdvanced> ui; 31 std::unique_ptr<Ui::ConfigureGraphicsAdvanced> ui;
30}; 32};
diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp
new file mode 100644
index 000000000..1e49f0787
--- /dev/null
+++ b/src/yuzu/configuration/configure_per_game.cpp
@@ -0,0 +1,140 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <memory>
7#include <utility>
8
9#include <QCheckBox>
10#include <QHeaderView>
11#include <QMenu>
12#include <QStandardItemModel>
13#include <QString>
14#include <QTimer>
15#include <QTreeView>
16
17#include "common/common_paths.h"
18#include "common/file_util.h"
19#include "core/file_sys/control_metadata.h"
20#include "core/file_sys/patch_manager.h"
21#include "core/file_sys/xts_archive.h"
22#include "core/loader/loader.h"
23#include "ui_configure_per_game.h"
24#include "yuzu/configuration/config.h"
25#include "yuzu/configuration/configure_input.h"
26#include "yuzu/configuration/configure_per_game.h"
27#include "yuzu/uisettings.h"
28#include "yuzu/util/util.h"
29
30ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id)
31 : QDialog(parent), ui(std::make_unique<Ui::ConfigurePerGame>()), title_id(title_id) {
32 game_config = std::make_unique<Config>(fmt::format("{:016X}.ini", title_id), false);
33
34 Settings::configuring_global = false;
35
36 ui->setupUi(this);
37 setFocusPolicy(Qt::ClickFocus);
38 setWindowTitle(tr("Properties"));
39
40 ui->addonsTab->SetTitleId(title_id);
41
42 scene = new QGraphicsScene;
43 ui->icon_view->setScene(scene);
44
45 LoadConfiguration();
46}
47
48ConfigurePerGame::~ConfigurePerGame() = default;
49
50void ConfigurePerGame::ApplyConfiguration() {
51 ui->addonsTab->ApplyConfiguration();
52 ui->generalTab->ApplyConfiguration();
53 ui->systemTab->ApplyConfiguration();
54 ui->graphicsTab->ApplyConfiguration();
55 ui->graphicsAdvancedTab->ApplyConfiguration();
56 ui->audioTab->ApplyConfiguration();
57
58 Settings::Apply();
59 Settings::LogSettings();
60
61 game_config->Save();
62}
63
64void ConfigurePerGame::changeEvent(QEvent* event) {
65 if (event->type() == QEvent::LanguageChange) {
66 RetranslateUI();
67 }
68
69 QDialog::changeEvent(event);
70}
71
72void ConfigurePerGame::RetranslateUI() {
73 ui->retranslateUi(this);
74}
75
76void ConfigurePerGame::LoadFromFile(FileSys::VirtualFile file) {
77 this->file = std::move(file);
78 LoadConfiguration();
79}
80
81void ConfigurePerGame::LoadConfiguration() {
82 if (file == nullptr) {
83 return;
84 }
85
86 ui->addonsTab->LoadFromFile(file);
87
88 ui->display_title_id->setText(
89 QStringLiteral("%1").arg(title_id, 16, 16, QLatin1Char{'0'}).toUpper());
90
91 FileSys::PatchManager pm{title_id};
92 const auto control = pm.GetControlMetadata();
93 const auto loader = Loader::GetLoader(file);
94
95 if (control.first != nullptr) {
96 ui->display_version->setText(QString::fromStdString(control.first->GetVersionString()));
97 ui->display_name->setText(QString::fromStdString(control.first->GetApplicationName()));
98 ui->display_developer->setText(QString::fromStdString(control.first->GetDeveloperName()));
99 } else {
100 std::string title;
101 if (loader->ReadTitle(title) == Loader::ResultStatus::Success)
102 ui->display_name->setText(QString::fromStdString(title));
103
104 FileSys::NACP nacp;
105 if (loader->ReadControlData(nacp) == Loader::ResultStatus::Success)
106 ui->display_developer->setText(QString::fromStdString(nacp.GetDeveloperName()));
107
108 ui->display_version->setText(QStringLiteral("1.0.0"));
109 }
110
111 if (control.second != nullptr) {
112 scene->clear();
113
114 QPixmap map;
115 const auto bytes = control.second->ReadAllBytes();
116 map.loadFromData(bytes.data(), static_cast<u32>(bytes.size()));
117
118 scene->addPixmap(map.scaled(ui->icon_view->width(), ui->icon_view->height(),
119 Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
120 } else {
121 std::vector<u8> bytes;
122 if (loader->ReadIcon(bytes) == Loader::ResultStatus::Success) {
123 scene->clear();
124
125 QPixmap map;
126 map.loadFromData(bytes.data(), static_cast<u32>(bytes.size()));
127
128 scene->addPixmap(map.scaled(ui->icon_view->width(), ui->icon_view->height(),
129 Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
130 }
131 }
132
133 ui->display_filename->setText(QString::fromStdString(file->GetName()));
134
135 ui->display_format->setText(
136 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())));
137
138 const auto valueText = ReadableByteSize(file->GetSize());
139 ui->display_size->setText(valueText);
140}
diff --git a/src/yuzu/configuration/configure_per_game.h b/src/yuzu/configuration/configure_per_game.h
new file mode 100644
index 000000000..5f9a08cef
--- /dev/null
+++ b/src/yuzu/configuration/configure_per_game.h
@@ -0,0 +1,51 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <vector>
9
10#include <QDialog>
11#include <QList>
12
13#include "core/file_sys/vfs_types.h"
14#include "yuzu/configuration/config.h"
15
16class QGraphicsScene;
17class QStandardItem;
18class QStandardItemModel;
19class QTreeView;
20class QVBoxLayout;
21
22namespace Ui {
23class ConfigurePerGame;
24}
25
26class ConfigurePerGame : public QDialog {
27 Q_OBJECT
28
29public:
30 explicit ConfigurePerGame(QWidget* parent, u64 title_id);
31 ~ConfigurePerGame() override;
32
33 /// Save all button configurations to settings file
34 void ApplyConfiguration();
35
36 void LoadFromFile(FileSys::VirtualFile file);
37
38private:
39 void changeEvent(QEvent* event) override;
40 void RetranslateUI();
41
42 void LoadConfiguration();
43
44 std::unique_ptr<Ui::ConfigurePerGame> ui;
45 FileSys::VirtualFile file;
46 u64 title_id;
47
48 QGraphicsScene* scene;
49
50 std::unique_ptr<Config> game_config;
51};
diff --git a/src/yuzu/configuration/configure_per_game.ui b/src/yuzu/configuration/configure_per_game.ui
new file mode 100644
index 000000000..d2057c4ab
--- /dev/null
+++ b/src/yuzu/configuration/configure_per_game.ui
@@ -0,0 +1,350 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigurePerGame</class>
4 <widget class="QDialog" name="ConfigurePerGame">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>800</width>
10 <height>600</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Dialog</string>
15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout_3">
17 <item>
18 <layout class="QHBoxLayout" name="horizontalLayout">
19 <item>
20 <widget class="QGroupBox" name="groupBox">
21 <property name="sizePolicy">
22 <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
23 <horstretch>0</horstretch>
24 <verstretch>0</verstretch>
25 </sizepolicy>
26 </property>
27 <property name="title">
28 <string>Info</string>
29 </property>
30 <layout class="QVBoxLayout" name="verticalLayout">
31 <item alignment="Qt::AlignHCenter">
32 <widget class="QGraphicsView" name="icon_view">
33 <property name="sizePolicy">
34 <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
35 <horstretch>0</horstretch>
36 <verstretch>0</verstretch>
37 </sizepolicy>
38 </property>
39 <property name="minimumSize">
40 <size>
41 <width>256</width>
42 <height>256</height>
43 </size>
44 </property>
45 <property name="maximumSize">
46 <size>
47 <width>256</width>
48 <height>256</height>
49 </size>
50 </property>
51 <property name="verticalScrollBarPolicy">
52 <enum>Qt::ScrollBarAlwaysOff</enum>
53 </property>
54 <property name="horizontalScrollBarPolicy">
55 <enum>Qt::ScrollBarAlwaysOff</enum>
56 </property>
57 <property name="interactive">
58 <bool>false</bool>
59 </property>
60 </widget>
61 </item>
62 <item>
63 <layout class="QGridLayout" name="gridLayout_2">
64 <item row="6" column="1">
65 <widget class="QLineEdit" name="display_size">
66 <property name="enabled">
67 <bool>true</bool>
68 </property>
69 <property name="readOnly">
70 <bool>true</bool>
71 </property>
72 </widget>
73 </item>
74 <item row="3" column="1">
75 <widget class="QLineEdit" name="display_version">
76 <property name="enabled">
77 <bool>true</bool>
78 </property>
79 <property name="readOnly">
80 <bool>true</bool>
81 </property>
82 </widget>
83 </item>
84 <item row="1" column="0">
85 <widget class="QLabel" name="label">
86 <property name="text">
87 <string>Name</string>
88 </property>
89 </widget>
90 </item>
91 <item row="4" column="0">
92 <widget class="QLabel" name="label_4">
93 <property name="text">
94 <string>Title ID</string>
95 </property>
96 </widget>
97 </item>
98 <item row="4" column="1">
99 <widget class="QLineEdit" name="display_title_id">
100 <property name="enabled">
101 <bool>true</bool>
102 </property>
103 <property name="readOnly">
104 <bool>true</bool>
105 </property>
106 </widget>
107 </item>
108 <item row="7" column="1">
109 <widget class="QLineEdit" name="display_filename">
110 <property name="enabled">
111 <bool>true</bool>
112 </property>
113 <property name="readOnly">
114 <bool>true</bool>
115 </property>
116 </widget>
117 </item>
118 <item row="5" column="1">
119 <widget class="QLineEdit" name="display_format">
120 <property name="enabled">
121 <bool>true</bool>
122 </property>
123 <property name="readOnly">
124 <bool>true</bool>
125 </property>
126 </widget>
127 </item>
128 <item row="7" column="0">
129 <widget class="QLabel" name="label_7">
130 <property name="text">
131 <string>Filename</string>
132 </property>
133 </widget>
134 </item>
135 <item row="1" column="1">
136 <widget class="QLineEdit" name="display_name">
137 <property name="enabled">
138 <bool>true</bool>
139 </property>
140 <property name="readOnly">
141 <bool>true</bool>
142 </property>
143 </widget>
144 </item>
145 <item row="2" column="1">
146 <widget class="QLineEdit" name="display_developer">
147 <property name="enabled">
148 <bool>true</bool>
149 </property>
150 <property name="readOnly">
151 <bool>true</bool>
152 </property>
153 </widget>
154 </item>
155 <item row="5" column="0">
156 <widget class="QLabel" name="label_5">
157 <property name="text">
158 <string>Format</string>
159 </property>
160 </widget>
161 </item>
162 <item row="3" column="0">
163 <widget class="QLabel" name="label_3">
164 <property name="text">
165 <string>Version</string>
166 </property>
167 </widget>
168 </item>
169 <item row="6" column="0">
170 <widget class="QLabel" name="label_6">
171 <property name="text">
172 <string>Size</string>
173 </property>
174 </widget>
175 </item>
176 <item row="2" column="0">
177 <widget class="QLabel" name="label_2">
178 <property name="text">
179 <string>Developer</string>
180 </property>
181 </widget>
182 </item>
183 </layout>
184 </item>
185 <item>
186 <spacer name="verticalSpacer">
187 <property name="orientation">
188 <enum>Qt::Vertical</enum>
189 </property>
190 <property name="sizeHint" stdset="0">
191 <size>
192 <width>20</width>
193 <height>40</height>
194 </size>
195 </property>
196 </spacer>
197 </item>
198 </layout>
199 </widget>
200 </item>
201 <item>
202 <layout class="QVBoxLayout" name="VerticalLayout">
203 <item>
204 <layout class="QVBoxLayout" name="verticalLayout_2"/>
205 </item>
206 <item>
207 <widget class="QTabWidget" name="tabWidget">
208 <property name="enabled">
209 <bool>true</bool>
210 </property>
211 <property name="currentIndex">
212 <number>0</number>
213 </property>
214 <property name="usesScrollButtons">
215 <bool>true</bool>
216 </property>
217 <property name="documentMode">
218 <bool>false</bool>
219 </property>
220 <property name="tabsClosable">
221 <bool>false</bool>
222 </property>
223 <widget class="ConfigurePerGameAddons" name="addonsTab">
224 <attribute name="title">
225 <string>Add-Ons</string>
226 </attribute>
227 </widget>
228 <widget class="ConfigureGeneral" name="generalTab">
229 <attribute name="title">
230 <string>General</string>
231 </attribute>
232 </widget>
233 <widget class="ConfigureSystem" name="systemTab">
234 <attribute name="title">
235 <string>System</string>
236 </attribute>
237 </widget>
238 <widget class="ConfigureGraphics" name="graphicsTab">
239 <attribute name="title">
240 <string>Graphics</string>
241 </attribute>
242 </widget>
243 <widget class="ConfigureGraphicsAdvanced" name="graphicsAdvancedTab">
244 <attribute name="title">
245 <string>Adv. Graphics</string>
246 </attribute>
247 </widget>
248 <widget class="ConfigureAudio" name="audioTab">
249 <attribute name="title">
250 <string>Audio</string>
251 </attribute>
252 </widget>
253 </widget>
254 </item>
255 </layout>
256 </item>
257 </layout>
258 </item>
259 <item>
260 <widget class="QDialogButtonBox" name="buttonBox">
261 <property name="sizePolicy">
262 <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
263 <horstretch>0</horstretch>
264 <verstretch>0</verstretch>
265 </sizepolicy>
266 </property>
267 <property name="orientation">
268 <enum>Qt::Horizontal</enum>
269 </property>
270 <property name="standardButtons">
271 <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
272 </property>
273 </widget>
274 </item>
275 </layout>
276 </widget>
277 <customwidgets>
278 <customwidget>
279 <class>ConfigureGeneral</class>
280 <extends>QWidget</extends>
281 <header>configuration/configure_general.h</header>
282 <container>1</container>
283 </customwidget>
284 <customwidget>
285 <class>ConfigureSystem</class>
286 <extends>QWidget</extends>
287 <header>configuration/configure_system.h</header>
288 <container>1</container>
289 </customwidget>
290 <customwidget>
291 <class>ConfigureAudio</class>
292 <extends>QWidget</extends>
293 <header>configuration/configure_audio.h</header>
294 <container>1</container>
295 </customwidget>
296 <customwidget>
297 <class>ConfigureGraphics</class>
298 <extends>QWidget</extends>
299 <header>configuration/configure_graphics.h</header>
300 <container>1</container>
301 </customwidget>
302 <customwidget>
303 <class>ConfigureGraphicsAdvanced</class>
304 <extends>QWidget</extends>
305 <header>configuration/configure_graphics_advanced.h</header>
306 <container>1</container>
307 </customwidget>
308 <customwidget>
309 <class>ConfigurePerGameAddons</class>
310 <extends>QWidget</extends>
311 <header>configuration/configure_per_game_addons.h</header>
312 <container>1</container>
313 </customwidget>
314 </customwidgets>
315 <resources/>
316 <connections>
317 <connection>
318 <sender>buttonBox</sender>
319 <signal>accepted()</signal>
320 <receiver>ConfigurePerGame</receiver>
321 <slot>accept()</slot>
322 <hints>
323 <hint type="sourcelabel">
324 <x>248</x>
325 <y>254</y>
326 </hint>
327 <hint type="destinationlabel">
328 <x>157</x>
329 <y>274</y>
330 </hint>
331 </hints>
332 </connection>
333 <connection>
334 <sender>buttonBox</sender>
335 <signal>rejected()</signal>
336 <receiver>ConfigurePerGame</receiver>
337 <slot>reject()</slot>
338 <hints>
339 <hint type="sourcelabel">
340 <x>316</x>
341 <y>260</y>
342 </hint>
343 <hint type="destinationlabel">
344 <x>286</x>
345 <y>274</y>
346 </hint>
347 </hints>
348 </connection>
349 </connections>
350</ui>
diff --git a/src/yuzu/configuration/configure_per_general.cpp b/src/yuzu/configuration/configure_per_game_addons.cpp
index d7f259f12..478d5d3a1 100644
--- a/src/yuzu/configuration/configure_per_general.cpp
+++ b/src/yuzu/configuration/configure_per_game_addons.cpp
@@ -15,23 +15,20 @@
15 15
16#include "common/common_paths.h" 16#include "common/common_paths.h"
17#include "common/file_util.h" 17#include "common/file_util.h"
18#include "core/file_sys/control_metadata.h" 18#include "core/core.h"
19#include "core/file_sys/patch_manager.h" 19#include "core/file_sys/patch_manager.h"
20#include "core/file_sys/xts_archive.h" 20#include "core/file_sys/xts_archive.h"
21#include "core/loader/loader.h" 21#include "core/loader/loader.h"
22#include "ui_configure_per_general.h" 22#include "ui_configure_per_game_addons.h"
23#include "yuzu/configuration/config.h" 23#include "yuzu/configuration/config.h"
24#include "yuzu/configuration/configure_input.h" 24#include "yuzu/configuration/configure_input.h"
25#include "yuzu/configuration/configure_per_general.h" 25#include "yuzu/configuration/configure_per_game_addons.h"
26#include "yuzu/uisettings.h" 26#include "yuzu/uisettings.h"
27#include "yuzu/util/util.h" 27#include "yuzu/util/util.h"
28 28
29ConfigurePerGameGeneral::ConfigurePerGameGeneral(QWidget* parent, u64 title_id) 29ConfigurePerGameAddons::ConfigurePerGameAddons(QWidget* parent)
30 : QDialog(parent), ui(std::make_unique<Ui::ConfigurePerGameGeneral>()), title_id(title_id) { 30 : QWidget(parent), ui(new Ui::ConfigurePerGameAddons) {
31
32 ui->setupUi(this); 31 ui->setupUi(this);
33 setFocusPolicy(Qt::ClickFocus);
34 setWindowTitle(tr("Properties"));
35 32
36 layout = new QVBoxLayout; 33 layout = new QVBoxLayout;
37 tree_view = new QTreeView; 34 tree_view = new QTreeView;
@@ -52,7 +49,7 @@ ConfigurePerGameGeneral::ConfigurePerGameGeneral(QWidget* parent, u64 title_id)
52 item_model->setHeaderData(1, Qt::Horizontal, tr("Version")); 49 item_model->setHeaderData(1, Qt::Horizontal, tr("Version"));
53 50
54 // We must register all custom types with the Qt Automoc system so that we are able to use it 51 // We must register all custom types with the Qt Automoc system so that we are able to use it
55 // with signals/slots. In this case, QList falls under the umbrells of custom types. 52 // with signals/slots. In this case, QList falls under the umbrella of custom types.
56 qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>"); 53 qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>");
57 54
58 layout->setContentsMargins(0, 0, 0, 0); 55 layout->setContentsMargins(0, 0, 0, 0);
@@ -61,18 +58,15 @@ ConfigurePerGameGeneral::ConfigurePerGameGeneral(QWidget* parent, u64 title_id)
61 58
62 ui->scrollArea->setLayout(layout); 59 ui->scrollArea->setLayout(layout);
63 60
64 scene = new QGraphicsScene; 61 ui->scrollArea->setEnabled(!Core::System::GetInstance().IsPoweredOn());
65 ui->icon_view->setScene(scene);
66 62
67 connect(item_model, &QStandardItemModel::itemChanged, 63 connect(item_model, &QStandardItemModel::itemChanged,
68 [] { UISettings::values.is_game_list_reload_pending.exchange(true); }); 64 [] { UISettings::values.is_game_list_reload_pending.exchange(true); });
69
70 LoadConfiguration();
71} 65}
72 66
73ConfigurePerGameGeneral::~ConfigurePerGameGeneral() = default; 67ConfigurePerGameAddons::~ConfigurePerGameAddons() = default;
74 68
75void ConfigurePerGameGeneral::ApplyConfiguration() { 69void ConfigurePerGameAddons::ApplyConfiguration() {
76 std::vector<std::string> disabled_addons; 70 std::vector<std::string> disabled_addons;
77 71
78 for (const auto& item : list_items) { 72 for (const auto& item : list_items) {
@@ -92,72 +86,35 @@ void ConfigurePerGameGeneral::ApplyConfiguration() {
92 Settings::values.disabled_addons[title_id] = disabled_addons; 86 Settings::values.disabled_addons[title_id] = disabled_addons;
93} 87}
94 88
95void ConfigurePerGameGeneral::changeEvent(QEvent* event) { 89void ConfigurePerGameAddons::LoadFromFile(FileSys::VirtualFile file) {
90 this->file = std::move(file);
91 LoadConfiguration();
92}
93
94void ConfigurePerGameAddons::SetTitleId(u64 id) {
95 this->title_id = id;
96}
97
98void ConfigurePerGameAddons::changeEvent(QEvent* event) {
96 if (event->type() == QEvent::LanguageChange) { 99 if (event->type() == QEvent::LanguageChange) {
97 RetranslateUI(); 100 RetranslateUI();
98 } 101 }
99 102
100 QDialog::changeEvent(event); 103 QWidget::changeEvent(event);
101} 104}
102 105
103void ConfigurePerGameGeneral::RetranslateUI() { 106void ConfigurePerGameAddons::RetranslateUI() {
104 ui->retranslateUi(this); 107 ui->retranslateUi(this);
105} 108}
106 109
107void ConfigurePerGameGeneral::LoadFromFile(FileSys::VirtualFile file) { 110void ConfigurePerGameAddons::LoadConfiguration() {
108 this->file = std::move(file);
109 LoadConfiguration();
110}
111
112void ConfigurePerGameGeneral::LoadConfiguration() {
113 if (file == nullptr) { 111 if (file == nullptr) {
114 return; 112 return;
115 } 113 }
116 114
117 ui->display_title_id->setText(QString::fromStdString(fmt::format("{:016X}", title_id)));
118
119 FileSys::PatchManager pm{title_id}; 115 FileSys::PatchManager pm{title_id};
120 const auto control = pm.GetControlMetadata();
121 const auto loader = Loader::GetLoader(file); 116 const auto loader = Loader::GetLoader(file);
122 117
123 if (control.first != nullptr) {
124 ui->display_version->setText(QString::fromStdString(control.first->GetVersionString()));
125 ui->display_name->setText(QString::fromStdString(control.first->GetApplicationName()));
126 ui->display_developer->setText(QString::fromStdString(control.first->GetDeveloperName()));
127 } else {
128 std::string title;
129 if (loader->ReadTitle(title) == Loader::ResultStatus::Success)
130 ui->display_name->setText(QString::fromStdString(title));
131
132 FileSys::NACP nacp;
133 if (loader->ReadControlData(nacp) == Loader::ResultStatus::Success)
134 ui->display_developer->setText(QString::fromStdString(nacp.GetDeveloperName()));
135
136 ui->display_version->setText(QStringLiteral("1.0.0"));
137 }
138
139 if (control.second != nullptr) {
140 scene->clear();
141
142 QPixmap map;
143 const auto bytes = control.second->ReadAllBytes();
144 map.loadFromData(bytes.data(), static_cast<u32>(bytes.size()));
145
146 scene->addPixmap(map.scaled(ui->icon_view->width(), ui->icon_view->height(),
147 Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
148 } else {
149 std::vector<u8> bytes;
150 if (loader->ReadIcon(bytes) == Loader::ResultStatus::Success) {
151 scene->clear();
152
153 QPixmap map;
154 map.loadFromData(bytes.data(), static_cast<u32>(bytes.size()));
155
156 scene->addPixmap(map.scaled(ui->icon_view->width(), ui->icon_view->height(),
157 Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
158 }
159 }
160
161 FileSys::VirtualFile update_raw; 118 FileSys::VirtualFile update_raw;
162 loader->ReadUpdateRaw(update_raw); 119 loader->ReadUpdateRaw(update_raw);
163 120
@@ -182,12 +139,4 @@ void ConfigurePerGameGeneral::LoadConfiguration() {
182 } 139 }
183 140
184 tree_view->setColumnWidth(0, 5 * tree_view->width() / 16); 141 tree_view->setColumnWidth(0, 5 * tree_view->width() / 16);
185
186 ui->display_filename->setText(QString::fromStdString(file->GetName()));
187
188 ui->display_format->setText(
189 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())));
190
191 const auto valueText = ReadableByteSize(file->GetSize());
192 ui->display_size->setText(valueText);
193} 142}
diff --git a/src/yuzu/configuration/configure_per_general.h b/src/yuzu/configuration/configure_per_game_addons.h
index a3b2cdeff..a00ec3539 100644
--- a/src/yuzu/configuration/configure_per_general.h
+++ b/src/yuzu/configuration/configure_per_game_addons.h
@@ -1,4 +1,4 @@
1// Copyright 2016 Citra Emulator Project 1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
@@ -7,7 +7,6 @@
7#include <memory> 7#include <memory>
8#include <vector> 8#include <vector>
9 9
10#include <QDialog>
11#include <QList> 10#include <QList>
12 11
13#include "core/file_sys/vfs_types.h" 12#include "core/file_sys/vfs_types.h"
@@ -19,35 +18,36 @@ class QTreeView;
19class QVBoxLayout; 18class QVBoxLayout;
20 19
21namespace Ui { 20namespace Ui {
22class ConfigurePerGameGeneral; 21class ConfigurePerGameAddons;
23} 22}
24 23
25class ConfigurePerGameGeneral : public QDialog { 24class ConfigurePerGameAddons : public QWidget {
26 Q_OBJECT 25 Q_OBJECT
27 26
28public: 27public:
29 explicit ConfigurePerGameGeneral(QWidget* parent, u64 title_id); 28 explicit ConfigurePerGameAddons(QWidget* parent = nullptr);
30 ~ConfigurePerGameGeneral() override; 29 ~ConfigurePerGameAddons() override;
31 30
32 /// Save all button configurations to settings file 31 /// Save all button configurations to settings file
33 void ApplyConfiguration(); 32 void ApplyConfiguration();
34 33
35 void LoadFromFile(FileSys::VirtualFile file); 34 void LoadFromFile(FileSys::VirtualFile file);
36 35
36 void SetTitleId(u64 id);
37
37private: 38private:
38 void changeEvent(QEvent* event) override; 39 void changeEvent(QEvent* event) override;
39 void RetranslateUI(); 40 void RetranslateUI();
40 41
41 void LoadConfiguration(); 42 void LoadConfiguration();
42 43
43 std::unique_ptr<Ui::ConfigurePerGameGeneral> ui; 44 std::unique_ptr<Ui::ConfigurePerGameAddons> ui;
44 FileSys::VirtualFile file; 45 FileSys::VirtualFile file;
45 u64 title_id; 46 u64 title_id;
46 47
47 QVBoxLayout* layout; 48 QVBoxLayout* layout;
48 QTreeView* tree_view; 49 QTreeView* tree_view;
49 QStandardItemModel* item_model; 50 QStandardItemModel* item_model;
50 QGraphicsScene* scene;
51 51
52 std::vector<QList<QStandardItem*>> list_items; 52 std::vector<QList<QStandardItem*>> list_items;
53}; 53};
diff --git a/src/yuzu/configuration/configure_per_game_addons.ui b/src/yuzu/configuration/configure_per_game_addons.ui
new file mode 100644
index 000000000..aefdebfcd
--- /dev/null
+++ b/src/yuzu/configuration/configure_per_game_addons.ui
@@ -0,0 +1,38 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigurePerGameAddons</class>
4 <widget class="QWidget" name="ConfigurePerGameAddons">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>400</width>
10 <height>300</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Form</string>
15 </property>
16 <layout class="QGridLayout" name="gridLayout">
17 <item row="0" column="0">
18 <widget class="QScrollArea" name="scrollArea">
19 <property name="widgetResizable">
20 <bool>true</bool>
21 </property>
22 <widget class="QWidget" name="scrollAreaWidgetContents">
23 <property name="geometry">
24 <rect>
25 <x>0</x>
26 <y>0</y>
27 <width>380</width>
28 <height>280</height>
29 </rect>
30 </property>
31 </widget>
32 </widget>
33 </item>
34 </layout>
35 </widget>
36 <resources/>
37 <connections/>
38</ui>
diff --git a/src/yuzu/configuration/configure_per_general.ui b/src/yuzu/configuration/configure_per_general.ui
deleted file mode 100644
index 8fdd96fa4..000000000
--- a/src/yuzu/configuration/configure_per_general.ui
+++ /dev/null
@@ -1,276 +0,0 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigurePerGameGeneral</class>
4 <widget class="QDialog" name="ConfigurePerGameGeneral">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>400</width>
10 <height>520</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>ConfigurePerGameGeneral</string>
15 </property>
16 <layout class="QHBoxLayout" name="HorizontalLayout">
17 <item>
18 <layout class="QVBoxLayout" name="VerticalLayout">
19 <item>
20 <widget class="QGroupBox" name="GeneralGroupBox">
21 <property name="title">
22 <string>Info</string>
23 </property>
24 <layout class="QHBoxLayout" name="GeneralHorizontalLayout">
25 <item>
26 <layout class="QGridLayout" name="gridLayout_2">
27 <item row="6" column="1" colspan="2">
28 <widget class="QLineEdit" name="display_filename">
29 <property name="enabled">
30 <bool>true</bool>
31 </property>
32 <property name="readOnly">
33 <bool>true</bool>
34 </property>
35 </widget>
36 </item>
37 <item row="0" column="1">
38 <widget class="QLineEdit" name="display_name">
39 <property name="enabled">
40 <bool>true</bool>
41 </property>
42 <property name="readOnly">
43 <bool>true</bool>
44 </property>
45 </widget>
46 </item>
47 <item row="1" column="0">
48 <widget class="QLabel" name="label_2">
49 <property name="text">
50 <string>Developer</string>
51 </property>
52 </widget>
53 </item>
54 <item row="5" column="1" colspan="2">
55 <widget class="QLineEdit" name="display_size">
56 <property name="enabled">
57 <bool>true</bool>
58 </property>
59 <property name="readOnly">
60 <bool>true</bool>
61 </property>
62 </widget>
63 </item>
64 <item row="0" column="0">
65 <widget class="QLabel" name="label">
66 <property name="text">
67 <string>Name</string>
68 </property>
69 </widget>
70 </item>
71 <item row="6" column="0">
72 <widget class="QLabel" name="label_7">
73 <property name="text">
74 <string>Filename</string>
75 </property>
76 </widget>
77 </item>
78 <item row="2" column="0">
79 <widget class="QLabel" name="label_3">
80 <property name="text">
81 <string>Version</string>
82 </property>
83 </widget>
84 </item>
85 <item row="4" column="0">
86 <widget class="QLabel" name="label_5">
87 <property name="text">
88 <string>Format</string>
89 </property>
90 </widget>
91 </item>
92 <item row="2" column="1">
93 <widget class="QLineEdit" name="display_version">
94 <property name="enabled">
95 <bool>true</bool>
96 </property>
97 <property name="readOnly">
98 <bool>true</bool>
99 </property>
100 </widget>
101 </item>
102 <item row="4" column="1">
103 <widget class="QLineEdit" name="display_format">
104 <property name="enabled">
105 <bool>true</bool>
106 </property>
107 <property name="readOnly">
108 <bool>true</bool>
109 </property>
110 </widget>
111 </item>
112 <item row="5" column="0">
113 <widget class="QLabel" name="label_6">
114 <property name="text">
115 <string>Size</string>
116 </property>
117 </widget>
118 </item>
119 <item row="1" column="1">
120 <widget class="QLineEdit" name="display_developer">
121 <property name="enabled">
122 <bool>true</bool>
123 </property>
124 <property name="readOnly">
125 <bool>true</bool>
126 </property>
127 </widget>
128 </item>
129 <item row="3" column="0">
130 <widget class="QLabel" name="label_4">
131 <property name="text">
132 <string>Title ID</string>
133 </property>
134 </widget>
135 </item>
136 <item row="3" column="1">
137 <widget class="QLineEdit" name="display_title_id">
138 <property name="enabled">
139 <bool>true</bool>
140 </property>
141 <property name="readOnly">
142 <bool>true</bool>
143 </property>
144 </widget>
145 </item>
146 <item row="0" column="2" rowspan="5">
147 <widget class="QGraphicsView" name="icon_view">
148 <property name="sizePolicy">
149 <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
150 <horstretch>0</horstretch>
151 <verstretch>0</verstretch>
152 </sizepolicy>
153 </property>
154 <property name="minimumSize">
155 <size>
156 <width>128</width>
157 <height>128</height>
158 </size>
159 </property>
160 <property name="maximumSize">
161 <size>
162 <width>128</width>
163 <height>128</height>
164 </size>
165 </property>
166 <property name="verticalScrollBarPolicy">
167 <enum>Qt::ScrollBarAlwaysOff</enum>
168 </property>
169 <property name="horizontalScrollBarPolicy">
170 <enum>Qt::ScrollBarAlwaysOff</enum>
171 </property>
172 <property name="sizeAdjustPolicy">
173 <enum>QAbstractScrollArea::AdjustToContents</enum>
174 </property>
175 <property name="interactive">
176 <bool>false</bool>
177 </property>
178 </widget>
179 </item>
180 </layout>
181 </item>
182 </layout>
183 </widget>
184 </item>
185 <item>
186 <widget class="QGroupBox" name="PerformanceGroupBox">
187 <property name="title">
188 <string>Add-Ons</string>
189 </property>
190 <layout class="QHBoxLayout" name="PerformanceHorizontalLayout">
191 <item>
192 <widget class="QScrollArea" name="scrollArea">
193 <property name="widgetResizable">
194 <bool>true</bool>
195 </property>
196 <widget class="QWidget" name="scrollAreaWidgetContents">
197 <property name="geometry">
198 <rect>
199 <x>0</x>
200 <y>0</y>
201 <width>350</width>
202 <height>169</height>
203 </rect>
204 </property>
205 </widget>
206 </widget>
207 </item>
208 <item>
209 <layout class="QVBoxLayout" name="PerformanceVerticalLayout"/>
210 </item>
211 </layout>
212 </widget>
213 </item>
214 <item>
215 <spacer name="verticalSpacer">
216 <property name="orientation">
217 <enum>Qt::Vertical</enum>
218 </property>
219 <property name="sizeType">
220 <enum>QSizePolicy::Fixed</enum>
221 </property>
222 <property name="sizeHint" stdset="0">
223 <size>
224 <width>20</width>
225 <height>40</height>
226 </size>
227 </property>
228 </spacer>
229 </item>
230 <item>
231 <widget class="QDialogButtonBox" name="buttonBox">
232 <property name="standardButtons">
233 <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
234 </property>
235 </widget>
236 </item>
237 </layout>
238 </item>
239 </layout>
240 </widget>
241 <resources/>
242 <connections>
243 <connection>
244 <sender>buttonBox</sender>
245 <signal>accepted()</signal>
246 <receiver>ConfigurePerGameGeneral</receiver>
247 <slot>accept()</slot>
248 <hints>
249 <hint type="sourcelabel">
250 <x>269</x>
251 <y>567</y>
252 </hint>
253 <hint type="destinationlabel">
254 <x>269</x>
255 <y>294</y>
256 </hint>
257 </hints>
258 </connection>
259 <connection>
260 <sender>buttonBox</sender>
261 <signal>rejected()</signal>
262 <receiver>ConfigurePerGameGeneral</receiver>
263 <slot>reject()</slot>
264 <hints>
265 <hint type="sourcelabel">
266 <x>269</x>
267 <y>567</y>
268 </hint>
269 <hint type="destinationlabel">
270 <x>269</x>
271 <y>294</y>
272 </hint>
273 </hints>
274 </connection>
275 </connections>
276</ui>
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp
index 10315e7a6..68e02738b 100644
--- a/src/yuzu/configuration/configure_system.cpp
+++ b/src/yuzu/configuration/configure_system.cpp
@@ -14,6 +14,7 @@
14#include "core/core.h" 14#include "core/core.h"
15#include "core/settings.h" 15#include "core/settings.h"
16#include "ui_configure_system.h" 16#include "ui_configure_system.h"
17#include "yuzu/configuration/configuration_shared.h"
17#include "yuzu/configuration/configure_system.h" 18#include "yuzu/configuration/configure_system.h"
18 19
19ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureSystem) { 20ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureSystem) {
@@ -21,20 +22,25 @@ ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(new Ui::
21 connect(ui->button_regenerate_console_id, &QPushButton::clicked, this, 22 connect(ui->button_regenerate_console_id, &QPushButton::clicked, this,
22 &ConfigureSystem::RefreshConsoleID); 23 &ConfigureSystem::RefreshConsoleID);
23 24
24 connect(ui->rng_seed_checkbox, &QCheckBox::stateChanged, this, [this](bool checked) { 25 connect(ui->rng_seed_checkbox, &QCheckBox::stateChanged, this, [this](int state) {
25 ui->rng_seed_edit->setEnabled(checked); 26 ui->rng_seed_edit->setEnabled(state == Qt::Checked);
26 if (!checked) { 27 if (state != Qt::Checked) {
27 ui->rng_seed_edit->setText(QStringLiteral("00000000")); 28 ui->rng_seed_edit->setText(QStringLiteral("00000000"));
28 } 29 }
29 }); 30 });
30 31
31 connect(ui->custom_rtc_checkbox, &QCheckBox::stateChanged, this, [this](bool checked) { 32 connect(ui->custom_rtc_checkbox, &QCheckBox::stateChanged, this, [this](int state) {
32 ui->custom_rtc_edit->setEnabled(checked); 33 ui->custom_rtc_edit->setEnabled(state == Qt::Checked);
33 if (!checked) { 34 if (state != Qt::Checked) {
34 ui->custom_rtc_edit->setDateTime(QDateTime::currentDateTime()); 35 ui->custom_rtc_edit->setDateTime(QDateTime::currentDateTime());
35 } 36 }
36 }); 37 });
37 38
39 ui->label_console_id->setVisible(Settings::configuring_global);
40 ui->button_regenerate_console_id->setVisible(Settings::configuring_global);
41
42 SetupPerGameUI();
43
38 SetConfiguration(); 44 SetConfiguration();
39} 45}
40 46
@@ -54,26 +60,58 @@ void ConfigureSystem::RetranslateUI() {
54 60
55void ConfigureSystem::SetConfiguration() { 61void ConfigureSystem::SetConfiguration() {
56 enabled = !Core::System::GetInstance().IsPoweredOn(); 62 enabled = !Core::System::GetInstance().IsPoweredOn();
63 const auto rng_seed =
64 QStringLiteral("%1")
65 .arg(Settings::values.rng_seed.GetValue().value_or(0), 8, 16, QLatin1Char{'0'})
66 .toUpper();
67 const auto rtc_time = Settings::values.custom_rtc.GetValue().value_or(
68 std::chrono::seconds(QDateTime::currentSecsSinceEpoch()));
57 69
58 ui->combo_language->setCurrentIndex(Settings::values.language_index); 70 if (Settings::configuring_global) {
59 ui->combo_region->setCurrentIndex(Settings::values.region_index); 71 ui->combo_language->setCurrentIndex(Settings::values.language_index.GetValue());
60 ui->combo_time_zone->setCurrentIndex(Settings::values.time_zone_index); 72 ui->combo_region->setCurrentIndex(Settings::values.region_index.GetValue());
61 ui->combo_sound->setCurrentIndex(Settings::values.sound_index); 73 ui->combo_time_zone->setCurrentIndex(Settings::values.time_zone_index.GetValue());
62 74 ui->combo_sound->setCurrentIndex(Settings::values.sound_index.GetValue());
63 ui->rng_seed_checkbox->setChecked(Settings::values.rng_seed.has_value()); 75
64 ui->rng_seed_edit->setEnabled(Settings::values.rng_seed.has_value()); 76 ui->rng_seed_checkbox->setChecked(Settings::values.rng_seed.GetValue().has_value());
65 77 ui->rng_seed_edit->setEnabled(Settings::values.rng_seed.GetValue().has_value() &&
66 const auto rng_seed = QStringLiteral("%1") 78 Settings::values.rng_seed.UsingGlobal());
67 .arg(Settings::values.rng_seed.value_or(0), 8, 16, QLatin1Char{'0'}) 79 ui->rng_seed_edit->setText(rng_seed);
68 .toUpper(); 80
69 ui->rng_seed_edit->setText(rng_seed); 81 ui->custom_rtc_checkbox->setChecked(Settings::values.custom_rtc.GetValue().has_value());
70 82 ui->custom_rtc_edit->setEnabled(Settings::values.custom_rtc.GetValue().has_value() &&
71 ui->custom_rtc_checkbox->setChecked(Settings::values.custom_rtc.has_value()); 83 Settings::values.rng_seed.UsingGlobal());
72 ui->custom_rtc_edit->setEnabled(Settings::values.custom_rtc.has_value()); 84 ui->custom_rtc_edit->setDateTime(QDateTime::fromSecsSinceEpoch(rtc_time.count()));
85 } else {
86 ConfigurationShared::SetPerGameSetting(ui->combo_language,
87 &Settings::values.language_index);
88 ConfigurationShared::SetPerGameSetting(ui->combo_region, &Settings::values.region_index);
89 ConfigurationShared::SetPerGameSetting(ui->combo_time_zone,
90 &Settings::values.time_zone_index);
91 ConfigurationShared::SetPerGameSetting(ui->combo_sound, &Settings::values.sound_index);
92
93 if (Settings::values.rng_seed.UsingGlobal()) {
94 ui->rng_seed_checkbox->setCheckState(Qt::PartiallyChecked);
95 } else {
96 ui->rng_seed_checkbox->setCheckState(
97 Settings::values.rng_seed.GetValue().has_value() ? Qt::Checked : Qt::Unchecked);
98 ui->rng_seed_edit->setEnabled(Settings::values.rng_seed.GetValue().has_value());
99 if (Settings::values.rng_seed.GetValue().has_value()) {
100 ui->rng_seed_edit->setText(rng_seed);
101 }
102 }
73 103
74 const auto rtc_time = Settings::values.custom_rtc.value_or( 104 if (Settings::values.custom_rtc.UsingGlobal()) {
75 std::chrono::seconds(QDateTime::currentSecsSinceEpoch())); 105 ui->custom_rtc_checkbox->setCheckState(Qt::PartiallyChecked);
76 ui->custom_rtc_edit->setDateTime(QDateTime::fromSecsSinceEpoch(rtc_time.count())); 106 } else {
107 ui->custom_rtc_checkbox->setCheckState(
108 Settings::values.custom_rtc.GetValue().has_value() ? Qt::Checked : Qt::Unchecked);
109 ui->custom_rtc_edit->setEnabled(Settings::values.custom_rtc.GetValue().has_value());
110 if (Settings::values.custom_rtc.GetValue().has_value()) {
111 ui->custom_rtc_edit->setDateTime(QDateTime::fromSecsSinceEpoch(rtc_time.count()));
112 }
113 }
114 }
77} 115}
78 116
79void ConfigureSystem::ReadSystemSettings() {} 117void ConfigureSystem::ReadSystemSettings() {}
@@ -83,22 +121,78 @@ void ConfigureSystem::ApplyConfiguration() {
83 return; 121 return;
84 } 122 }
85 123
86 Settings::values.language_index = ui->combo_language->currentIndex(); 124 if (Settings::configuring_global) {
87 Settings::values.region_index = ui->combo_region->currentIndex(); 125 // Guard if during game and set to game-specific value
88 Settings::values.time_zone_index = ui->combo_time_zone->currentIndex(); 126 if (Settings::values.language_index.UsingGlobal()) {
89 Settings::values.sound_index = ui->combo_sound->currentIndex(); 127 Settings::values.language_index.SetValue(ui->combo_language->currentIndex());
128 }
129 if (Settings::values.region_index.UsingGlobal()) {
130 Settings::values.region_index.SetValue(ui->combo_region->currentIndex());
131 }
132 if (Settings::values.time_zone_index.UsingGlobal()) {
133 Settings::values.time_zone_index.SetValue(ui->combo_time_zone->currentIndex());
134 }
135 if (Settings::values.sound_index.UsingGlobal()) {
136 Settings::values.sound_index.SetValue(ui->combo_sound->currentIndex());
137 }
138
139 if (Settings::values.rng_seed.UsingGlobal()) {
140 if (ui->rng_seed_checkbox->isChecked()) {
141 Settings::values.rng_seed.SetValue(
142 ui->rng_seed_edit->text().toULongLong(nullptr, 16));
143 } else {
144 Settings::values.rng_seed.SetValue(std::nullopt);
145 }
146 }
90 147
91 if (ui->rng_seed_checkbox->isChecked()) { 148 if (Settings::values.custom_rtc.UsingGlobal()) {
92 Settings::values.rng_seed = ui->rng_seed_edit->text().toULongLong(nullptr, 16); 149 if (ui->custom_rtc_checkbox->isChecked()) {
150 Settings::values.custom_rtc.SetValue(
151 std::chrono::seconds(ui->custom_rtc_edit->dateTime().toSecsSinceEpoch()));
152 } else {
153 Settings::values.custom_rtc.SetValue(std::nullopt);
154 }
155 }
93 } else { 156 } else {
94 Settings::values.rng_seed = std::nullopt; 157 ConfigurationShared::ApplyPerGameSetting(&Settings::values.language_index,
95 } 158 ui->combo_language);
159 ConfigurationShared::ApplyPerGameSetting(&Settings::values.region_index, ui->combo_region);
160 ConfigurationShared::ApplyPerGameSetting(&Settings::values.time_zone_index,
161 ui->combo_time_zone);
162 ConfigurationShared::ApplyPerGameSetting(&Settings::values.sound_index, ui->combo_sound);
163
164 switch (ui->rng_seed_checkbox->checkState()) {
165 case Qt::Checked:
166 Settings::values.rng_seed.SetGlobal(false);
167 Settings::values.rng_seed.SetValue(ui->rng_seed_edit->text().toULongLong(nullptr, 16));
168 break;
169 case Qt::Unchecked:
170 Settings::values.rng_seed.SetGlobal(false);
171 Settings::values.rng_seed.SetValue(std::nullopt);
172 break;
173 case Qt::PartiallyChecked:
174 Settings::values.rng_seed.SetGlobal(false);
175 Settings::values.rng_seed.SetValue(std::nullopt);
176 Settings::values.rng_seed.SetGlobal(true);
177 break;
178 }
96 179
97 if (ui->custom_rtc_checkbox->isChecked()) { 180 switch (ui->custom_rtc_checkbox->checkState()) {
98 Settings::values.custom_rtc = 181 case Qt::Checked:
99 std::chrono::seconds(ui->custom_rtc_edit->dateTime().toSecsSinceEpoch()); 182 Settings::values.custom_rtc.SetGlobal(false);
100 } else { 183 Settings::values.custom_rtc.SetValue(
101 Settings::values.custom_rtc = std::nullopt; 184 std::chrono::seconds(ui->custom_rtc_edit->dateTime().toSecsSinceEpoch()));
185 break;
186 case Qt::Unchecked:
187 Settings::values.custom_rtc.SetGlobal(false);
188 Settings::values.custom_rtc.SetValue(std::nullopt);
189 break;
190 case Qt::PartiallyChecked:
191 Settings::values.custom_rtc.SetGlobal(false);
192 Settings::values.custom_rtc.SetValue(std::nullopt);
193 Settings::values.custom_rtc.SetGlobal(true);
194 break;
195 }
102 } 196 }
103 197
104 Settings::Apply(); 198 Settings::Apply();
@@ -120,3 +214,25 @@ void ConfigureSystem::RefreshConsoleID() {
120 ui->label_console_id->setText( 214 ui->label_console_id->setText(
121 tr("Console ID: 0x%1").arg(QString::number(console_id, 16).toUpper())); 215 tr("Console ID: 0x%1").arg(QString::number(console_id, 16).toUpper()));
122} 216}
217
218void ConfigureSystem::SetupPerGameUI() {
219 if (Settings::configuring_global) {
220 ui->combo_language->setEnabled(Settings::values.language_index.UsingGlobal());
221 ui->combo_region->setEnabled(Settings::values.region_index.UsingGlobal());
222 ui->combo_time_zone->setEnabled(Settings::values.time_zone_index.UsingGlobal());
223 ui->combo_sound->setEnabled(Settings::values.sound_index.UsingGlobal());
224 ui->rng_seed_checkbox->setEnabled(Settings::values.rng_seed.UsingGlobal());
225 ui->rng_seed_edit->setEnabled(Settings::values.rng_seed.UsingGlobal());
226 ui->custom_rtc_checkbox->setEnabled(Settings::values.custom_rtc.UsingGlobal());
227 ui->custom_rtc_edit->setEnabled(Settings::values.custom_rtc.UsingGlobal());
228
229 return;
230 }
231
232 ConfigurationShared::InsertGlobalItem(ui->combo_language);
233 ConfigurationShared::InsertGlobalItem(ui->combo_region);
234 ConfigurationShared::InsertGlobalItem(ui->combo_time_zone);
235 ConfigurationShared::InsertGlobalItem(ui->combo_sound);
236 ui->rng_seed_checkbox->setTristate(true);
237 ui->custom_rtc_checkbox->setTristate(true);
238}
diff --git a/src/yuzu/configuration/configure_system.h b/src/yuzu/configuration/configure_system.h
index 26d42d5c5..f317ef8b5 100644
--- a/src/yuzu/configuration/configure_system.h
+++ b/src/yuzu/configuration/configure_system.h
@@ -32,6 +32,8 @@ private:
32 32
33 void RefreshConsoleID(); 33 void RefreshConsoleID();
34 34
35 void SetupPerGameUI();
36
35 std::unique_ptr<Ui::ConfigureSystem> ui; 37 std::unique_ptr<Ui::ConfigureSystem> ui;
36 bool enabled = false; 38 bool enabled = false;
37 39
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 9844e4764..4d501a8f9 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -16,7 +16,7 @@
16#include "applets/software_keyboard.h" 16#include "applets/software_keyboard.h"
17#include "applets/web_browser.h" 17#include "applets/web_browser.h"
18#include "configuration/configure_input.h" 18#include "configuration/configure_input.h"
19#include "configuration/configure_per_general.h" 19#include "configuration/configure_per_game.h"
20#include "core/file_sys/vfs.h" 20#include "core/file_sys/vfs.h"
21#include "core/file_sys/vfs_real.h" 21#include "core/file_sys/vfs_real.h"
22#include "core/frontend/applets/general_frontend.h" 22#include "core/frontend/applets/general_frontend.h"
@@ -534,15 +534,15 @@ void GMainWindow::InitializeWidgets() {
534 if (emulation_running) { 534 if (emulation_running) {
535 return; 535 return;
536 } 536 }
537 bool is_async = 537 bool is_async = !Settings::values.use_asynchronous_gpu_emulation.GetValue() ||
538 !Settings::values.use_asynchronous_gpu_emulation || Settings::values.use_multi_core; 538 Settings::values.use_multi_core.GetValue();
539 Settings::values.use_asynchronous_gpu_emulation = is_async; 539 Settings::values.use_asynchronous_gpu_emulation.SetValue(is_async);
540 async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation); 540 async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation.GetValue());
541 Settings::Apply(); 541 Settings::Apply();
542 }); 542 });
543 async_status_button->setText(tr("ASYNC")); 543 async_status_button->setText(tr("ASYNC"));
544 async_status_button->setCheckable(true); 544 async_status_button->setCheckable(true);
545 async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation); 545 async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation.GetValue());
546 546
547 // Setup Multicore button 547 // Setup Multicore button
548 multicore_status_button = new QPushButton(); 548 multicore_status_button = new QPushButton();
@@ -552,17 +552,17 @@ void GMainWindow::InitializeWidgets() {
552 if (emulation_running) { 552 if (emulation_running) {
553 return; 553 return;
554 } 554 }
555 Settings::values.use_multi_core = !Settings::values.use_multi_core; 555 Settings::values.use_multi_core.SetValue(!Settings::values.use_multi_core.GetValue());
556 bool is_async = 556 bool is_async = Settings::values.use_asynchronous_gpu_emulation.GetValue() ||
557 Settings::values.use_asynchronous_gpu_emulation || Settings::values.use_multi_core; 557 Settings::values.use_multi_core.GetValue();
558 Settings::values.use_asynchronous_gpu_emulation = is_async; 558 Settings::values.use_asynchronous_gpu_emulation.SetValue(is_async);
559 async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation); 559 async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation.GetValue());
560 multicore_status_button->setChecked(Settings::values.use_multi_core); 560 multicore_status_button->setChecked(Settings::values.use_multi_core.GetValue());
561 Settings::Apply(); 561 Settings::Apply();
562 }); 562 });
563 multicore_status_button->setText(tr("MULTICORE")); 563 multicore_status_button->setText(tr("MULTICORE"));
564 multicore_status_button->setCheckable(true); 564 multicore_status_button->setCheckable(true);
565 multicore_status_button->setChecked(Settings::values.use_multi_core); 565 multicore_status_button->setChecked(Settings::values.use_multi_core.GetValue());
566 statusBar()->insertPermanentWidget(0, multicore_status_button); 566 statusBar()->insertPermanentWidget(0, multicore_status_button);
567 statusBar()->insertPermanentWidget(0, async_status_button); 567 statusBar()->insertPermanentWidget(0, async_status_button);
568 568
@@ -581,16 +581,16 @@ void GMainWindow::InitializeWidgets() {
581 renderer_status_button->setCheckable(false); 581 renderer_status_button->setCheckable(false);
582 renderer_status_button->setDisabled(true); 582 renderer_status_button->setDisabled(true);
583#else 583#else
584 renderer_status_button->setChecked(Settings::values.renderer_backend == 584 renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() ==
585 Settings::RendererBackend::Vulkan); 585 Settings::RendererBackend::Vulkan);
586 connect(renderer_status_button, &QPushButton::clicked, [=] { 586 connect(renderer_status_button, &QPushButton::clicked, [=] {
587 if (emulation_running) { 587 if (emulation_running) {
588 return; 588 return;
589 } 589 }
590 if (renderer_status_button->isChecked()) { 590 if (renderer_status_button->isChecked()) {
591 Settings::values.renderer_backend = Settings::RendererBackend::Vulkan; 591 Settings::values.renderer_backend.SetValue(Settings::RendererBackend::Vulkan);
592 } else { 592 } else {
593 Settings::values.renderer_backend = Settings::RendererBackend::OpenGL; 593 Settings::values.renderer_backend.SetValue(Settings::RendererBackend::OpenGL);
594 } 594 }
595 595
596 Settings::Apply(); 596 Settings::Apply();
@@ -727,21 +727,24 @@ void GMainWindow::InitializeHotkeys() {
727 }); 727 });
728 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Toggle Speed Limit"), this), 728 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Toggle Speed Limit"), this),
729 &QShortcut::activated, this, [&] { 729 &QShortcut::activated, this, [&] {
730 Settings::values.use_frame_limit = !Settings::values.use_frame_limit; 730 Settings::values.use_frame_limit.SetValue(
731 !Settings::values.use_frame_limit.GetValue());
731 UpdateStatusBar(); 732 UpdateStatusBar();
732 }); 733 });
733 constexpr u16 SPEED_LIMIT_STEP = 5; 734 constexpr u16 SPEED_LIMIT_STEP = 5;
734 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Increase Speed Limit"), this), 735 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Increase Speed Limit"), this),
735 &QShortcut::activated, this, [&] { 736 &QShortcut::activated, this, [&] {
736 if (Settings::values.frame_limit < 9999 - SPEED_LIMIT_STEP) { 737 if (Settings::values.frame_limit.GetValue() < 9999 - SPEED_LIMIT_STEP) {
737 Settings::values.frame_limit += SPEED_LIMIT_STEP; 738 Settings::values.frame_limit.SetValue(SPEED_LIMIT_STEP +
739 Settings::values.frame_limit.GetValue());
738 UpdateStatusBar(); 740 UpdateStatusBar();
739 } 741 }
740 }); 742 });
741 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Decrease Speed Limit"), this), 743 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Decrease Speed Limit"), this),
742 &QShortcut::activated, this, [&] { 744 &QShortcut::activated, this, [&] {
743 if (Settings::values.frame_limit > SPEED_LIMIT_STEP) { 745 if (Settings::values.frame_limit.GetValue() > SPEED_LIMIT_STEP) {
744 Settings::values.frame_limit -= SPEED_LIMIT_STEP; 746 Settings::values.frame_limit.SetValue(Settings::values.frame_limit.GetValue() -
747 SPEED_LIMIT_STEP);
745 UpdateStatusBar(); 748 UpdateStatusBar();
746 } 749 }
747 }); 750 });
@@ -1039,6 +1042,17 @@ void GMainWindow::BootGame(const QString& filename) {
1039 LOG_INFO(Frontend, "yuzu starting..."); 1042 LOG_INFO(Frontend, "yuzu starting...");
1040 StoreRecentFile(filename); // Put the filename on top of the list 1043 StoreRecentFile(filename); // Put the filename on top of the list
1041 1044
1045 u64 title_id{0};
1046
1047 const auto v_file = Core::GetGameFileFromPath(vfs, filename.toUtf8().constData());
1048 const auto loader = Loader::GetLoader(v_file);
1049 if (!(loader == nullptr || loader->ReadProgramId(title_id) != Loader::ResultStatus::Success)) {
1050 // Load per game settings
1051 Config per_game_config(fmt::format("{:016X}.ini", title_id), false);
1052 }
1053
1054 Settings::LogSettings();
1055
1042 if (UISettings::values.select_user_on_boot) { 1056 if (UISettings::values.select_user_on_boot) {
1043 SelectAndSetCurrentUser(); 1057 SelectAndSetCurrentUser();
1044 } 1058 }
@@ -1063,6 +1077,7 @@ void GMainWindow::BootGame(const QString& filename) {
1063 &LoadingScreen::OnLoadProgress, Qt::QueuedConnection); 1077 &LoadingScreen::OnLoadProgress, Qt::QueuedConnection);
1064 1078
1065 // Update the GUI 1079 // Update the GUI
1080 UpdateStatusButtons();
1066 if (ui.action_Single_Window_Mode->isChecked()) { 1081 if (ui.action_Single_Window_Mode->isChecked()) {
1067 game_list->hide(); 1082 game_list->hide();
1068 game_list_placeholder->hide(); 1083 game_list_placeholder->hide();
@@ -1078,8 +1093,6 @@ void GMainWindow::BootGame(const QString& filename) {
1078 ui.centralwidget->setMouseTracking(true); 1093 ui.centralwidget->setMouseTracking(true);
1079 } 1094 }
1080 1095
1081 const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID();
1082
1083 std::string title_name; 1096 std::string title_name;
1084 std::string title_version; 1097 std::string title_version;
1085 const auto res = Core::System::GetInstance().GetGameName(title_name); 1098 const auto res = Core::System::GetInstance().GetGameName(title_name);
@@ -1521,7 +1534,7 @@ void GMainWindow::OnGameListOpenPerGameProperties(const std::string& file) {
1521 return; 1534 return;
1522 } 1535 }
1523 1536
1524 ConfigurePerGameGeneral dialog(this, title_id); 1537 ConfigurePerGame dialog(this, title_id);
1525 dialog.LoadFromFile(v_file); 1538 dialog.LoadFromFile(v_file);
1526 auto result = dialog.exec(); 1539 auto result = dialog.exec();
1527 if (result == QDialog::Accepted) { 1540 if (result == QDialog::Accepted) {
@@ -1532,7 +1545,14 @@ void GMainWindow::OnGameListOpenPerGameProperties(const std::string& file) {
1532 game_list->PopulateAsync(UISettings::values.game_dirs); 1545 game_list->PopulateAsync(UISettings::values.game_dirs);
1533 } 1546 }
1534 1547
1535 config->Save(); 1548 // Do not cause the global config to write local settings into the config file
1549 Settings::RestoreGlobalState();
1550
1551 if (!Core::System::GetInstance().IsPoweredOn()) {
1552 config->Save();
1553 }
1554 } else {
1555 Settings::RestoreGlobalState();
1536 } 1556 }
1537} 1557}
1538 1558
@@ -1819,6 +1839,9 @@ void GMainWindow::OnStopGame() {
1819 } 1839 }
1820 1840
1821 ShutdownGame(); 1841 ShutdownGame();
1842
1843 Settings::RestoreGlobalState();
1844 UpdateStatusButtons();
1822} 1845}
1823 1846
1824void GMainWindow::OnLoadComplete() { 1847void GMainWindow::OnLoadComplete() {
@@ -1926,7 +1949,7 @@ void GMainWindow::ToggleWindowMode() {
1926 1949
1927void GMainWindow::ResetWindowSize() { 1950void GMainWindow::ResetWindowSize() {
1928 const auto aspect_ratio = Layout::EmulationAspectRatio( 1951 const auto aspect_ratio = Layout::EmulationAspectRatio(
1929 static_cast<Layout::AspectRatio>(Settings::values.aspect_ratio), 1952 static_cast<Layout::AspectRatio>(Settings::values.aspect_ratio.GetValue()),
1930 static_cast<float>(Layout::ScreenUndocked::Height) / Layout::ScreenUndocked::Width); 1953 static_cast<float>(Layout::ScreenUndocked::Height) / Layout::ScreenUndocked::Width);
1931 if (!ui.action_Single_Window_Mode->isChecked()) { 1954 if (!ui.action_Single_Window_Mode->isChecked()) {
1932 render_window->resize(Layout::ScreenUndocked::Height / aspect_ratio, 1955 render_window->resize(Layout::ScreenUndocked::Height / aspect_ratio,
@@ -1974,16 +1997,7 @@ void GMainWindow::OnConfigure() {
1974 ui.centralwidget->setMouseTracking(false); 1997 ui.centralwidget->setMouseTracking(false);
1975 } 1998 }
1976 1999
1977 dock_status_button->setChecked(Settings::values.use_docked_mode); 2000 UpdateStatusButtons();
1978 multicore_status_button->setChecked(Settings::values.use_multi_core);
1979 Settings::values.use_asynchronous_gpu_emulation =
1980 Settings::values.use_asynchronous_gpu_emulation || Settings::values.use_multi_core;
1981 async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation);
1982
1983#ifdef HAS_VULKAN
1984 renderer_status_button->setChecked(Settings::values.renderer_backend ==
1985 Settings::RendererBackend::Vulkan);
1986#endif
1987} 2001}
1988 2002
1989void GMainWindow::OnLoadAmiibo() { 2003void GMainWindow::OnLoadAmiibo() {
@@ -2097,21 +2111,34 @@ void GMainWindow::UpdateStatusBar() {
2097 2111
2098 auto results = Core::System::GetInstance().GetAndResetPerfStats(); 2112 auto results = Core::System::GetInstance().GetAndResetPerfStats();
2099 2113
2100 if (Settings::values.use_frame_limit) { 2114 if (Settings::values.use_frame_limit.GetValue()) {
2101 emu_speed_label->setText(tr("Speed: %1% / %2%") 2115 emu_speed_label->setText(tr("Speed: %1% / %2%")
2102 .arg(results.emulation_speed * 100.0, 0, 'f', 0) 2116 .arg(results.emulation_speed * 100.0, 0, 'f', 0)
2103 .arg(Settings::values.frame_limit)); 2117 .arg(Settings::values.frame_limit.GetValue()));
2104 } else { 2118 } else {
2105 emu_speed_label->setText(tr("Speed: %1%").arg(results.emulation_speed * 100.0, 0, 'f', 0)); 2119 emu_speed_label->setText(tr("Speed: %1%").arg(results.emulation_speed * 100.0, 0, 'f', 0));
2106 } 2120 }
2107 game_fps_label->setText(tr("Game: %1 FPS").arg(results.game_fps, 0, 'f', 0)); 2121 game_fps_label->setText(tr("Game: %1 FPS").arg(results.game_fps, 0, 'f', 0));
2108 emu_frametime_label->setText(tr("Frame: %1 ms").arg(results.frametime * 1000.0, 0, 'f', 2)); 2122 emu_frametime_label->setText(tr("Frame: %1 ms").arg(results.frametime * 1000.0, 0, 'f', 2));
2109 2123
2110 emu_speed_label->setVisible(!Settings::values.use_multi_core); 2124 emu_speed_label->setVisible(!Settings::values.use_multi_core.GetValue());
2111 game_fps_label->setVisible(true); 2125 game_fps_label->setVisible(true);
2112 emu_frametime_label->setVisible(true); 2126 emu_frametime_label->setVisible(true);
2113} 2127}
2114 2128
2129void GMainWindow::UpdateStatusButtons() {
2130 dock_status_button->setChecked(Settings::values.use_docked_mode);
2131 multicore_status_button->setChecked(Settings::values.use_multi_core.GetValue());
2132 Settings::values.use_asynchronous_gpu_emulation.SetValue(
2133 Settings::values.use_asynchronous_gpu_emulation.GetValue() ||
2134 Settings::values.use_multi_core.GetValue());
2135 async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation.GetValue());
2136#ifdef HAS_VULKAN
2137 renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() ==
2138 Settings::RendererBackend::Vulkan);
2139#endif
2140}
2141
2115void GMainWindow::HideMouseCursor() { 2142void GMainWindow::HideMouseCursor() {
2116 if (emu_thread == nullptr || UISettings::values.hide_mouse == false) { 2143 if (emu_thread == nullptr || UISettings::values.hide_mouse == false) {
2117 mouse_hide_timer.stop(); 2144 mouse_hide_timer.stop();
@@ -2195,6 +2222,9 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det
2195 if (answer == QMessageBox::Yes) { 2222 if (answer == QMessageBox::Yes) {
2196 if (emu_thread) { 2223 if (emu_thread) {
2197 ShutdownGame(); 2224 ShutdownGame();
2225
2226 Settings::RestoreGlobalState();
2227 UpdateStatusButtons();
2198 } 2228 }
2199 } else { 2229 } else {
2200 // Only show the message if the game is still running. 2230 // Only show the message if the game is still running.
@@ -2357,9 +2387,13 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
2357 hotkey_registry.SaveHotkeys(); 2387 hotkey_registry.SaveHotkeys();
2358 2388
2359 // Shutdown session if the emu thread is active... 2389 // Shutdown session if the emu thread is active...
2360 if (emu_thread != nullptr) 2390 if (emu_thread != nullptr) {
2361 ShutdownGame(); 2391 ShutdownGame();
2362 2392
2393 Settings::RestoreGlobalState();
2394 UpdateStatusButtons();
2395 }
2396
2363 render_window->close(); 2397 render_window->close();
2364 2398
2365 QWidget::closeEvent(event); 2399 QWidget::closeEvent(event);
@@ -2539,8 +2573,6 @@ int main(int argc, char* argv[]) {
2539 QObject::connect(&app, &QGuiApplication::applicationStateChanged, &main_window, 2573 QObject::connect(&app, &QGuiApplication::applicationStateChanged, &main_window,
2540 &GMainWindow::OnAppFocusStateChanged); 2574 &GMainWindow::OnAppFocusStateChanged);
2541 2575
2542 Settings::LogSettings();
2543
2544 int result = app.exec(); 2576 int result = app.exec();
2545 detached_tasks.WaitForAllTasks(); 2577 detached_tasks.WaitForAllTasks();
2546 return result; 2578 return result;
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 66c84e5c0..8e3d39c38 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -221,6 +221,7 @@ private:
221 void UpdateWindowTitle(const std::string& title_name = {}, 221 void UpdateWindowTitle(const std::string& title_name = {},
222 const std::string& title_version = {}); 222 const std::string& title_version = {});
223 void UpdateStatusBar(); 223 void UpdateStatusBar();
224 void UpdateStatusButtons();
224 void HideMouseCursor(); 225 void HideMouseCursor();
225 void ShowMouseCursor(); 226 void ShowMouseCursor();
226 void OpenURL(const QUrl& url); 227 void OpenURL(const QUrl& url);
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 659b9f701..23763144f 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -354,63 +354,72 @@ void Config::ReadValues() {
354 354
355 const auto rng_seed_enabled = sdl2_config->GetBoolean("System", "rng_seed_enabled", false); 355 const auto rng_seed_enabled = sdl2_config->GetBoolean("System", "rng_seed_enabled", false);
356 if (rng_seed_enabled) { 356 if (rng_seed_enabled) {
357 Settings::values.rng_seed = sdl2_config->GetInteger("System", "rng_seed", 0); 357 Settings::values.rng_seed.SetValue(sdl2_config->GetInteger("System", "rng_seed", 0));
358 } else { 358 } else {
359 Settings::values.rng_seed = std::nullopt; 359 Settings::values.rng_seed.SetValue(std::nullopt);
360 } 360 }
361 361
362 const auto custom_rtc_enabled = sdl2_config->GetBoolean("System", "custom_rtc_enabled", false); 362 const auto custom_rtc_enabled = sdl2_config->GetBoolean("System", "custom_rtc_enabled", false);
363 if (custom_rtc_enabled) { 363 if (custom_rtc_enabled) {
364 Settings::values.custom_rtc = 364 Settings::values.custom_rtc.SetValue(
365 std::chrono::seconds(sdl2_config->GetInteger("System", "custom_rtc", 0)); 365 std::chrono::seconds(sdl2_config->GetInteger("System", "custom_rtc", 0)));
366 } else { 366 } else {
367 Settings::values.custom_rtc = std::nullopt; 367 Settings::values.custom_rtc.SetValue(std::nullopt);
368 } 368 }
369 369
370 Settings::values.language_index = sdl2_config->GetInteger("System", "language_index", 1); 370 Settings::values.language_index.SetValue(
371 Settings::values.time_zone_index = sdl2_config->GetInteger("System", "time_zone_index", 0); 371 sdl2_config->GetInteger("System", "language_index", 1));
372 Settings::values.time_zone_index.SetValue(
373 sdl2_config->GetInteger("System", "time_zone_index", 0));
372 374
373 // Core 375 // Core
374 Settings::values.use_multi_core = sdl2_config->GetBoolean("Core", "use_multi_core", false); 376 Settings::values.use_multi_core.SetValue(
377 sdl2_config->GetBoolean("Core", "use_multi_core", false));
375 378
376 // Renderer 379 // Renderer
377 const int renderer_backend = sdl2_config->GetInteger( 380 const int renderer_backend = sdl2_config->GetInteger(
378 "Renderer", "backend", static_cast<int>(Settings::RendererBackend::OpenGL)); 381 "Renderer", "backend", static_cast<int>(Settings::RendererBackend::OpenGL));
379 Settings::values.renderer_backend = static_cast<Settings::RendererBackend>(renderer_backend); 382 Settings::values.renderer_backend.SetValue(
383 static_cast<Settings::RendererBackend>(renderer_backend));
380 Settings::values.renderer_debug = sdl2_config->GetBoolean("Renderer", "debug", false); 384 Settings::values.renderer_debug = sdl2_config->GetBoolean("Renderer", "debug", false);
381 Settings::values.vulkan_device = sdl2_config->GetInteger("Renderer", "vulkan_device", 0); 385 Settings::values.vulkan_device.SetValue(
382 386 sdl2_config->GetInteger("Renderer", "vulkan_device", 0));
383 Settings::values.aspect_ratio = 387
384 static_cast<int>(sdl2_config->GetInteger("Renderer", "aspect_ratio", 0)); 388 Settings::values.aspect_ratio.SetValue(
385 Settings::values.max_anisotropy = 389 static_cast<int>(sdl2_config->GetInteger("Renderer", "aspect_ratio", 0)));
386 static_cast<int>(sdl2_config->GetInteger("Renderer", "max_anisotropy", 0)); 390 Settings::values.max_anisotropy.SetValue(
387 Settings::values.use_frame_limit = sdl2_config->GetBoolean("Renderer", "use_frame_limit", true); 391 static_cast<int>(sdl2_config->GetInteger("Renderer", "max_anisotropy", 0)));
388 Settings::values.frame_limit = 392 Settings::values.use_frame_limit.SetValue(
389 static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100)); 393 sdl2_config->GetBoolean("Renderer", "use_frame_limit", true));
390 Settings::values.use_disk_shader_cache = 394 Settings::values.frame_limit.SetValue(
391 sdl2_config->GetBoolean("Renderer", "use_disk_shader_cache", false); 395 static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100)));
396 Settings::values.use_disk_shader_cache.SetValue(
397 sdl2_config->GetBoolean("Renderer", "use_disk_shader_cache", false));
392 const int gpu_accuracy_level = sdl2_config->GetInteger("Renderer", "gpu_accuracy", 0); 398 const int gpu_accuracy_level = sdl2_config->GetInteger("Renderer", "gpu_accuracy", 0);
393 Settings::values.gpu_accuracy = static_cast<Settings::GPUAccuracy>(gpu_accuracy_level); 399 Settings::values.gpu_accuracy.SetValue(static_cast<Settings::GPUAccuracy>(gpu_accuracy_level));
394 Settings::values.use_asynchronous_gpu_emulation = 400 Settings::values.use_asynchronous_gpu_emulation.SetValue(
395 sdl2_config->GetBoolean("Renderer", "use_asynchronous_gpu_emulation", false); 401 sdl2_config->GetBoolean("Renderer", "use_asynchronous_gpu_emulation", false));
396 Settings::values.use_vsync = 402 Settings::values.use_vsync.SetValue(
397 static_cast<u16>(sdl2_config->GetInteger("Renderer", "use_vsync", 1)); 403 static_cast<u16>(sdl2_config->GetInteger("Renderer", "use_vsync", 1)));
398 Settings::values.use_assembly_shaders = 404 Settings::values.use_assembly_shaders.SetValue(
399 sdl2_config->GetBoolean("Renderer", "use_assembly_shaders", false); 405 sdl2_config->GetBoolean("Renderer", "use_assembly_shaders", false));
400 Settings::values.use_fast_gpu_time = 406 Settings::values.use_fast_gpu_time.SetValue(
401 sdl2_config->GetBoolean("Renderer", "use_fast_gpu_time", true); 407 sdl2_config->GetBoolean("Renderer", "use_fast_gpu_time", true));
402 408
403 Settings::values.bg_red = static_cast<float>(sdl2_config->GetReal("Renderer", "bg_red", 0.0)); 409 Settings::values.bg_red.SetValue(
404 Settings::values.bg_green = 410 static_cast<float>(sdl2_config->GetReal("Renderer", "bg_red", 0.0)));
405 static_cast<float>(sdl2_config->GetReal("Renderer", "bg_green", 0.0)); 411 Settings::values.bg_green.SetValue(
406 Settings::values.bg_blue = static_cast<float>(sdl2_config->GetReal("Renderer", "bg_blue", 0.0)); 412 static_cast<float>(sdl2_config->GetReal("Renderer", "bg_green", 0.0)));
413 Settings::values.bg_blue.SetValue(
414 static_cast<float>(sdl2_config->GetReal("Renderer", "bg_blue", 0.0)));
407 415
408 // Audio 416 // Audio
409 Settings::values.sink_id = sdl2_config->Get("Audio", "output_engine", "auto"); 417 Settings::values.sink_id = sdl2_config->Get("Audio", "output_engine", "auto");
410 Settings::values.enable_audio_stretching = 418 Settings::values.enable_audio_stretching.SetValue(
411 sdl2_config->GetBoolean("Audio", "enable_audio_stretching", true); 419 sdl2_config->GetBoolean("Audio", "enable_audio_stretching", true));
412 Settings::values.audio_device_id = sdl2_config->Get("Audio", "output_device", "auto"); 420 Settings::values.audio_device_id = sdl2_config->Get("Audio", "output_device", "auto");
413 Settings::values.volume = static_cast<float>(sdl2_config->GetReal("Audio", "volume", 1)); 421 Settings::values.volume.SetValue(
422 static_cast<float>(sdl2_config->GetReal("Audio", "volume", 1)));
414 423
415 // Miscellaneous 424 // Miscellaneous
416 Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace"); 425 Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace");
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
index 09cc0a3b5..e78025737 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
@@ -165,7 +165,7 @@ std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_GL::CreateShared
165 165
166void EmuWindow_SDL2_GL::Present() { 166void EmuWindow_SDL2_GL::Present() {
167 SDL_GL_MakeCurrent(render_window, window_context); 167 SDL_GL_MakeCurrent(render_window, window_context);
168 SDL_GL_SetSwapInterval(Settings::values.use_vsync ? 1 : 0); 168 SDL_GL_SetSwapInterval(Settings::values.use_vsync.GetValue() ? 1 : 0);
169 while (IsOpen()) { 169 while (IsOpen()) {
170 system.Renderer().TryPresent(100); 170 system.Renderer().TryPresent(100);
171 SDL_GL_SwapWindow(render_window); 171 SDL_GL_SwapWindow(render_window);
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index e6c6a839d..512b060a7 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -181,7 +181,7 @@ int main(int argc, char** argv) {
181 Core::System& system{Core::System::GetInstance()}; 181 Core::System& system{Core::System::GetInstance()};
182 182
183 std::unique_ptr<EmuWindow_SDL2> emu_window; 183 std::unique_ptr<EmuWindow_SDL2> emu_window;
184 switch (Settings::values.renderer_backend) { 184 switch (Settings::values.renderer_backend.GetValue()) {
185 case Settings::RendererBackend::OpenGL: 185 case Settings::RendererBackend::OpenGL:
186 emu_window = std::make_unique<EmuWindow_SDL2_GL>(system, fullscreen); 186 emu_window = std::make_unique<EmuWindow_SDL2_GL>(system, fullscreen);
187 break; 187 break;
diff --git a/src/yuzu_tester/config.cpp b/src/yuzu_tester/config.cpp
index 1566c2e3f..acb22885e 100644
--- a/src/yuzu_tester/config.cpp
+++ b/src/yuzu_tester/config.cpp
@@ -81,6 +81,9 @@ void Config::ReadValues() {
81 Settings::values.touchscreen.diameter_x = 15; 81 Settings::values.touchscreen.diameter_x = 15;
82 Settings::values.touchscreen.diameter_y = 15; 82 Settings::values.touchscreen.diameter_y = 15;
83 83
84 Settings::values.use_docked_mode =
85 sdl2_config->GetBoolean("Controls", "use_docked_mode", false);
86
84 // Data Storage 87 // Data Storage
85 Settings::values.use_virtual_sd = 88 Settings::values.use_virtual_sd =
86 sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true); 89 sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true);
@@ -92,57 +95,59 @@ void Config::ReadValues() {
92 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))); 95 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)));
93 96
94 // System 97 // System
95 Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false);
96
97 Settings::values.current_user = std::clamp<int>( 98 Settings::values.current_user = std::clamp<int>(
98 sdl2_config->GetInteger("System", "current_user", 0), 0, Service::Account::MAX_USERS - 1); 99 sdl2_config->GetInteger("System", "current_user", 0), 0, Service::Account::MAX_USERS - 1);
99 100
100 const auto rng_seed_enabled = sdl2_config->GetBoolean("System", "rng_seed_enabled", false); 101 const auto rng_seed_enabled = sdl2_config->GetBoolean("System", "rng_seed_enabled", false);
101 if (rng_seed_enabled) { 102 if (rng_seed_enabled) {
102 Settings::values.rng_seed = sdl2_config->GetInteger("System", "rng_seed", 0); 103 Settings::values.rng_seed.SetValue(sdl2_config->GetInteger("System", "rng_seed", 0));
103 } else { 104 } else {
104 Settings::values.rng_seed = std::nullopt; 105 Settings::values.rng_seed.SetValue(std::nullopt);
105 } 106 }
106 107
107 const auto custom_rtc_enabled = sdl2_config->GetBoolean("System", "custom_rtc_enabled", false); 108 const auto custom_rtc_enabled = sdl2_config->GetBoolean("System", "custom_rtc_enabled", false);
108 if (custom_rtc_enabled) { 109 if (custom_rtc_enabled) {
109 Settings::values.custom_rtc = 110 Settings::values.custom_rtc.SetValue(
110 std::chrono::seconds(sdl2_config->GetInteger("System", "custom_rtc", 0)); 111 std::chrono::seconds(sdl2_config->GetInteger("System", "custom_rtc", 0)));
111 } else { 112 } else {
112 Settings::values.custom_rtc = std::nullopt; 113 Settings::values.custom_rtc.SetValue(std::nullopt);
113 } 114 }
114 115
115 // Core 116 // Core
116 Settings::values.use_multi_core = sdl2_config->GetBoolean("Core", "use_multi_core", false); 117 Settings::values.use_multi_core.SetValue(
118 sdl2_config->GetBoolean("Core", "use_multi_core", false));
117 119
118 // Renderer 120 // Renderer
119 Settings::values.aspect_ratio = 121 Settings::values.aspect_ratio.SetValue(
120 static_cast<int>(sdl2_config->GetInteger("Renderer", "aspect_ratio", 0)); 122 static_cast<int>(sdl2_config->GetInteger("Renderer", "aspect_ratio", 0)));
121 Settings::values.max_anisotropy = 123 Settings::values.max_anisotropy.SetValue(
122 static_cast<int>(sdl2_config->GetInteger("Renderer", "max_anisotropy", 0)); 124 static_cast<int>(sdl2_config->GetInteger("Renderer", "max_anisotropy", 0)));
123 Settings::values.use_frame_limit = false; 125 Settings::values.use_frame_limit.SetValue(false);
124 Settings::values.frame_limit = 100; 126 Settings::values.frame_limit.SetValue(100);
125 Settings::values.use_disk_shader_cache = 127 Settings::values.use_disk_shader_cache.SetValue(
126 sdl2_config->GetBoolean("Renderer", "use_disk_shader_cache", false); 128 sdl2_config->GetBoolean("Renderer", "use_disk_shader_cache", false));
127 const int gpu_accuracy_level = sdl2_config->GetInteger("Renderer", "gpu_accuracy", 0); 129 const int gpu_accuracy_level = sdl2_config->GetInteger("Renderer", "gpu_accuracy", 0);
128 Settings::values.gpu_accuracy = static_cast<Settings::GPUAccuracy>(gpu_accuracy_level); 130 Settings::values.gpu_accuracy.SetValue(static_cast<Settings::GPUAccuracy>(gpu_accuracy_level));
129 Settings::values.use_asynchronous_gpu_emulation = 131 Settings::values.use_asynchronous_gpu_emulation.SetValue(
130 sdl2_config->GetBoolean("Renderer", "use_asynchronous_gpu_emulation", false); 132 sdl2_config->GetBoolean("Renderer", "use_asynchronous_gpu_emulation", false));
131 Settings::values.use_fast_gpu_time = 133 Settings::values.use_fast_gpu_time.SetValue(
132 sdl2_config->GetBoolean("Renderer", "use_fast_gpu_time", true); 134 sdl2_config->GetBoolean("Renderer", "use_fast_gpu_time", true));
133 135
134 Settings::values.bg_red = static_cast<float>(sdl2_config->GetReal("Renderer", "bg_red", 0.0)); 136 Settings::values.bg_red.SetValue(
135 Settings::values.bg_green = 137 static_cast<float>(sdl2_config->GetReal("Renderer", "bg_red", 0.0)));
136 static_cast<float>(sdl2_config->GetReal("Renderer", "bg_green", 0.0)); 138 Settings::values.bg_green.SetValue(
137 Settings::values.bg_blue = static_cast<float>(sdl2_config->GetReal("Renderer", "bg_blue", 0.0)); 139 static_cast<float>(sdl2_config->GetReal("Renderer", "bg_green", 0.0)));
140 Settings::values.bg_blue.SetValue(
141 static_cast<float>(sdl2_config->GetReal("Renderer", "bg_blue", 0.0)));
138 142
139 // Audio 143 // Audio
140 Settings::values.sink_id = "null"; 144 Settings::values.sink_id = "null";
141 Settings::values.enable_audio_stretching = false; 145 Settings::values.enable_audio_stretching.SetValue(false);
142 Settings::values.audio_device_id = "auto"; 146 Settings::values.audio_device_id = "auto";
143 Settings::values.volume = 0; 147 Settings::values.volume.SetValue(0);
144 148
145 Settings::values.language_index = sdl2_config->GetInteger("System", "language_index", 1); 149 Settings::values.language_index.SetValue(
150 sdl2_config->GetInteger("System", "language_index", 1));
146 151
147 // Miscellaneous 152 // Miscellaneous
148 Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace"); 153 Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace");