summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/bit_field.h9
-rw-r--r--src/common/common_funcs.h10
-rw-r--r--src/common/logging/filter.cpp2
-rw-r--r--src/common/logging/types.h2
-rw-r--r--src/common/settings.h67
-rw-r--r--src/common/wall_clock.cpp2
-rw-r--r--src/common/x64/cpu_detect.cpp16
-rw-r--r--src/common/x64/cpu_detect.h5
-rw-r--r--src/core/CMakeLists.txt4
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp6
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp6
-rw-r--r--src/core/hle/kernel/k_code_memory.h6
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp4
-rw-r--r--src/core/hle/service/nifm/nifm.cpp42
-rw-r--r--src/core/hle/service/nvflinger/binder.h1
-rw-r--r--src/core/hle/service/ptm/psm.cpp122
-rw-r--r--src/core/hle/service/ptm/psm.h31
-rw-r--r--src/core/hle/service/ptm/ptm.cpp18
-rw-r--r--src/core/hle/service/ptm/ptm.h18
-rw-r--r--src/core/hle/service/ptm/ts.cpp41
-rw-r--r--src/core/hle/service/ptm/ts.h25
-rw-r--r--src/core/hle/service/service.cpp4
-rw-r--r--src/input_common/drivers/sdl_driver.cpp10
-rw-r--r--src/input_common/drivers/sdl_driver.h1
-rw-r--r--src/yuzu/applets/qt_web_browser.cpp8
-rw-r--r--src/yuzu/configuration/config.cpp16
-rw-r--r--src/yuzu/configuration/config.h16
-rw-r--r--src/yuzu/configuration/configuration_shared.h10
-rw-r--r--src/yuzu_cmd/config.cpp4
-rw-r--r--src/yuzu_cmd/config.h4
30 files changed, 348 insertions, 162 deletions
diff --git a/src/common/bit_field.h b/src/common/bit_field.h
index 16d805694..7e1df62b1 100644
--- a/src/common/bit_field.h
+++ b/src/common/bit_field.h
@@ -146,7 +146,16 @@ public:
146 } 146 }
147 147
148 constexpr void Assign(const T& value) { 148 constexpr void Assign(const T& value) {
149#ifdef _MSC_VER
149 storage = static_cast<StorageType>((storage & ~mask) | FormatValue(value)); 150 storage = static_cast<StorageType>((storage & ~mask) | FormatValue(value));
151#else
152 // Explicitly reload with memcpy to avoid compiler aliasing quirks
153 // regarding optimization: GCC/Clang clobber chained stores to
154 // different bitfields in the same struct with the last value.
155 StorageTypeWithEndian storage_;
156 std::memcpy(&storage_, &storage, sizeof(storage_));
157 storage = static_cast<StorageType>((storage_ & ~mask) | FormatValue(value));
158#endif
150 } 159 }
151 160
152 [[nodiscard]] constexpr T Value() const { 161 [[nodiscard]] constexpr T Value() const {
diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h
index adc31c758..e1e2a90fc 100644
--- a/src/common/common_funcs.h
+++ b/src/common/common_funcs.h
@@ -18,14 +18,16 @@
18/// Helper macros to insert unused bytes or words to properly align structs. These values will be 18/// Helper macros to insert unused bytes or words to properly align structs. These values will be
19/// zero-initialized. 19/// zero-initialized.
20#define INSERT_PADDING_BYTES(num_bytes) \ 20#define INSERT_PADDING_BYTES(num_bytes) \
21 std::array<u8, num_bytes> CONCAT2(pad, __LINE__) {} 21 [[maybe_unused]] std::array<u8, num_bytes> CONCAT2(pad, __LINE__) {}
22#define INSERT_PADDING_WORDS(num_words) \ 22#define INSERT_PADDING_WORDS(num_words) \
23 std::array<u32, num_words> CONCAT2(pad, __LINE__) {} 23 [[maybe_unused]] std::array<u32, num_words> CONCAT2(pad, __LINE__) {}
24 24
25/// These are similar to the INSERT_PADDING_* macros but do not zero-initialize the contents. 25/// These are similar to the INSERT_PADDING_* macros but do not zero-initialize the contents.
26/// This keeps the structure trivial to construct. 26/// This keeps the structure trivial to construct.
27#define INSERT_PADDING_BYTES_NOINIT(num_bytes) std::array<u8, num_bytes> CONCAT2(pad, __LINE__) 27#define INSERT_PADDING_BYTES_NOINIT(num_bytes) \
28#define INSERT_PADDING_WORDS_NOINIT(num_words) std::array<u32, num_words> CONCAT2(pad, __LINE__) 28 [[maybe_unused]] std::array<u8, num_bytes> CONCAT2(pad, __LINE__)
29#define INSERT_PADDING_WORDS_NOINIT(num_words) \
30 [[maybe_unused]] std::array<u32, num_words> CONCAT2(pad, __LINE__)
29 31
30#ifndef _MSC_VER 32#ifndef _MSC_VER
31 33
diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp
index 4acbff649..6de9bacbf 100644
--- a/src/common/logging/filter.cpp
+++ b/src/common/logging/filter.cpp
@@ -128,7 +128,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
128 SUB(Service, PM) \ 128 SUB(Service, PM) \
129 SUB(Service, PREPO) \ 129 SUB(Service, PREPO) \
130 SUB(Service, PSC) \ 130 SUB(Service, PSC) \
131 SUB(Service, PSM) \ 131 SUB(Service, PTM) \
132 SUB(Service, SET) \ 132 SUB(Service, SET) \
133 SUB(Service, SM) \ 133 SUB(Service, SM) \
134 SUB(Service, SPL) \ 134 SUB(Service, SPL) \
diff --git a/src/common/logging/types.h b/src/common/logging/types.h
index cabb4db8e..595c15ada 100644
--- a/src/common/logging/types.h
+++ b/src/common/logging/types.h
@@ -95,7 +95,7 @@ enum class Class : u8 {
95 Service_PM, ///< The PM service 95 Service_PM, ///< The PM service
96 Service_PREPO, ///< The PREPO (Play report) service 96 Service_PREPO, ///< The PREPO (Play report) service
97 Service_PSC, ///< The PSC service 97 Service_PSC, ///< The PSC service
98 Service_PSM, ///< The PSM service 98 Service_PTM, ///< The PTM service
99 Service_SET, ///< The SET (Settings) service 99 Service_SET, ///< The SET (Settings) service
100 Service_SM, ///< The SM (Service manager) service 100 Service_SM, ///< The SM (Service manager) service
101 Service_SPL, ///< The SPL service 101 Service_SPL, ///< The SPL service
diff --git a/src/common/settings.h b/src/common/settings.h
index 3583a2e70..368046e87 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -106,7 +106,7 @@ struct ResolutionScalingInfo {
106 * configurations. Specifying a default value and label is required. A minimum and maximum range can 106 * configurations. Specifying a default value and label is required. A minimum and maximum range can
107 * be specified for sanitization. 107 * be specified for sanitization.
108 */ 108 */
109template <typename Type> 109template <typename Type, bool ranged = false>
110class Setting { 110class Setting {
111protected: 111protected:
112 Setting() = default; 112 Setting() = default;
@@ -126,8 +126,8 @@ public:
126 * @param default_val Intial value of the setting, and default value of the setting 126 * @param default_val Intial value of the setting, and default value of the setting
127 * @param name Label for the setting 127 * @param name Label for the setting
128 */ 128 */
129 explicit Setting(const Type& default_val, const std::string& name) 129 explicit Setting(const Type& default_val, const std::string& name) requires(!ranged)
130 : value{default_val}, default_value{default_val}, ranged{false}, label{name} {} 130 : value{default_val}, default_value{default_val}, label{name} {}
131 virtual ~Setting() = default; 131 virtual ~Setting() = default;
132 132
133 /** 133 /**
@@ -139,9 +139,9 @@ public:
139 * @param name Label for the setting 139 * @param name Label for the setting
140 */ 140 */
141 explicit Setting(const Type& default_val, const Type& min_val, const Type& max_val, 141 explicit Setting(const Type& default_val, const Type& min_val, const Type& max_val,
142 const std::string& name) 142 const std::string& name) requires(ranged)
143 : value{default_val}, default_value{default_val}, maximum{max_val}, minimum{min_val}, 143 : value{default_val},
144 ranged{true}, label{name} {} 144 default_value{default_val}, maximum{max_val}, minimum{min_val}, label{name} {}
145 145
146 /** 146 /**
147 * Returns a reference to the setting's value. 147 * Returns a reference to the setting's value.
@@ -158,7 +158,7 @@ public:
158 * @param val The desired value 158 * @param val The desired value
159 */ 159 */
160 virtual void SetValue(const Type& val) { 160 virtual void SetValue(const Type& val) {
161 Type temp{(ranged) ? std::clamp(val, minimum, maximum) : val}; 161 Type temp{ranged ? std::clamp(val, minimum, maximum) : val};
162 std::swap(value, temp); 162 std::swap(value, temp);
163 } 163 }
164 164
@@ -188,7 +188,7 @@ public:
188 * @returns A reference to the setting 188 * @returns A reference to the setting
189 */ 189 */
190 virtual const Type& operator=(const Type& val) { 190 virtual const Type& operator=(const Type& val) {
191 Type temp{(ranged) ? std::clamp(val, minimum, maximum) : val}; 191 Type temp{ranged ? std::clamp(val, minimum, maximum) : val};
192 std::swap(value, temp); 192 std::swap(value, temp);
193 return value; 193 return value;
194 } 194 }
@@ -207,7 +207,6 @@ protected:
207 const Type default_value{}; ///< The default value 207 const Type default_value{}; ///< The default value
208 const Type maximum{}; ///< Maximum allowed value of the setting 208 const Type maximum{}; ///< Maximum allowed value of the setting
209 const Type minimum{}; ///< Minimum allowed value of the setting 209 const Type minimum{}; ///< Minimum allowed value of the setting
210 const bool ranged; ///< The setting has sanitization ranges
211 const std::string label{}; ///< The setting's label 210 const std::string label{}; ///< The setting's label
212}; 211};
213 212
@@ -219,8 +218,8 @@ protected:
219 * 218 *
220 * By default, the global setting is used. 219 * By default, the global setting is used.
221 */ 220 */
222template <typename Type> 221template <typename Type, bool ranged = false>
223class SwitchableSetting : virtual public Setting<Type> { 222class SwitchableSetting : virtual public Setting<Type, ranged> {
224public: 223public:
225 /** 224 /**
226 * Sets a default value, label, and setting value. 225 * Sets a default value, label, and setting value.
@@ -228,7 +227,7 @@ public:
228 * @param default_val Intial value of the setting, and default value of the setting 227 * @param default_val Intial value of the setting, and default value of the setting
229 * @param name Label for the setting 228 * @param name Label for the setting
230 */ 229 */
231 explicit SwitchableSetting(const Type& default_val, const std::string& name) 230 explicit SwitchableSetting(const Type& default_val, const std::string& name) requires(!ranged)
232 : Setting<Type>{default_val, name} {} 231 : Setting<Type>{default_val, name} {}
233 virtual ~SwitchableSetting() = default; 232 virtual ~SwitchableSetting() = default;
234 233
@@ -241,8 +240,8 @@ public:
241 * @param name Label for the setting 240 * @param name Label for the setting
242 */ 241 */
243 explicit SwitchableSetting(const Type& default_val, const Type& min_val, const Type& max_val, 242 explicit SwitchableSetting(const Type& default_val, const Type& min_val, const Type& max_val,
244 const std::string& name) 243 const std::string& name) requires(ranged)
245 : Setting<Type>{default_val, min_val, max_val, name} {} 244 : Setting<Type, true>{default_val, min_val, max_val, name} {}
246 245
247 /** 246 /**
248 * Tells this setting to represent either the global or custom setting when other member 247 * Tells this setting to represent either the global or custom setting when other member
@@ -290,7 +289,7 @@ public:
290 * @param val The new value 289 * @param val The new value
291 */ 290 */
292 void SetValue(const Type& val) override { 291 void SetValue(const Type& val) override {
293 Type temp{(this->ranged) ? std::clamp(val, this->minimum, this->maximum) : val}; 292 Type temp{ranged ? std::clamp(val, this->minimum, this->maximum) : val};
294 if (use_global) { 293 if (use_global) {
295 std::swap(this->value, temp); 294 std::swap(this->value, temp);
296 } else { 295 } else {
@@ -306,7 +305,7 @@ public:
306 * @returns A reference to the current setting value 305 * @returns A reference to the current setting value
307 */ 306 */
308 const Type& operator=(const Type& val) override { 307 const Type& operator=(const Type& val) override {
309 Type temp{(this->ranged) ? std::clamp(val, this->minimum, this->maximum) : val}; 308 Type temp{ranged ? std::clamp(val, this->minimum, this->maximum) : val};
310 if (use_global) { 309 if (use_global) {
311 std::swap(this->value, temp); 310 std::swap(this->value, temp);
312 return this->value; 311 return this->value;
@@ -374,15 +373,15 @@ struct Values {
374 Setting<std::string> audio_device_id{"auto", "output_device"}; 373 Setting<std::string> audio_device_id{"auto", "output_device"};
375 Setting<std::string> sink_id{"auto", "output_engine"}; 374 Setting<std::string> sink_id{"auto", "output_engine"};
376 Setting<bool> audio_muted{false, "audio_muted"}; 375 Setting<bool> audio_muted{false, "audio_muted"};
377 SwitchableSetting<u8> volume{100, 0, 100, "volume"}; 376 SwitchableSetting<u8, true> volume{100, 0, 100, "volume"};
378 377
379 // Core 378 // Core
380 SwitchableSetting<bool> use_multi_core{true, "use_multi_core"}; 379 SwitchableSetting<bool> use_multi_core{true, "use_multi_core"};
381 SwitchableSetting<bool> use_extended_memory_layout{false, "use_extended_memory_layout"}; 380 SwitchableSetting<bool> use_extended_memory_layout{false, "use_extended_memory_layout"};
382 381
383 // Cpu 382 // Cpu
384 SwitchableSetting<CPUAccuracy> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto, 383 SwitchableSetting<CPUAccuracy, true> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto,
385 CPUAccuracy::Paranoid, "cpu_accuracy"}; 384 CPUAccuracy::Paranoid, "cpu_accuracy"};
386 // TODO: remove cpu_accuracy_first_time, migration setting added 8 July 2021 385 // TODO: remove cpu_accuracy_first_time, migration setting added 8 July 2021
387 Setting<bool> cpu_accuracy_first_time{true, "cpu_accuracy_first_time"}; 386 Setting<bool> cpu_accuracy_first_time{true, "cpu_accuracy_first_time"};
388 Setting<bool> cpu_debug_mode{false, "cpu_debug_mode"}; 387 Setting<bool> cpu_debug_mode{false, "cpu_debug_mode"};
@@ -409,7 +408,7 @@ struct Values {
409 true, "cpuopt_unsafe_ignore_global_monitor"}; 408 true, "cpuopt_unsafe_ignore_global_monitor"};
410 409
411 // Renderer 410 // Renderer
412 SwitchableSetting<RendererBackend> renderer_backend{ 411 SwitchableSetting<RendererBackend, true> renderer_backend{
413 RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Vulkan, "backend"}; 412 RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Vulkan, "backend"};
414 Setting<bool> renderer_debug{false, "debug"}; 413 Setting<bool> renderer_debug{false, "debug"};
415 Setting<bool> renderer_shader_feedback{false, "shader_feedback"}; 414 Setting<bool> renderer_shader_feedback{false, "shader_feedback"};
@@ -423,28 +422,28 @@ struct Values {
423 SwitchableSetting<AntiAliasing> anti_aliasing{AntiAliasing::None, "anti_aliasing"}; 422 SwitchableSetting<AntiAliasing> anti_aliasing{AntiAliasing::None, "anti_aliasing"};
424 // *nix platforms may have issues with the borderless windowed fullscreen mode. 423 // *nix platforms may have issues with the borderless windowed fullscreen mode.
425 // Default to exclusive fullscreen on these platforms for now. 424 // Default to exclusive fullscreen on these platforms for now.
426 SwitchableSetting<FullscreenMode> fullscreen_mode{ 425 SwitchableSetting<FullscreenMode, true> fullscreen_mode{
427#ifdef _WIN32 426#ifdef _WIN32
428 FullscreenMode::Borderless, 427 FullscreenMode::Borderless,
429#else 428#else
430 FullscreenMode::Exclusive, 429 FullscreenMode::Exclusive,
431#endif 430#endif
432 FullscreenMode::Borderless, FullscreenMode::Exclusive, "fullscreen_mode"}; 431 FullscreenMode::Borderless, FullscreenMode::Exclusive, "fullscreen_mode"};
433 SwitchableSetting<int> aspect_ratio{0, 0, 3, "aspect_ratio"}; 432 SwitchableSetting<int, true> aspect_ratio{0, 0, 3, "aspect_ratio"};
434 SwitchableSetting<int> max_anisotropy{0, 0, 5, "max_anisotropy"}; 433 SwitchableSetting<int, true> max_anisotropy{0, 0, 5, "max_anisotropy"};
435 SwitchableSetting<bool> use_speed_limit{true, "use_speed_limit"}; 434 SwitchableSetting<bool> use_speed_limit{true, "use_speed_limit"};
436 SwitchableSetting<u16> speed_limit{100, 0, 9999, "speed_limit"}; 435 SwitchableSetting<u16, true> speed_limit{100, 0, 9999, "speed_limit"};
437 SwitchableSetting<bool> use_disk_shader_cache{true, "use_disk_shader_cache"}; 436 SwitchableSetting<bool> use_disk_shader_cache{true, "use_disk_shader_cache"};
438 SwitchableSetting<GPUAccuracy> gpu_accuracy{GPUAccuracy::High, GPUAccuracy::Normal, 437 SwitchableSetting<GPUAccuracy, true> gpu_accuracy{GPUAccuracy::High, GPUAccuracy::Normal,
439 GPUAccuracy::Extreme, "gpu_accuracy"}; 438 GPUAccuracy::Extreme, "gpu_accuracy"};
440 SwitchableSetting<bool> use_asynchronous_gpu_emulation{true, "use_asynchronous_gpu_emulation"}; 439 SwitchableSetting<bool> use_asynchronous_gpu_emulation{true, "use_asynchronous_gpu_emulation"};
441 SwitchableSetting<NvdecEmulation> nvdec_emulation{NvdecEmulation::GPU, "nvdec_emulation"}; 440 SwitchableSetting<NvdecEmulation> nvdec_emulation{NvdecEmulation::GPU, "nvdec_emulation"};
442 SwitchableSetting<bool> accelerate_astc{true, "accelerate_astc"}; 441 SwitchableSetting<bool> accelerate_astc{true, "accelerate_astc"};
443 SwitchableSetting<bool> use_vsync{true, "use_vsync"}; 442 SwitchableSetting<bool> use_vsync{true, "use_vsync"};
444 SwitchableSetting<u16> fps_cap{1000, 1, 1000, "fps_cap"}; 443 SwitchableSetting<u16, true> fps_cap{1000, 1, 1000, "fps_cap"};
445 Setting<bool> disable_fps_limit{false, "disable_fps_limit"}; 444 Setting<bool> disable_fps_limit{false, "disable_fps_limit"};
446 SwitchableSetting<ShaderBackend> shader_backend{ShaderBackend::GLASM, ShaderBackend::GLSL, 445 SwitchableSetting<ShaderBackend, true> shader_backend{ShaderBackend::GLASM, ShaderBackend::GLSL,
447 ShaderBackend::SPIRV, "shader_backend"}; 446 ShaderBackend::SPIRV, "shader_backend"};
448 SwitchableSetting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"}; 447 SwitchableSetting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"};
449 SwitchableSetting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"}; 448 SwitchableSetting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"};
450 449
@@ -460,10 +459,10 @@ struct Values {
460 s64 custom_rtc_differential; 459 s64 custom_rtc_differential;
461 460
462 Setting<s32> current_user{0, "current_user"}; 461 Setting<s32> current_user{0, "current_user"};
463 SwitchableSetting<s32> language_index{1, 0, 17, "language_index"}; 462 SwitchableSetting<s32, true> language_index{1, 0, 17, "language_index"};
464 SwitchableSetting<s32> region_index{1, 0, 6, "region_index"}; 463 SwitchableSetting<s32, true> region_index{1, 0, 6, "region_index"};
465 SwitchableSetting<s32> time_zone_index{0, 0, 45, "time_zone_index"}; 464 SwitchableSetting<s32, true> time_zone_index{0, 0, 45, "time_zone_index"};
466 SwitchableSetting<s32> sound_index{1, 0, 2, "sound_index"}; 465 SwitchableSetting<s32, true> sound_index{1, 0, 2, "sound_index"};
467 466
468 // Controls 467 // Controls
469 InputSetting<std::array<PlayerInput, 10>> players; 468 InputSetting<std::array<PlayerInput, 10>> players;
@@ -485,7 +484,7 @@ struct Values {
485 Setting<bool> tas_loop{false, "tas_loop"}; 484 Setting<bool> tas_loop{false, "tas_loop"};
486 485
487 Setting<bool> mouse_panning{false, "mouse_panning"}; 486 Setting<bool> mouse_panning{false, "mouse_panning"};
488 Setting<u8> mouse_panning_sensitivity{10, 1, 100, "mouse_panning_sensitivity"}; 487 Setting<u8, true> mouse_panning_sensitivity{10, 1, 100, "mouse_panning_sensitivity"};
489 Setting<bool> mouse_enabled{false, "mouse_enabled"}; 488 Setting<bool> mouse_enabled{false, "mouse_enabled"};
490 489
491 Setting<bool> emulate_analog_keyboard{false, "emulate_analog_keyboard"}; 490 Setting<bool> emulate_analog_keyboard{false, "emulate_analog_keyboard"};
diff --git a/src/common/wall_clock.cpp b/src/common/wall_clock.cpp
index b4fb3a59f..ae07f2811 100644
--- a/src/common/wall_clock.cpp
+++ b/src/common/wall_clock.cpp
@@ -67,7 +67,7 @@ std::unique_ptr<WallClock> CreateBestMatchingClock(u64 emulated_cpu_frequency,
67 const auto& caps = GetCPUCaps(); 67 const auto& caps = GetCPUCaps();
68 u64 rtsc_frequency = 0; 68 u64 rtsc_frequency = 0;
69 if (caps.invariant_tsc) { 69 if (caps.invariant_tsc) {
70 rtsc_frequency = EstimateRDTSCFrequency(); 70 rtsc_frequency = caps.tsc_frequency ? caps.tsc_frequency : EstimateRDTSCFrequency();
71 } 71 }
72 72
73 // Fallback to StandardWallClock if the hardware TSC does not have the precision greater than: 73 // Fallback to StandardWallClock if the hardware TSC does not have the precision greater than:
diff --git a/src/common/x64/cpu_detect.cpp b/src/common/x64/cpu_detect.cpp
index 322aa1f08..1a27532d4 100644
--- a/src/common/x64/cpu_detect.cpp
+++ b/src/common/x64/cpu_detect.cpp
@@ -161,6 +161,22 @@ static CPUCaps Detect() {
161 caps.invariant_tsc = Common::Bit<8>(cpu_id[3]); 161 caps.invariant_tsc = Common::Bit<8>(cpu_id[3]);
162 } 162 }
163 163
164 if (max_std_fn >= 0x15) {
165 __cpuid(cpu_id, 0x15);
166 caps.tsc_crystal_ratio_denominator = cpu_id[0];
167 caps.tsc_crystal_ratio_numerator = cpu_id[1];
168 caps.crystal_frequency = cpu_id[2];
169 // Some CPU models might not return a crystal frequency.
170 // The CPU model can be detected to use the values from turbostat
171 // https://github.com/torvalds/linux/blob/master/tools/power/x86/turbostat/turbostat.c#L5569
172 // but it's easier to just estimate the TSC tick rate for these cases.
173 if (caps.tsc_crystal_ratio_denominator) {
174 caps.tsc_frequency = static_cast<u64>(caps.crystal_frequency) *
175 caps.tsc_crystal_ratio_numerator /
176 caps.tsc_crystal_ratio_denominator;
177 }
178 }
179
164 if (max_std_fn >= 0x16) { 180 if (max_std_fn >= 0x16) {
165 __cpuid(cpu_id, 0x16); 181 __cpuid(cpu_id, 0x16);
166 caps.base_frequency = cpu_id[0]; 182 caps.base_frequency = cpu_id[0];
diff --git a/src/common/x64/cpu_detect.h b/src/common/x64/cpu_detect.h
index 9bdc9dbfa..6830f3795 100644
--- a/src/common/x64/cpu_detect.h
+++ b/src/common/x64/cpu_detect.h
@@ -30,6 +30,11 @@ struct CPUCaps {
30 u32 max_frequency; 30 u32 max_frequency;
31 u32 bus_frequency; 31 u32 bus_frequency;
32 32
33 u32 tsc_crystal_ratio_denominator;
34 u32 tsc_crystal_ratio_numerator;
35 u32 crystal_frequency;
36 u64 tsc_frequency; // Derived from the above three values
37
33 bool sse : 1; 38 bool sse : 1;
34 bool sse2 : 1; 39 bool sse2 : 1;
35 bool sse3 : 1; 40 bool sse3 : 1;
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index d9357138f..11d554bad 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -605,6 +605,10 @@ add_library(core STATIC
605 hle/service/psc/psc.h 605 hle/service/psc/psc.h
606 hle/service/ptm/psm.cpp 606 hle/service/ptm/psm.cpp
607 hle/service/ptm/psm.h 607 hle/service/ptm/psm.h
608 hle/service/ptm/ptm.cpp
609 hle/service/ptm/ptm.h
610 hle/service/ptm/ts.cpp
611 hle/service/ptm/ts.h
608 hle/service/kernel_helpers.cpp 612 hle/service/kernel_helpers.cpp
609 hle/service/kernel_helpers.h 613 hle/service/kernel_helpers.h
610 hle/service/service.cpp 614 hle/service/service.cpp
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index 1be5fe1c1..a780cf800 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -52,7 +52,7 @@ public:
52 if (!memory.IsValidVirtualAddressRange(vaddr, sizeof(u32))) { 52 if (!memory.IsValidVirtualAddressRange(vaddr, sizeof(u32))) {
53 return std::nullopt; 53 return std::nullopt;
54 } 54 }
55 return MemoryRead32(vaddr); 55 return memory.Read32(vaddr);
56 } 56 }
57 57
58 void MemoryWrite8(u32 vaddr, u8 value) override { 58 void MemoryWrite8(u32 vaddr, u8 value) override {
@@ -97,7 +97,7 @@ public:
97 parent.LogBacktrace(); 97 parent.LogBacktrace();
98 LOG_ERROR(Core_ARM, 98 LOG_ERROR(Core_ARM,
99 "Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc, 99 "Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc,
100 num_instructions, MemoryRead32(pc)); 100 num_instructions, memory.Read32(pc));
101 } 101 }
102 102
103 void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override { 103 void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override {
@@ -115,7 +115,7 @@ public:
115 parent.LogBacktrace(); 115 parent.LogBacktrace();
116 LOG_CRITICAL(Core_ARM, 116 LOG_CRITICAL(Core_ARM,
117 "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})", 117 "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})",
118 exception, pc, MemoryRead32(pc), parent.IsInThumbMode()); 118 exception, pc, memory.Read32(pc), parent.IsInThumbMode());
119 } 119 }
120 } 120 }
121 121
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index c437f24b8..1a75312a4 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -56,7 +56,7 @@ public:
56 if (!memory.IsValidVirtualAddressRange(vaddr, sizeof(u32))) { 56 if (!memory.IsValidVirtualAddressRange(vaddr, sizeof(u32))) {
57 return std::nullopt; 57 return std::nullopt;
58 } 58 }
59 return MemoryRead32(vaddr); 59 return memory.Read32(vaddr);
60 } 60 }
61 61
62 void MemoryWrite8(u64 vaddr, u8 value) override { 62 void MemoryWrite8(u64 vaddr, u8 value) override {
@@ -111,7 +111,7 @@ public:
111 parent.LogBacktrace(); 111 parent.LogBacktrace();
112 LOG_ERROR(Core_ARM, 112 LOG_ERROR(Core_ARM,
113 "Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc, 113 "Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc,
114 num_instructions, MemoryRead32(pc)); 114 num_instructions, memory.Read32(pc));
115 } 115 }
116 116
117 void InstructionCacheOperationRaised(Dynarmic::A64::InstructionCacheOperation op, 117 void InstructionCacheOperationRaised(Dynarmic::A64::InstructionCacheOperation op,
@@ -156,7 +156,7 @@ public:
156 156
157 parent.LogBacktrace(); 157 parent.LogBacktrace();
158 LOG_CRITICAL(Core_ARM, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})", 158 LOG_CRITICAL(Core_ARM, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})",
159 static_cast<std::size_t>(exception), pc, MemoryRead32(pc)); 159 static_cast<std::size_t>(exception), pc, memory.Read32(pc));
160 } 160 }
161 } 161 }
162 162
diff --git a/src/core/hle/kernel/k_code_memory.h b/src/core/hle/kernel/k_code_memory.h
index 2410f74a3..2e7e1436a 100644
--- a/src/core/hle/kernel/k_code_memory.h
+++ b/src/core/hle/kernel/k_code_memory.h
@@ -30,19 +30,19 @@ public:
30 explicit KCodeMemory(KernelCore& kernel_); 30 explicit KCodeMemory(KernelCore& kernel_);
31 31
32 Result Initialize(Core::DeviceMemory& device_memory, VAddr address, size_t size); 32 Result Initialize(Core::DeviceMemory& device_memory, VAddr address, size_t size);
33 void Finalize(); 33 void Finalize() override;
34 34
35 Result Map(VAddr address, size_t size); 35 Result Map(VAddr address, size_t size);
36 Result Unmap(VAddr address, size_t size); 36 Result Unmap(VAddr address, size_t size);
37 Result MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm); 37 Result MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm);
38 Result UnmapFromOwner(VAddr address, size_t size); 38 Result UnmapFromOwner(VAddr address, size_t size);
39 39
40 bool IsInitialized() const { 40 bool IsInitialized() const override {
41 return m_is_initialized; 41 return m_is_initialized;
42 } 42 }
43 static void PostDestroy([[maybe_unused]] uintptr_t arg) {} 43 static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
44 44
45 KProcess* GetOwner() const { 45 KProcess* GetOwner() const override {
46 return m_owner; 46 return m_owner;
47 } 47 }
48 VAddr GetSourceAddress() const { 48 VAddr GetSourceAddress() const {
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index aa7189bda..3c28dee76 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -838,11 +838,11 @@ bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id,
838 838
839 const auto now = steady_clock::now(); 839 const auto now = steady_clock::now();
840 840
841 // Filter out non-zero vibrations that are within 10ms of each other. 841 // Filter out non-zero vibrations that are within 15ms of each other.
842 if ((vibration_value.low_amplitude != 0.0f || vibration_value.high_amplitude != 0.0f) && 842 if ((vibration_value.low_amplitude != 0.0f || vibration_value.high_amplitude != 0.0f) &&
843 duration_cast<milliseconds>( 843 duration_cast<milliseconds>(
844 now - controller.vibration[device_index].last_vibration_timepoint) < 844 now - controller.vibration[device_index].last_vibration_timepoint) <
845 milliseconds(10)) { 845 milliseconds(15)) {
846 return false; 846 return false;
847 } 847 }
848 848
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index 0310ce883..7055ea93e 100644
--- a/src/core/hle/service/nifm/nifm.cpp
+++ b/src/core/hle/service/nifm/nifm.cpp
@@ -30,6 +30,19 @@ enum class RequestState : u32 {
30 Connected = 3, 30 Connected = 3,
31}; 31};
32 32
33enum class InternetConnectionType : u8 {
34 WiFi = 1,
35 Ethernet = 2,
36};
37
38enum class InternetConnectionStatus : u8 {
39 ConnectingUnknown1,
40 ConnectingUnknown2,
41 ConnectingUnknown3,
42 ConnectingUnknown4,
43 Connected,
44};
45
33struct IpAddressSetting { 46struct IpAddressSetting {
34 bool is_automatic{}; 47 bool is_automatic{};
35 Network::IPv4Address current_address{}; 48 Network::IPv4Address current_address{};
@@ -271,6 +284,7 @@ private:
271 rb.Push(ResultSuccess); 284 rb.Push(ResultSuccess);
272 rb.Push<u64>(client_id); // Client ID needs to be non zero otherwise it's considered invalid 285 rb.Push<u64>(client_id); // Client ID needs to be non zero otherwise it's considered invalid
273 } 286 }
287
274 void CreateScanRequest(Kernel::HLERequestContext& ctx) { 288 void CreateScanRequest(Kernel::HLERequestContext& ctx) {
275 LOG_DEBUG(Service_NIFM, "called"); 289 LOG_DEBUG(Service_NIFM, "called");
276 290
@@ -279,6 +293,7 @@ private:
279 rb.Push(ResultSuccess); 293 rb.Push(ResultSuccess);
280 rb.PushIpcInterface<IScanRequest>(system); 294 rb.PushIpcInterface<IScanRequest>(system);
281 } 295 }
296
282 void CreateRequest(Kernel::HLERequestContext& ctx) { 297 void CreateRequest(Kernel::HLERequestContext& ctx) {
283 LOG_DEBUG(Service_NIFM, "called"); 298 LOG_DEBUG(Service_NIFM, "called");
284 299
@@ -287,6 +302,7 @@ private:
287 rb.Push(ResultSuccess); 302 rb.Push(ResultSuccess);
288 rb.PushIpcInterface<IRequest>(system); 303 rb.PushIpcInterface<IRequest>(system);
289 } 304 }
305
290 void GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx) { 306 void GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx) {
291 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 307 LOG_WARNING(Service_NIFM, "(STUBBED) called");
292 308
@@ -335,12 +351,14 @@ private:
335 IPC::ResponseBuilder rb{ctx, 2}; 351 IPC::ResponseBuilder rb{ctx, 2};
336 rb.Push(ResultSuccess); 352 rb.Push(ResultSuccess);
337 } 353 }
354
338 void RemoveNetworkProfile(Kernel::HLERequestContext& ctx) { 355 void RemoveNetworkProfile(Kernel::HLERequestContext& ctx) {
339 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 356 LOG_WARNING(Service_NIFM, "(STUBBED) called");
340 357
341 IPC::ResponseBuilder rb{ctx, 2}; 358 IPC::ResponseBuilder rb{ctx, 2};
342 rb.Push(ResultSuccess); 359 rb.Push(ResultSuccess);
343 } 360 }
361
344 void GetCurrentIpAddress(Kernel::HLERequestContext& ctx) { 362 void GetCurrentIpAddress(Kernel::HLERequestContext& ctx) {
345 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 363 LOG_WARNING(Service_NIFM, "(STUBBED) called");
346 364
@@ -354,6 +372,7 @@ private:
354 rb.Push(ResultSuccess); 372 rb.Push(ResultSuccess);
355 rb.PushRaw(*ipv4); 373 rb.PushRaw(*ipv4);
356 } 374 }
375
357 void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) { 376 void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) {
358 LOG_DEBUG(Service_NIFM, "called"); 377 LOG_DEBUG(Service_NIFM, "called");
359 378
@@ -369,6 +388,7 @@ private:
369 rb.PushIpcInterface<INetworkProfile>(system); 388 rb.PushIpcInterface<INetworkProfile>(system);
370 rb.PushRaw<u128>(uuid); 389 rb.PushRaw<u128>(uuid);
371 } 390 }
391
372 void GetCurrentIpConfigInfo(Kernel::HLERequestContext& ctx) { 392 void GetCurrentIpConfigInfo(Kernel::HLERequestContext& ctx) {
373 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 393 LOG_WARNING(Service_NIFM, "(STUBBED) called");
374 394
@@ -405,6 +425,7 @@ private:
405 rb.Push(ResultSuccess); 425 rb.Push(ResultSuccess);
406 rb.PushRaw<IpConfigInfo>(ip_config_info); 426 rb.PushRaw<IpConfigInfo>(ip_config_info);
407 } 427 }
428
408 void IsWirelessCommunicationEnabled(Kernel::HLERequestContext& ctx) { 429 void IsWirelessCommunicationEnabled(Kernel::HLERequestContext& ctx) {
409 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 430 LOG_WARNING(Service_NIFM, "(STUBBED) called");
410 431
@@ -412,6 +433,24 @@ private:
412 rb.Push(ResultSuccess); 433 rb.Push(ResultSuccess);
413 rb.Push<u8>(0); 434 rb.Push<u8>(0);
414 } 435 }
436
437 void GetInternetConnectionStatus(Kernel::HLERequestContext& ctx) {
438 LOG_WARNING(Service_NIFM, "(STUBBED) called");
439
440 struct Output {
441 InternetConnectionType type{InternetConnectionType::WiFi};
442 u8 wifi_strength{3};
443 InternetConnectionStatus state{InternetConnectionStatus::Connected};
444 };
445 static_assert(sizeof(Output) == 0x3, "Output has incorrect size.");
446
447 constexpr Output out{};
448
449 IPC::ResponseBuilder rb{ctx, 3};
450 rb.Push(ResultSuccess);
451 rb.PushRaw(out);
452 }
453
415 void IsEthernetCommunicationEnabled(Kernel::HLERequestContext& ctx) { 454 void IsEthernetCommunicationEnabled(Kernel::HLERequestContext& ctx) {
416 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 455 LOG_WARNING(Service_NIFM, "(STUBBED) called");
417 456
@@ -423,6 +462,7 @@ private:
423 rb.Push<u8>(0); 462 rb.Push<u8>(0);
424 } 463 }
425 } 464 }
465
426 void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) { 466 void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) {
427 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 467 LOG_WARNING(Service_NIFM, "(STUBBED) called");
428 468
@@ -456,7 +496,7 @@ IGeneralService::IGeneralService(Core::System& system_)
456 {15, &IGeneralService::GetCurrentIpConfigInfo, "GetCurrentIpConfigInfo"}, 496 {15, &IGeneralService::GetCurrentIpConfigInfo, "GetCurrentIpConfigInfo"},
457 {16, nullptr, "SetWirelessCommunicationEnabled"}, 497 {16, nullptr, "SetWirelessCommunicationEnabled"},
458 {17, &IGeneralService::IsWirelessCommunicationEnabled, "IsWirelessCommunicationEnabled"}, 498 {17, &IGeneralService::IsWirelessCommunicationEnabled, "IsWirelessCommunicationEnabled"},
459 {18, nullptr, "GetInternetConnectionStatus"}, 499 {18, &IGeneralService::GetInternetConnectionStatus, "GetInternetConnectionStatus"},
460 {19, nullptr, "SetEthernetCommunicationEnabled"}, 500 {19, nullptr, "SetEthernetCommunicationEnabled"},
461 {20, &IGeneralService::IsEthernetCommunicationEnabled, "IsEthernetCommunicationEnabled"}, 501 {20, &IGeneralService::IsEthernetCommunicationEnabled, "IsEthernetCommunicationEnabled"},
462 {21, &IGeneralService::IsAnyInternetRequestAccepted, "IsAnyInternetRequestAccepted"}, 502 {21, &IGeneralService::IsAnyInternetRequestAccepted, "IsAnyInternetRequestAccepted"},
diff --git a/src/core/hle/service/nvflinger/binder.h b/src/core/hle/service/nvflinger/binder.h
index 21aaa40cd..157333ff8 100644
--- a/src/core/hle/service/nvflinger/binder.h
+++ b/src/core/hle/service/nvflinger/binder.h
@@ -34,6 +34,7 @@ enum class TransactionId {
34 34
35class IBinder { 35class IBinder {
36public: 36public:
37 virtual ~IBinder() = default;
37 virtual void Transact(Kernel::HLERequestContext& ctx, android::TransactionId code, 38 virtual void Transact(Kernel::HLERequestContext& ctx, android::TransactionId code,
38 u32 flags) = 0; 39 u32 flags) = 0;
39 virtual Kernel::KReadableEvent& GetNativeHandle() = 0; 40 virtual Kernel::KReadableEvent& GetNativeHandle() = 0;
diff --git a/src/core/hle/service/ptm/psm.cpp b/src/core/hle/service/ptm/psm.cpp
index 9e0eb6ac0..2c31e9485 100644
--- a/src/core/hle/service/ptm/psm.cpp
+++ b/src/core/hle/service/ptm/psm.cpp
@@ -9,10 +9,8 @@
9#include "core/hle/kernel/k_event.h" 9#include "core/hle/kernel/k_event.h"
10#include "core/hle/service/kernel_helpers.h" 10#include "core/hle/service/kernel_helpers.h"
11#include "core/hle/service/ptm/psm.h" 11#include "core/hle/service/ptm/psm.h"
12#include "core/hle/service/service.h"
13#include "core/hle/service/sm/sm.h"
14 12
15namespace Service::PSM { 13namespace Service::PTM {
16 14
17class IPsmSession final : public ServiceFramework<IPsmSession> { 15class IPsmSession final : public ServiceFramework<IPsmSession> {
18public: 16public:
@@ -57,7 +55,7 @@ public:
57 55
58private: 56private:
59 void BindStateChangeEvent(Kernel::HLERequestContext& ctx) { 57 void BindStateChangeEvent(Kernel::HLERequestContext& ctx) {
60 LOG_DEBUG(Service_PSM, "called"); 58 LOG_DEBUG(Service_PTM, "called");
61 59
62 should_signal = true; 60 should_signal = true;
63 61
@@ -67,7 +65,7 @@ private:
67 } 65 }
68 66
69 void UnbindStateChangeEvent(Kernel::HLERequestContext& ctx) { 67 void UnbindStateChangeEvent(Kernel::HLERequestContext& ctx) {
70 LOG_DEBUG(Service_PSM, "called"); 68 LOG_DEBUG(Service_PTM, "called");
71 69
72 should_signal = false; 70 should_signal = false;
73 71
@@ -78,7 +76,7 @@ private:
78 void SetChargerTypeChangeEventEnabled(Kernel::HLERequestContext& ctx) { 76 void SetChargerTypeChangeEventEnabled(Kernel::HLERequestContext& ctx) {
79 IPC::RequestParser rp{ctx}; 77 IPC::RequestParser rp{ctx};
80 const auto state = rp.Pop<bool>(); 78 const auto state = rp.Pop<bool>();
81 LOG_DEBUG(Service_PSM, "called, state={}", state); 79 LOG_DEBUG(Service_PTM, "called, state={}", state);
82 80
83 should_signal_charger_type = state; 81 should_signal_charger_type = state;
84 82
@@ -89,7 +87,7 @@ private:
89 void SetPowerSupplyChangeEventEnabled(Kernel::HLERequestContext& ctx) { 87 void SetPowerSupplyChangeEventEnabled(Kernel::HLERequestContext& ctx) {
90 IPC::RequestParser rp{ctx}; 88 IPC::RequestParser rp{ctx};
91 const auto state = rp.Pop<bool>(); 89 const auto state = rp.Pop<bool>();
92 LOG_DEBUG(Service_PSM, "called, state={}", state); 90 LOG_DEBUG(Service_PTM, "called, state={}", state);
93 91
94 should_signal_power_supply = state; 92 should_signal_power_supply = state;
95 93
@@ -100,7 +98,7 @@ private:
100 void SetBatteryVoltageStateChangeEventEnabled(Kernel::HLERequestContext& ctx) { 98 void SetBatteryVoltageStateChangeEventEnabled(Kernel::HLERequestContext& ctx) {
101 IPC::RequestParser rp{ctx}; 99 IPC::RequestParser rp{ctx};
102 const auto state = rp.Pop<bool>(); 100 const auto state = rp.Pop<bool>();
103 LOG_DEBUG(Service_PSM, "called, state={}", state); 101 LOG_DEBUG(Service_PTM, "called, state={}", state);
104 102
105 should_signal_battery_voltage = state; 103 should_signal_battery_voltage = state;
106 104
@@ -117,76 +115,58 @@ private:
117 Kernel::KEvent* state_change_event; 115 Kernel::KEvent* state_change_event;
118}; 116};
119 117
120class PSM final : public ServiceFramework<PSM> { 118PSM::PSM(Core::System& system_) : ServiceFramework{system_, "psm"} {
121public: 119 // clang-format off
122 explicit PSM(Core::System& system_) : ServiceFramework{system_, "psm"} { 120 static const FunctionInfo functions[] = {
123 // clang-format off 121 {0, &PSM::GetBatteryChargePercentage, "GetBatteryChargePercentage"},
124 static const FunctionInfo functions[] = { 122 {1, &PSM::GetChargerType, "GetChargerType"},
125 {0, &PSM::GetBatteryChargePercentage, "GetBatteryChargePercentage"}, 123 {2, nullptr, "EnableBatteryCharging"},
126 {1, &PSM::GetChargerType, "GetChargerType"}, 124 {3, nullptr, "DisableBatteryCharging"},
127 {2, nullptr, "EnableBatteryCharging"}, 125 {4, nullptr, "IsBatteryChargingEnabled"},
128 {3, nullptr, "DisableBatteryCharging"}, 126 {5, nullptr, "AcquireControllerPowerSupply"},
129 {4, nullptr, "IsBatteryChargingEnabled"}, 127 {6, nullptr, "ReleaseControllerPowerSupply"},
130 {5, nullptr, "AcquireControllerPowerSupply"}, 128 {7, &PSM::OpenSession, "OpenSession"},
131 {6, nullptr, "ReleaseControllerPowerSupply"}, 129 {8, nullptr, "EnableEnoughPowerChargeEmulation"},
132 {7, &PSM::OpenSession, "OpenSession"}, 130 {9, nullptr, "DisableEnoughPowerChargeEmulation"},
133 {8, nullptr, "EnableEnoughPowerChargeEmulation"}, 131 {10, nullptr, "EnableFastBatteryCharging"},
134 {9, nullptr, "DisableEnoughPowerChargeEmulation"}, 132 {11, nullptr, "DisableFastBatteryCharging"},
135 {10, nullptr, "EnableFastBatteryCharging"}, 133 {12, nullptr, "GetBatteryVoltageState"},
136 {11, nullptr, "DisableFastBatteryCharging"}, 134 {13, nullptr, "GetRawBatteryChargePercentage"},
137 {12, nullptr, "GetBatteryVoltageState"}, 135 {14, nullptr, "IsEnoughPowerSupplied"},
138 {13, nullptr, "GetRawBatteryChargePercentage"}, 136 {15, nullptr, "GetBatteryAgePercentage"},
139 {14, nullptr, "IsEnoughPowerSupplied"}, 137 {16, nullptr, "GetBatteryChargeInfoEvent"},
140 {15, nullptr, "GetBatteryAgePercentage"}, 138 {17, nullptr, "GetBatteryChargeInfoFields"},
141 {16, nullptr, "GetBatteryChargeInfoEvent"}, 139 {18, nullptr, "GetBatteryChargeCalibratedEvent"},
142 {17, nullptr, "GetBatteryChargeInfoFields"}, 140 };
143 {18, nullptr, "GetBatteryChargeCalibratedEvent"}, 141 // clang-format on
144 };
145 // clang-format on
146
147 RegisterHandlers(functions);
148 }
149
150 ~PSM() override = default;
151
152private:
153 void GetBatteryChargePercentage(Kernel::HLERequestContext& ctx) {
154 LOG_DEBUG(Service_PSM, "called");
155 142
156 IPC::ResponseBuilder rb{ctx, 3}; 143 RegisterHandlers(functions);
157 rb.Push(ResultSuccess); 144}
158 rb.Push<u32>(battery_charge_percentage);
159 }
160 145
161 void GetChargerType(Kernel::HLERequestContext& ctx) { 146PSM::~PSM() = default;
162 LOG_DEBUG(Service_PSM, "called");
163 147
164 IPC::ResponseBuilder rb{ctx, 3}; 148void PSM::GetBatteryChargePercentage(Kernel::HLERequestContext& ctx) {
165 rb.Push(ResultSuccess); 149 LOG_DEBUG(Service_PTM, "called");
166 rb.PushEnum(charger_type);
167 }
168 150
169 void OpenSession(Kernel::HLERequestContext& ctx) { 151 IPC::ResponseBuilder rb{ctx, 3};
170 LOG_DEBUG(Service_PSM, "called"); 152 rb.Push(ResultSuccess);
153 rb.Push<u32>(battery_charge_percentage);
154}
171 155
172 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 156void PSM::GetChargerType(Kernel::HLERequestContext& ctx) {
173 rb.Push(ResultSuccess); 157 LOG_DEBUG(Service_PTM, "called");
174 rb.PushIpcInterface<IPsmSession>(system);
175 }
176 158
177 enum class ChargerType : u32 { 159 IPC::ResponseBuilder rb{ctx, 3};
178 Unplugged = 0, 160 rb.Push(ResultSuccess);
179 RegularCharger = 1, 161 rb.PushEnum(charger_type);
180 LowPowerCharger = 2, 162}
181 Unknown = 3,
182 };
183 163
184 u32 battery_charge_percentage{100}; // 100% 164void PSM::OpenSession(Kernel::HLERequestContext& ctx) {
185 ChargerType charger_type{ChargerType::RegularCharger}; 165 LOG_DEBUG(Service_PTM, "called");
186};
187 166
188void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) { 167 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
189 std::make_shared<PSM>(system)->InstallAsService(sm); 168 rb.Push(ResultSuccess);
169 rb.PushIpcInterface<IPsmSession>(system);
190} 170}
191 171
192} // namespace Service::PSM 172} // namespace Service::PTM
diff --git a/src/core/hle/service/ptm/psm.h b/src/core/hle/service/ptm/psm.h
index 94a1044db..f674ba8bc 100644
--- a/src/core/hle/service/ptm/psm.h
+++ b/src/core/hle/service/ptm/psm.h
@@ -3,16 +3,29 @@
3 3
4#pragma once 4#pragma once
5 5
6namespace Core { 6#include "core/hle/service/service.h"
7class System;
8}
9 7
10namespace Service::SM { 8namespace Service::PTM {
11class ServiceManager;
12}
13 9
14namespace Service::PSM { 10class PSM final : public ServiceFramework<PSM> {
11public:
12 explicit PSM(Core::System& system_);
13 ~PSM() override;
15 14
16void InstallInterfaces(SM::ServiceManager& sm, Core::System& system); 15private:
16 enum class ChargerType : u32 {
17 Unplugged = 0,
18 RegularCharger = 1,
19 LowPowerCharger = 2,
20 Unknown = 3,
21 };
17 22
18} // namespace Service::PSM 23 void GetBatteryChargePercentage(Kernel::HLERequestContext& ctx);
24 void GetChargerType(Kernel::HLERequestContext& ctx);
25 void OpenSession(Kernel::HLERequestContext& ctx);
26
27 u32 battery_charge_percentage{100};
28 ChargerType charger_type{ChargerType::RegularCharger};
29};
30
31} // namespace Service::PTM
diff --git a/src/core/hle/service/ptm/ptm.cpp b/src/core/hle/service/ptm/ptm.cpp
new file mode 100644
index 000000000..4bea995c6
--- /dev/null
+++ b/src/core/hle/service/ptm/ptm.cpp
@@ -0,0 +1,18 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include <memory>
5
6#include "core/core.h"
7#include "core/hle/service/ptm/psm.h"
8#include "core/hle/service/ptm/ptm.h"
9#include "core/hle/service/ptm/ts.h"
10
11namespace Service::PTM {
12
13void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) {
14 std::make_shared<PSM>(system)->InstallAsService(sm);
15 std::make_shared<TS>(system)->InstallAsService(sm);
16}
17
18} // namespace Service::PTM
diff --git a/src/core/hle/service/ptm/ptm.h b/src/core/hle/service/ptm/ptm.h
new file mode 100644
index 000000000..06224a24e
--- /dev/null
+++ b/src/core/hle/service/ptm/ptm.h
@@ -0,0 +1,18 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6namespace Core {
7class System;
8}
9
10namespace Service::SM {
11class ServiceManager;
12}
13
14namespace Service::PTM {
15
16void InstallInterfaces(SM::ServiceManager& sm, Core::System& system);
17
18} // namespace Service::PTM
diff --git a/src/core/hle/service/ptm/ts.cpp b/src/core/hle/service/ptm/ts.cpp
new file mode 100644
index 000000000..65c3f135f
--- /dev/null
+++ b/src/core/hle/service/ptm/ts.cpp
@@ -0,0 +1,41 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include <memory>
5
6#include "core/core.h"
7#include "core/hle/ipc_helpers.h"
8#include "core/hle/service/ptm/ts.h"
9
10namespace Service::PTM {
11
12TS::TS(Core::System& system_) : ServiceFramework{system_, "ts"} {
13 // clang-format off
14 static const FunctionInfo functions[] = {
15 {0, nullptr, "GetTemperatureRange"},
16 {1, &TS::GetTemperature, "GetTemperature"},
17 {2, nullptr, "SetMeasurementMode"},
18 {3, nullptr, "GetTemperatureMilliC"},
19 {4, nullptr, "OpenSession"},
20 };
21 // clang-format on
22
23 RegisterHandlers(functions);
24}
25
26TS::~TS() = default;
27
28void TS::GetTemperature(Kernel::HLERequestContext& ctx) {
29 IPC::RequestParser rp{ctx};
30 const auto location{rp.PopEnum<Location>()};
31
32 LOG_WARNING(Service_HID, "(STUBBED) called. location={}", location);
33
34 const s32 temperature = location == Location::Internal ? 35 : 20;
35
36 IPC::ResponseBuilder rb{ctx, 3};
37 rb.Push(ResultSuccess);
38 rb.Push(temperature);
39}
40
41} // namespace Service::PTM
diff --git a/src/core/hle/service/ptm/ts.h b/src/core/hle/service/ptm/ts.h
new file mode 100644
index 000000000..39a734ef7
--- /dev/null
+++ b/src/core/hle/service/ptm/ts.h
@@ -0,0 +1,25 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7#include "core/hle/service/service.h"
8
9namespace Service::PTM {
10
11class TS final : public ServiceFramework<TS> {
12public:
13 explicit TS(Core::System& system_);
14 ~TS() override;
15
16private:
17 enum class Location : u8 {
18 Internal,
19 External,
20 };
21
22 void GetTemperature(Kernel::HLERequestContext& ctx);
23};
24
25} // namespace Service::PTM
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 318009e4f..c64291e7f 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -58,7 +58,7 @@
58#include "core/hle/service/pm/pm.h" 58#include "core/hle/service/pm/pm.h"
59#include "core/hle/service/prepo/prepo.h" 59#include "core/hle/service/prepo/prepo.h"
60#include "core/hle/service/psc/psc.h" 60#include "core/hle/service/psc/psc.h"
61#include "core/hle/service/ptm/psm.h" 61#include "core/hle/service/ptm/ptm.h"
62#include "core/hle/service/service.h" 62#include "core/hle/service/service.h"
63#include "core/hle/service/set/settings.h" 63#include "core/hle/service/set/settings.h"
64#include "core/hle/service/sm/sm.h" 64#include "core/hle/service/sm/sm.h"
@@ -287,7 +287,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
287 PlayReport::InstallInterfaces(*sm, system); 287 PlayReport::InstallInterfaces(*sm, system);
288 PM::InstallInterfaces(system); 288 PM::InstallInterfaces(system);
289 PSC::InstallInterfaces(*sm, system); 289 PSC::InstallInterfaces(*sm, system);
290 PSM::InstallInterfaces(*sm, system); 290 PTM::InstallInterfaces(*sm, system);
291 Set::InstallInterfaces(*sm, system); 291 Set::InstallInterfaces(*sm, system);
292 Sockets::InstallInterfaces(*sm, system); 292 Sockets::InstallInterfaces(*sm, system);
293 SPL::InstallInterfaces(*sm, system); 293 SPL::InstallInterfaces(*sm, system);
diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp
index 446c027d3..00474ac77 100644
--- a/src/input_common/drivers/sdl_driver.cpp
+++ b/src/input_common/drivers/sdl_driver.cpp
@@ -438,10 +438,17 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en
438 using namespace std::chrono_literals; 438 using namespace std::chrono_literals;
439 while (initialized) { 439 while (initialized) {
440 SDL_PumpEvents(); 440 SDL_PumpEvents();
441 SendVibrations();
442 std::this_thread::sleep_for(1ms); 441 std::this_thread::sleep_for(1ms);
443 } 442 }
444 }); 443 });
444 vibration_thread = std::thread([this] {
445 Common::SetCurrentThreadName("yuzu:input:SDL_Vibration");
446 using namespace std::chrono_literals;
447 while (initialized) {
448 SendVibrations();
449 std::this_thread::sleep_for(10ms);
450 }
451 });
445 } 452 }
446 // Because the events for joystick connection happens before we have our event watcher added, we 453 // Because the events for joystick connection happens before we have our event watcher added, we
447 // can just open all the joysticks right here 454 // can just open all the joysticks right here
@@ -457,6 +464,7 @@ SDLDriver::~SDLDriver() {
457 initialized = false; 464 initialized = false;
458 if (start_thread) { 465 if (start_thread) {
459 poll_thread.join(); 466 poll_thread.join();
467 vibration_thread.join();
460 SDL_QuitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER); 468 SDL_QuitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER);
461 } 469 }
462} 470}
diff --git a/src/input_common/drivers/sdl_driver.h b/src/input_common/drivers/sdl_driver.h
index 0846fbb50..7dc7a93c7 100644
--- a/src/input_common/drivers/sdl_driver.h
+++ b/src/input_common/drivers/sdl_driver.h
@@ -128,5 +128,6 @@ private:
128 std::atomic<bool> initialized = false; 128 std::atomic<bool> initialized = false;
129 129
130 std::thread poll_thread; 130 std::thread poll_thread;
131 std::thread vibration_thread;
131}; 132};
132} // namespace InputCommon 133} // namespace InputCommon
diff --git a/src/yuzu/applets/qt_web_browser.cpp b/src/yuzu/applets/qt_web_browser.cpp
index 790edbb2a..89bd482e0 100644
--- a/src/yuzu/applets/qt_web_browser.cpp
+++ b/src/yuzu/applets/qt_web_browser.cpp
@@ -2,6 +2,8 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#ifdef YUZU_USE_QT_WEB_ENGINE 4#ifdef YUZU_USE_QT_WEB_ENGINE
5#include <bit>
6
5#include <QApplication> 7#include <QApplication>
6#include <QKeyEvent> 8#include <QKeyEvent>
7 9
@@ -211,8 +213,10 @@ template <Core::HID::NpadButton... T>
211void QtNXWebEngineView::HandleWindowFooterButtonPressedOnce() { 213void QtNXWebEngineView::HandleWindowFooterButtonPressedOnce() {
212 const auto f = [this](Core::HID::NpadButton button) { 214 const auto f = [this](Core::HID::NpadButton button) {
213 if (input_interpreter->IsButtonPressedOnce(button)) { 215 if (input_interpreter->IsButtonPressedOnce(button)) {
216 const auto button_index = std::countr_zero(static_cast<u64>(button));
217
214 page()->runJavaScript( 218 page()->runJavaScript(
215 QStringLiteral("yuzu_key_callbacks[%1] == null;").arg(static_cast<u8>(button)), 219 QStringLiteral("yuzu_key_callbacks[%1] == null;").arg(button_index),
216 [this, button](const QVariant& variant) { 220 [this, button](const QVariant& variant) {
217 if (variant.toBool()) { 221 if (variant.toBool()) {
218 switch (button) { 222 switch (button) {
@@ -236,7 +240,7 @@ void QtNXWebEngineView::HandleWindowFooterButtonPressedOnce() {
236 240
237 page()->runJavaScript( 241 page()->runJavaScript(
238 QStringLiteral("if (yuzu_key_callbacks[%1] != null) { yuzu_key_callbacks[%1](); }") 242 QStringLiteral("if (yuzu_key_callbacks[%1] != null) { yuzu_key_callbacks[%1](); }")
239 .arg(static_cast<u8>(button))); 243 .arg(button_index));
240 } 244 }
241 }; 245 };
242 246
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 9686412d0..a57b2e019 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -143,8 +143,8 @@ void Config::ReadBasicSetting(Settings::Setting<std::string>& setting) {
143 } 143 }
144} 144}
145 145
146template <typename Type> 146template <typename Type, bool ranged>
147void Config::ReadBasicSetting(Settings::Setting<Type>& setting) { 147void Config::ReadBasicSetting(Settings::Setting<Type, ranged>& setting) {
148 const QString name = QString::fromStdString(setting.GetLabel()); 148 const QString name = QString::fromStdString(setting.GetLabel());
149 const Type default_value = setting.GetDefault(); 149 const Type default_value = setting.GetDefault();
150 if (qt_config->value(name + QStringLiteral("/default"), false).toBool()) { 150 if (qt_config->value(name + QStringLiteral("/default"), false).toBool()) {
@@ -164,16 +164,16 @@ void Config::WriteBasicSetting(const Settings::Setting<std::string>& setting) {
164 qt_config->setValue(name, QString::fromStdString(value)); 164 qt_config->setValue(name, QString::fromStdString(value));
165} 165}
166 166
167template <typename Type> 167template <typename Type, bool ranged>
168void Config::WriteBasicSetting(const Settings::Setting<Type>& setting) { 168void Config::WriteBasicSetting(const Settings::Setting<Type, ranged>& setting) {
169 const QString name = QString::fromStdString(setting.GetLabel()); 169 const QString name = QString::fromStdString(setting.GetLabel());
170 const Type value = setting.GetValue(); 170 const Type value = setting.GetValue();
171 qt_config->setValue(name + QStringLiteral("/default"), value == setting.GetDefault()); 171 qt_config->setValue(name + QStringLiteral("/default"), value == setting.GetDefault());
172 qt_config->setValue(name, value); 172 qt_config->setValue(name, value);
173} 173}
174 174
175template <typename Type> 175template <typename Type, bool ranged>
176void Config::WriteGlobalSetting(const Settings::SwitchableSetting<Type>& setting) { 176void Config::WriteGlobalSetting(const Settings::SwitchableSetting<Type, ranged>& setting) {
177 const QString name = QString::fromStdString(setting.GetLabel()); 177 const QString name = QString::fromStdString(setting.GetLabel());
178 const Type& value = setting.GetValue(global); 178 const Type& value = setting.GetValue(global);
179 if (!global) { 179 if (!global) {
@@ -1421,8 +1421,8 @@ QVariant Config::ReadSetting(const QString& name, const QVariant& default_value)
1421 return result; 1421 return result;
1422} 1422}
1423 1423
1424template <typename Type> 1424template <typename Type, bool ranged>
1425void Config::ReadGlobalSetting(Settings::SwitchableSetting<Type>& setting) { 1425void Config::ReadGlobalSetting(Settings::SwitchableSetting<Type, ranged>& setting) {
1426 QString name = QString::fromStdString(setting.GetLabel()); 1426 QString name = QString::fromStdString(setting.GetLabel());
1427 const bool use_global = qt_config->value(name + QStringLiteral("/use_global"), true).toBool(); 1427 const bool use_global = qt_config->value(name + QStringLiteral("/use_global"), true).toBool();
1428 setting.SetGlobal(use_global); 1428 setting.SetGlobal(use_global);
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index 9ca878d23..d511b3dbd 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -159,8 +159,8 @@ private:
159 * 159 *
160 * @param The setting 160 * @param The setting
161 */ 161 */
162 template <typename Type> 162 template <typename Type, bool ranged>
163 void ReadGlobalSetting(Settings::SwitchableSetting<Type>& setting); 163 void ReadGlobalSetting(Settings::SwitchableSetting<Type, ranged>& setting);
164 164
165 /** 165 /**
166 * Sets a value to the qt_config using the setting's label and default value. If the config is a 166 * Sets a value to the qt_config using the setting's label and default value. If the config is a
@@ -168,8 +168,8 @@ private:
168 * 168 *
169 * @param The setting 169 * @param The setting
170 */ 170 */
171 template <typename Type> 171 template <typename Type, bool ranged>
172 void WriteGlobalSetting(const Settings::SwitchableSetting<Type>& setting); 172 void WriteGlobalSetting(const Settings::SwitchableSetting<Type, ranged>& setting);
173 173
174 /** 174 /**
175 * Reads a value from the qt_config using the setting's label and default value and applies the 175 * Reads a value from the qt_config using the setting's label and default value and applies the
@@ -177,15 +177,15 @@ private:
177 * 177 *
178 * @param The setting 178 * @param The setting
179 */ 179 */
180 template <typename Type> 180 template <typename Type, bool ranged>
181 void ReadBasicSetting(Settings::Setting<Type>& setting); 181 void ReadBasicSetting(Settings::Setting<Type, ranged>& setting);
182 182
183 /** Sets a value from the setting in the qt_config using the setting's label and default value. 183 /** Sets a value from the setting in the qt_config using the setting's label and default value.
184 * 184 *
185 * @param The setting 185 * @param The setting
186 */ 186 */
187 template <typename Type> 187 template <typename Type, bool ranged>
188 void WriteBasicSetting(const Settings::Setting<Type>& setting); 188 void WriteBasicSetting(const Settings::Setting<Type, ranged>& setting);
189 189
190 ConfigType type; 190 ConfigType type;
191 std::unique_ptr<QSettings> qt_config; 191 std::unique_ptr<QSettings> qt_config;
diff --git a/src/yuzu/configuration/configuration_shared.h b/src/yuzu/configuration/configuration_shared.h
index 77802a367..56800b6ff 100644
--- a/src/yuzu/configuration/configuration_shared.h
+++ b/src/yuzu/configuration/configuration_shared.h
@@ -27,8 +27,9 @@ enum class CheckState {
27// ApplyPerGameSetting, given a Settings::Setting and a Qt UI element, properly applies a Setting 27// ApplyPerGameSetting, given a Settings::Setting and a Qt UI element, properly applies a Setting
28void ApplyPerGameSetting(Settings::SwitchableSetting<bool>* setting, const QCheckBox* checkbox, 28void ApplyPerGameSetting(Settings::SwitchableSetting<bool>* setting, const QCheckBox* checkbox,
29 const CheckState& tracker); 29 const CheckState& tracker);
30template <typename Type> 30template <typename Type, bool ranged>
31void ApplyPerGameSetting(Settings::SwitchableSetting<Type>* setting, const QComboBox* combobox) { 31void ApplyPerGameSetting(Settings::SwitchableSetting<Type, ranged>* setting,
32 const QComboBox* combobox) {
32 if (Settings::IsConfiguringGlobal() && setting->UsingGlobal()) { 33 if (Settings::IsConfiguringGlobal() && setting->UsingGlobal()) {
33 setting->SetValue(static_cast<Type>(combobox->currentIndex())); 34 setting->SetValue(static_cast<Type>(combobox->currentIndex()));
34 } else if (!Settings::IsConfiguringGlobal()) { 35 } else if (!Settings::IsConfiguringGlobal()) {
@@ -45,8 +46,9 @@ void ApplyPerGameSetting(Settings::SwitchableSetting<Type>* setting, const QComb
45// Sets a Qt UI element given a Settings::Setting 46// Sets a Qt UI element given a Settings::Setting
46void SetPerGameSetting(QCheckBox* checkbox, const Settings::SwitchableSetting<bool>* setting); 47void SetPerGameSetting(QCheckBox* checkbox, const Settings::SwitchableSetting<bool>* setting);
47 48
48template <typename Type> 49template <typename Type, bool ranged>
49void SetPerGameSetting(QComboBox* combobox, const Settings::SwitchableSetting<Type>* setting) { 50void SetPerGameSetting(QComboBox* combobox,
51 const Settings::SwitchableSetting<Type, ranged>* setting) {
50 combobox->setCurrentIndex(setting->UsingGlobal() ? ConfigurationShared::USE_GLOBAL_INDEX 52 combobox->setCurrentIndex(setting->UsingGlobal() ? ConfigurationShared::USE_GLOBAL_INDEX
51 : static_cast<int>(setting->GetValue()) + 53 : static_cast<int>(setting->GetValue()) +
52 ConfigurationShared::USE_GLOBAL_OFFSET); 54 ConfigurationShared::USE_GLOBAL_OFFSET);
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 903e02297..9db7115a2 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -99,8 +99,8 @@ void Config::ReadSetting(const std::string& group, Settings::Setting<bool>& sett
99 setting = sdl2_config->GetBoolean(group, setting.GetLabel(), setting.GetDefault()); 99 setting = sdl2_config->GetBoolean(group, setting.GetLabel(), setting.GetDefault());
100} 100}
101 101
102template <typename Type> 102template <typename Type, bool ranged>
103void Config::ReadSetting(const std::string& group, Settings::Setting<Type>& setting) { 103void Config::ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting) {
104 setting = static_cast<Type>(sdl2_config->GetInteger(group, setting.GetLabel(), 104 setting = static_cast<Type>(sdl2_config->GetInteger(group, setting.GetLabel(),
105 static_cast<long>(setting.GetDefault()))); 105 static_cast<long>(setting.GetDefault())));
106} 106}
diff --git a/src/yuzu_cmd/config.h b/src/yuzu_cmd/config.h
index ccf77d668..32c03075f 100644
--- a/src/yuzu_cmd/config.h
+++ b/src/yuzu_cmd/config.h
@@ -33,6 +33,6 @@ private:
33 * @param group The name of the INI group 33 * @param group The name of the INI group
34 * @param setting The yuzu setting to modify 34 * @param setting The yuzu setting to modify
35 */ 35 */
36 template <typename Type> 36 template <typename Type, bool ranged>
37 void ReadSetting(const std::string& group, Settings::Setting<Type>& setting); 37 void ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting);
38}; 38};