summaryrefslogtreecommitdiffstats
path: root/src/common
diff options
context:
space:
mode:
authorliamwhite <liamwhite@users.noreply.github.com>2023-08-02 20:25:52 +0200
committerGitHub <noreply@github.com>2023-08-02 20:25:52 +0200
commitfca7d975fdbeb1c63677b80efc03920affee4b12 (patch)
treebbded6cf80886c6def87bae92cf6784340165de9 /src/common
parentMerge pull request #11204 from liamwhite/eds3-blend-amd (diff)
parentconfig(qt): Fix name of network category (diff)
downloadyuzu-fca7d975fdbeb1c63677b80efc03920affee4b12.tar
yuzu-fca7d975fdbeb1c63677b80efc03920affee4b12.tar.gz
yuzu-fca7d975fdbeb1c63677b80efc03920affee4b12.tar.bz2
yuzu-fca7d975fdbeb1c63677b80efc03920affee4b12.tar.lz
yuzu-fca7d975fdbeb1c63677b80efc03920affee4b12.tar.xz
yuzu-fca7d975fdbeb1c63677b80efc03920affee4b12.tar.zst
yuzu-fca7d975fdbeb1c63677b80efc03920affee4b12.zip
Diffstat (limited to 'src/common')
-rw-r--r--src/common/CMakeLists.txt15
-rw-r--r--src/common/logging/backend.cpp2
-rw-r--r--src/common/settings.cpp248
-rw-r--r--src/common/settings.h860
-rw-r--r--src/common/settings_common.cpp58
-rw-r--r--src/common/settings_common.h256
-rw-r--r--src/common/settings_enums.h214
-rw-r--r--src/common/settings_setting.h394
8 files changed, 1455 insertions, 592 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 3adf13a3f..bf97d9ba2 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -110,8 +110,12 @@ add_library(common STATIC
scratch_buffer.h
settings.cpp
settings.h
+ settings_common.cpp
+ settings_common.h
+ settings_enums.h
settings_input.cpp
settings_input.h
+ settings_setting.h
socket_types.h
spin_lock.cpp
spin_lock.h
@@ -193,9 +197,16 @@ if (MSVC)
/we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
/we4800 # Implicit conversion from 'type' to bool. Possible information loss
)
-else()
+endif()
+
+if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
target_compile_options(common PRIVATE
- $<$<CXX_COMPILER_ID:Clang>:-fsized-deallocation>
+ -fsized-deallocation
+ -Werror=unreachable-code-aggressive
+ )
+ target_compile_definitions(common PRIVATE
+ # Clang 14 and earlier have errors when explicitly instantiating Settings::Setting
+ $<$<VERSION_LESS:$<CXX_COMPILER_VERSION>,15>:CANNOT_EXPLICITLY_INSTANTIATE>
)
endif()
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 6e8e8eb36..d4f27197c 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -108,7 +108,7 @@ public:
using namespace Common::Literals;
// Prevent logs from exceeding a set maximum size in the event that log entries are spammed.
- const auto write_limit = Settings::values.extended_logging ? 1_GiB : 100_MiB;
+ const auto write_limit = Settings::values.extended_logging.GetValue() ? 1_GiB : 100_MiB;
const bool write_limit_exceeded = bytes_written > write_limit;
if (entry.log_level >= Level::Error || write_limit_exceeded) {
if (write_limit_exceeded) {
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index d4e55f988..15fd2e222 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -7,9 +7,16 @@
#include <exception>
#include <stdexcept>
#endif
+#include <compare>
+#include <cstddef>
+#include <filesystem>
+#include <functional>
#include <string_view>
+#include <type_traits>
+#include <fmt/core.h>
#include "common/assert.h"
+#include "common/fs/fs_util.h"
#include "common/fs/path_util.h"
#include "common/logging/log.h"
#include "common/settings.h"
@@ -17,11 +24,50 @@
namespace Settings {
+// Clang 14 and earlier have errors when explicitly instantiating these classes
+#ifndef CANNOT_EXPLICITLY_INSTANTIATE
+#define SETTING(TYPE, RANGED) template class Setting<TYPE, RANGED>
+#define SWITCHABLE(TYPE, RANGED) template class SwitchableSetting<TYPE, RANGED>
+
+SETTING(AudioEngine, false);
+SETTING(bool, false);
+SETTING(int, false);
+SETTING(std::string, false);
+SETTING(u16, false);
+SWITCHABLE(AnisotropyMode, true);
+SWITCHABLE(AntiAliasing, false);
+SWITCHABLE(AspectRatio, true);
+SWITCHABLE(AstcDecodeMode, true);
+SWITCHABLE(AstcRecompression, true);
+SWITCHABLE(AudioMode, true);
+SWITCHABLE(CpuAccuracy, true);
+SWITCHABLE(FullscreenMode, true);
+SWITCHABLE(GpuAccuracy, true);
+SWITCHABLE(Language, true);
+SWITCHABLE(NvdecEmulation, false);
+SWITCHABLE(Region, true);
+SWITCHABLE(RendererBackend, true);
+SWITCHABLE(ScalingFilter, false);
+SWITCHABLE(ShaderBackend, true);
+SWITCHABLE(TimeZone, true);
+SETTING(VSyncMode, true);
+SWITCHABLE(bool, false);
+SWITCHABLE(int, false);
+SWITCHABLE(int, true);
+SWITCHABLE(s64, false);
+SWITCHABLE(u16, true);
+SWITCHABLE(u32, false);
+SWITCHABLE(u8, false);
+SWITCHABLE(u8, true);
+
+#undef SETTING
+#undef SWITCHABLE
+#endif
+
Values values;
-static bool configuring_global = true;
-std::string GetTimeZoneString() {
- const auto time_zone_index = static_cast<std::size_t>(values.time_zone_index.GetValue());
+std::string GetTimeZoneString(TimeZone time_zone) {
+ const auto time_zone_index = static_cast<std::size_t>(time_zone);
ASSERT(time_zone_index < Common::TimeZone::GetTimeZoneStrings().size());
std::string location_name;
@@ -61,73 +107,35 @@ void LogSettings() {
};
LOG_INFO(Config, "yuzu Configuration:");
- log_setting("Controls_UseDockedMode", values.use_docked_mode.GetValue());
- log_setting("System_RngSeed", values.rng_seed.GetValue().value_or(0));
- log_setting("System_DeviceName", values.device_name.GetValue());
- log_setting("System_CurrentUser", values.current_user.GetValue());
- log_setting("System_LanguageIndex", values.language_index.GetValue());
- log_setting("System_RegionIndex", values.region_index.GetValue());
- log_setting("System_TimeZoneIndex", values.time_zone_index.GetValue());
- log_setting("System_UnsafeMemoryLayout", values.use_unsafe_extended_memory_layout.GetValue());
- log_setting("Core_UseMultiCore", values.use_multi_core.GetValue());
- log_setting("CPU_Accuracy", values.cpu_accuracy.GetValue());
- log_setting("Renderer_UseResolutionScaling", values.resolution_setup.GetValue());
- log_setting("Renderer_ScalingFilter", values.scaling_filter.GetValue());
- log_setting("Renderer_FSRSlider", values.fsr_sharpening_slider.GetValue());
- log_setting("Renderer_AntiAliasing", values.anti_aliasing.GetValue());
- log_setting("Renderer_UseSpeedLimit", values.use_speed_limit.GetValue());
- log_setting("Renderer_SpeedLimit", values.speed_limit.GetValue());
- log_setting("Renderer_UseDiskShaderCache", values.use_disk_shader_cache.GetValue());
- log_setting("Renderer_GPUAccuracyLevel", values.gpu_accuracy.GetValue());
- log_setting("Renderer_UseAsynchronousGpuEmulation",
- values.use_asynchronous_gpu_emulation.GetValue());
- log_setting("Renderer_NvdecEmulation", values.nvdec_emulation.GetValue());
- log_setting("Renderer_AccelerateASTC", values.accelerate_astc.GetValue());
- log_setting("Renderer_AsyncASTC", values.async_astc.GetValue());
- log_setting("Renderer_AstcRecompression", values.astc_recompression.GetValue());
- log_setting("Renderer_UseVsync", values.vsync_mode.GetValue());
- log_setting("Renderer_UseReactiveFlushing", values.use_reactive_flushing.GetValue());
- log_setting("Renderer_ShaderBackend", values.shader_backend.GetValue());
- log_setting("Renderer_UseAsynchronousShaders", values.use_asynchronous_shaders.GetValue());
- log_setting("Renderer_AnisotropicFilteringLevel", values.max_anisotropy.GetValue());
- log_setting("Audio_OutputEngine", values.sink_id.GetValue());
- log_setting("Audio_OutputDevice", values.audio_output_device_id.GetValue());
- log_setting("Audio_InputDevice", values.audio_input_device_id.GetValue());
- log_setting("DataStorage_UseVirtualSd", values.use_virtual_sd.GetValue());
+ for (auto& [category, settings] : values.linkage.by_category) {
+ for (const auto& setting : settings) {
+ if (setting->Id() == values.yuzu_token.Id()) {
+ // Hide the token secret, for security reasons.
+ continue;
+ }
+
+ const auto name = fmt::format(
+ "{:c}{:c} {}.{}", setting->ToString() == setting->DefaultToString() ? '-' : 'M',
+ setting->UsingGlobal() ? '-' : 'C', TranslateCategory(category),
+ setting->GetLabel());
+
+ log_setting(name, setting->Canonicalize());
+ }
+ }
log_path("DataStorage_CacheDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir));
log_path("DataStorage_ConfigDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir));
log_path("DataStorage_LoadDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::LoadDir));
log_path("DataStorage_NANDDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir));
log_path("DataStorage_SDMCDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::SDMCDir));
- log_setting("Debugging_ProgramArgs", values.program_args.GetValue());
- log_setting("Debugging_GDBStub", values.use_gdbstub.GetValue());
- log_setting("Input_EnableMotion", values.motion_enabled.GetValue());
- log_setting("Input_EnableVibration", values.vibration_enabled.GetValue());
- log_setting("Input_EnableTouch", values.touchscreen.enabled);
- log_setting("Input_EnableMouse", values.mouse_enabled.GetValue());
- log_setting("Input_EnableKeyboard", values.keyboard_enabled.GetValue());
- log_setting("Input_EnableRingController", values.enable_ring_controller.GetValue());
- log_setting("Input_EnableIrSensor", values.enable_ir_sensor.GetValue());
- log_setting("Input_EnableCustomJoycon", values.enable_joycon_driver.GetValue());
- log_setting("Input_EnableCustomProController", values.enable_procon_driver.GetValue());
- log_setting("Input_EnableRawInput", values.enable_raw_input.GetValue());
-}
-
-bool IsConfiguringGlobal() {
- return configuring_global;
-}
-
-void SetConfiguringGlobal(bool is_global) {
- configuring_global = is_global;
}
bool IsGPULevelExtreme() {
- return values.gpu_accuracy.GetValue() == GPUAccuracy::Extreme;
+ return values.gpu_accuracy.GetValue() == GpuAccuracy::Extreme;
}
bool IsGPULevelHigh() {
- return values.gpu_accuracy.GetValue() == GPUAccuracy::Extreme ||
- values.gpu_accuracy.GetValue() == GPUAccuracy::High;
+ return values.gpu_accuracy.GetValue() == GpuAccuracy::Extreme ||
+ values.gpu_accuracy.GetValue() == GpuAccuracy::High;
}
bool IsFastmemEnabled() {
@@ -144,6 +152,61 @@ float Volume() {
return values.volume.GetValue() / static_cast<f32>(values.volume.GetDefault());
}
+const char* TranslateCategory(Category category) {
+ switch (category) {
+ case Category::Audio:
+ return "Audio";
+ case Category::Core:
+ return "Core";
+ case Category::Cpu:
+ case Category::CpuDebug:
+ case Category::CpuUnsafe:
+ return "Cpu";
+ case Category::Renderer:
+ case Category::RendererAdvanced:
+ case Category::RendererDebug:
+ return "Renderer";
+ case Category::System:
+ case Category::SystemAudio:
+ return "System";
+ case Category::DataStorage:
+ return "Data Storage";
+ case Category::Debugging:
+ case Category::DebuggingGraphics:
+ return "Debugging";
+ case Category::Miscellaneous:
+ return "Miscellaneous";
+ case Category::Network:
+ return "Network";
+ case Category::WebService:
+ return "WebService";
+ case Category::AddOns:
+ return "DisabledAddOns";
+ case Category::Controls:
+ return "Controls";
+ case Category::Ui:
+ case Category::UiGeneral:
+ return "UI";
+ case Category::UiLayout:
+ return "UiLayout";
+ case Category::UiGameList:
+ return "UiGameList";
+ case Category::Screenshots:
+ return "Screenshots";
+ case Category::Shortcuts:
+ return "Shortcuts";
+ case Category::Multiplayer:
+ return "Multiplayer";
+ case Category::Services:
+ return "Services";
+ case Category::Paths:
+ return "Paths";
+ case Category::MaxEnum:
+ break;
+ }
+ return "Miscellaneous";
+}
+
void UpdateRescalingInfo() {
const auto setup = values.resolution_setup.GetValue();
auto& info = values.resolution_info;
@@ -212,66 +275,19 @@ void RestoreGlobalState(bool is_powered_on) {
return;
}
- // Audio
- values.volume.SetGlobal(true);
-
- // Core
- values.use_multi_core.SetGlobal(true);
- values.use_unsafe_extended_memory_layout.SetGlobal(true);
-
- // CPU
- values.cpu_accuracy.SetGlobal(true);
- values.cpuopt_unsafe_unfuse_fma.SetGlobal(true);
- values.cpuopt_unsafe_reduce_fp_error.SetGlobal(true);
- values.cpuopt_unsafe_ignore_standard_fpcr.SetGlobal(true);
- values.cpuopt_unsafe_inaccurate_nan.SetGlobal(true);
- values.cpuopt_unsafe_fastmem_check.SetGlobal(true);
- values.cpuopt_unsafe_ignore_global_monitor.SetGlobal(true);
+ for (const auto& reset : values.linkage.restore_functions) {
+ reset();
+ }
+}
- // Renderer
- values.fsr_sharpening_slider.SetGlobal(true);
- values.renderer_backend.SetGlobal(true);
- values.async_presentation.SetGlobal(true);
- values.renderer_force_max_clock.SetGlobal(true);
- values.vulkan_device.SetGlobal(true);
- values.fullscreen_mode.SetGlobal(true);
- values.aspect_ratio.SetGlobal(true);
- values.resolution_setup.SetGlobal(true);
- values.scaling_filter.SetGlobal(true);
- values.anti_aliasing.SetGlobal(true);
- values.max_anisotropy.SetGlobal(true);
- values.use_speed_limit.SetGlobal(true);
- values.speed_limit.SetGlobal(true);
- values.use_disk_shader_cache.SetGlobal(true);
- values.gpu_accuracy.SetGlobal(true);
- values.use_asynchronous_gpu_emulation.SetGlobal(true);
- values.nvdec_emulation.SetGlobal(true);
- values.accelerate_astc.SetGlobal(true);
- values.async_astc.SetGlobal(true);
- values.astc_recompression.SetGlobal(true);
- values.use_reactive_flushing.SetGlobal(true);
- values.shader_backend.SetGlobal(true);
- values.use_asynchronous_shaders.SetGlobal(true);
- values.use_fast_gpu_time.SetGlobal(true);
- values.use_vulkan_driver_pipeline_cache.SetGlobal(true);
- values.bg_red.SetGlobal(true);
- values.bg_green.SetGlobal(true);
- values.bg_blue.SetGlobal(true);
- values.enable_compute_pipelines.SetGlobal(true);
- values.use_video_framerate.SetGlobal(true);
+static bool configuring_global = true;
- // System
- values.language_index.SetGlobal(true);
- values.region_index.SetGlobal(true);
- values.time_zone_index.SetGlobal(true);
- values.rng_seed.SetGlobal(true);
- values.sound_index.SetGlobal(true);
+bool IsConfiguringGlobal() {
+ return configuring_global;
+}
- // Controls
- values.players.SetGlobal(true);
- values.use_docked_mode.SetGlobal(true);
- values.vibration_enabled.SetGlobal(true);
- values.motion_enabled.SetGlobal(true);
+void SetConfiguringGlobal(bool is_global) {
+ configuring_global = is_global;
}
} // namespace Settings
diff --git a/src/common/settings.h b/src/common/settings.h
index 59e96e74f..b0bc6519a 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -6,95 +6,21 @@
#include <algorithm>
#include <array>
#include <map>
-#include <optional>
+#include <memory>
+#include <stdexcept>
#include <string>
#include <utility>
#include <vector>
#include "common/common_types.h"
+#include "common/settings_common.h"
+#include "common/settings_enums.h"
#include "common/settings_input.h"
+#include "common/settings_setting.h"
namespace Settings {
-enum class VSyncMode : u32 {
- Immediate = 0,
- Mailbox = 1,
- FIFO = 2,
- FIFORelaxed = 3,
-};
-
-enum class RendererBackend : u32 {
- OpenGL = 0,
- Vulkan = 1,
- Null = 2,
-};
-
-enum class ShaderBackend : u32 {
- GLSL = 0,
- GLASM = 1,
- SPIRV = 2,
-};
-
-enum class GPUAccuracy : u32 {
- Normal = 0,
- High = 1,
- Extreme = 2,
-};
-
-enum class CPUAccuracy : u32 {
- Auto = 0,
- Accurate = 1,
- Unsafe = 2,
- Paranoid = 3,
-};
-
-enum class FullscreenMode : u32 {
- Borderless = 0,
- Exclusive = 1,
-};
-
-enum class NvdecEmulation : u32 {
- Off = 0,
- CPU = 1,
- GPU = 2,
-};
-
-enum class ResolutionSetup : u32 {
- Res1_2X = 0,
- Res3_4X = 1,
- Res1X = 2,
- Res3_2X = 3,
- Res2X = 4,
- Res3X = 5,
- Res4X = 6,
- Res5X = 7,
- Res6X = 8,
- Res7X = 9,
- Res8X = 10,
-};
-
-enum class ScalingFilter : u32 {
- NearestNeighbor = 0,
- Bilinear = 1,
- Bicubic = 2,
- Gaussian = 3,
- ScaleForce = 4,
- Fsr = 5,
- LastFilter = Fsr,
-};
-
-enum class AntiAliasing : u32 {
- None = 0,
- Fxaa = 1,
- Smaa = 2,
- LastAA = Smaa,
-};
-
-enum class AstcRecompression : u32 {
- Uncompressed = 0,
- Bc1 = 1,
- Bc3 = 2,
-};
+const char* TranslateCategory(Settings::Category category);
struct ResolutionScalingInfo {
u32 up_scale{1};
@@ -119,239 +45,47 @@ struct ResolutionScalingInfo {
}
};
-/** The Setting class is a simple resource manager. It defines a label and default value alongside
- * the actual value of the setting for simpler and less-error prone use with frontend
- * configurations. Specifying a default value and label is required. A minimum and maximum range can
- * be specified for sanitization.
- */
-template <typename Type, bool ranged = false>
-class Setting {
-protected:
- Setting() = default;
-
- /**
- * Only sets the setting to the given initializer, leaving the other members to their default
- * initializers.
- *
- * @param global_val Initial value of the setting
- */
- explicit Setting(const Type& val) : value{val} {}
-
-public:
- /**
- * Sets a default value, label, and setting value.
- *
- * @param default_val Initial value of the setting, and default value of the setting
- * @param name Label for the setting
- */
- explicit Setting(const Type& default_val, const std::string& name)
- requires(!ranged)
- : value{default_val}, default_value{default_val}, label{name} {}
- virtual ~Setting() = default;
-
- /**
- * Sets a default value, minimum value, maximum value, and label.
- *
- * @param default_val Initial value of the setting, and default value of the setting
- * @param min_val Sets the minimum allowed value of the setting
- * @param max_val Sets the maximum allowed value of the setting
- * @param name Label for the setting
- */
- explicit Setting(const Type& default_val, const Type& min_val, const Type& max_val,
- const std::string& name)
- requires(ranged)
- : value{default_val},
- default_value{default_val}, maximum{max_val}, minimum{min_val}, label{name} {}
-
- /**
- * Returns a reference to the setting's value.
- *
- * @returns A reference to the setting
- */
- [[nodiscard]] virtual const Type& GetValue() const {
- return value;
- }
-
- /**
- * Sets the setting to the given value.
- *
- * @param val The desired value
- */
- virtual void SetValue(const Type& val) {
- Type temp{ranged ? std::clamp(val, minimum, maximum) : val};
- std::swap(value, temp);
- }
-
- /**
- * Returns the value that this setting was created with.
- *
- * @returns A reference to the default value
- */
- [[nodiscard]] const Type& GetDefault() const {
- return default_value;
- }
-
- /**
- * Returns the label this setting was created with.
- *
- * @returns A reference to the label
- */
- [[nodiscard]] const std::string& GetLabel() const {
- return label;
- }
-
- /**
- * Assigns a value to the setting.
- *
- * @param val The desired setting value
- *
- * @returns A reference to the setting
- */
- virtual const Type& operator=(const Type& val) {
- Type temp{ranged ? std::clamp(val, minimum, maximum) : val};
- std::swap(value, temp);
- return value;
- }
-
- /**
- * Returns a reference to the setting.
- *
- * @returns A reference to the setting
- */
- explicit virtual operator const Type&() const {
- return value;
- }
-
-protected:
- Type value{}; ///< The setting
- const Type default_value{}; ///< The default value
- const Type maximum{}; ///< Maximum allowed value of the setting
- const Type minimum{}; ///< Minimum allowed value of the setting
- const std::string label{}; ///< The setting's label
-};
-
-/**
- * The SwitchableSetting class is a slightly more complex version of the Setting class. This adds a
- * custom setting to switch to when a guest application specifically requires it. The effect is that
- * other components of the emulator can access the setting's intended value without any need for the
- * component to ask whether the custom or global setting is needed at the moment.
- *
- * By default, the global setting is used.
- */
-template <typename Type, bool ranged = false>
-class SwitchableSetting : virtual public Setting<Type, ranged> {
-public:
- /**
- * Sets a default value, label, and setting value.
- *
- * @param default_val Initial value of the setting, and default value of the setting
- * @param name Label for the setting
- */
- explicit SwitchableSetting(const Type& default_val, const std::string& name)
- requires(!ranged)
- : Setting<Type>{default_val, name} {}
- virtual ~SwitchableSetting() = default;
-
- /**
- * Sets a default value, minimum value, maximum value, and label.
- *
- * @param default_val Initial value of the setting, and default value of the setting
- * @param min_val Sets the minimum allowed value of the setting
- * @param max_val Sets the maximum allowed value of the setting
- * @param name Label for the setting
- */
- explicit SwitchableSetting(const Type& default_val, const Type& min_val, const Type& max_val,
- const std::string& name)
- requires(ranged)
- : Setting<Type, true>{default_val, min_val, max_val, name} {}
-
- /**
- * Tells this setting to represent either the global or custom setting when other member
- * functions are used.
- *
- * @param to_global Whether to use the global or custom setting.
- */
- void SetGlobal(bool to_global) {
- use_global = to_global;
- }
-
- /**
- * Returns whether this setting is using the global setting or not.
- *
- * @returns The global state
- */
- [[nodiscard]] bool UsingGlobal() const {
- return use_global;
- }
-
- /**
- * Returns either the global or custom setting depending on the values of this setting's global
- * state or if the global value was specifically requested.
- *
- * @param need_global Request global value regardless of setting's state; defaults to false
- *
- * @returns The required value of the setting
- */
- [[nodiscard]] virtual const Type& GetValue() const override {
- if (use_global) {
- return this->value;
- }
- return custom;
- }
- [[nodiscard]] virtual const Type& GetValue(bool need_global) const {
- if (use_global || need_global) {
- return this->value;
- }
- return custom;
- }
-
- /**
- * Sets the current setting value depending on the global state.
- *
- * @param val The new value
- */
- void SetValue(const Type& val) override {
- Type temp{ranged ? std::clamp(val, this->minimum, this->maximum) : val};
- if (use_global) {
- std::swap(this->value, temp);
- } else {
- std::swap(custom, temp);
- }
- }
-
- /**
- * Assigns the current setting value depending on the global state.
- *
- * @param val The new value
- *
- * @returns A reference to the current setting value
- */
- const Type& operator=(const Type& val) override {
- Type temp{ranged ? std::clamp(val, this->minimum, this->maximum) : val};
- if (use_global) {
- std::swap(this->value, temp);
- return this->value;
- }
- std::swap(custom, temp);
- return custom;
- }
-
- /**
- * Returns the current setting value depending on the global state.
- *
- * @returns A reference to the current setting value
- */
- virtual explicit operator const Type&() const override {
- if (use_global) {
- return this->value;
- }
- return custom;
- }
-
-protected:
- bool use_global{true}; ///< The setting's global state
- Type custom{}; ///< The custom value of the setting
-};
+#ifndef CANNOT_EXPLICITLY_INSTANTIATE
+// Instantiate the classes elsewhere (settings.cpp) to reduce compiler/linker work
+#define SETTING(TYPE, RANGED) extern template class Setting<TYPE, RANGED>
+#define SWITCHABLE(TYPE, RANGED) extern template class SwitchableSetting<TYPE, RANGED>
+
+SETTING(AudioEngine, false);
+SETTING(bool, false);
+SETTING(int, false);
+SETTING(s32, false);
+SETTING(std::string, false);
+SETTING(std::string, false);
+SETTING(u16, false);
+SWITCHABLE(AnisotropyMode, true);
+SWITCHABLE(AntiAliasing, false);
+SWITCHABLE(AspectRatio, true);
+SWITCHABLE(AstcDecodeMode, true);
+SWITCHABLE(AstcRecompression, true);
+SWITCHABLE(AudioMode, true);
+SWITCHABLE(CpuAccuracy, true);
+SWITCHABLE(FullscreenMode, true);
+SWITCHABLE(GpuAccuracy, true);
+SWITCHABLE(Language, true);
+SWITCHABLE(NvdecEmulation, false);
+SWITCHABLE(Region, true);
+SWITCHABLE(RendererBackend, true);
+SWITCHABLE(ScalingFilter, false);
+SWITCHABLE(ShaderBackend, true);
+SWITCHABLE(TimeZone, true);
+SETTING(VSyncMode, true);
+SWITCHABLE(bool, false);
+SWITCHABLE(int, false);
+SWITCHABLE(int, true);
+SWITCHABLE(s64, false);
+SWITCHABLE(u16, true);
+SWITCHABLE(u32, false);
+SWITCHABLE(u8, false);
+SWITCHABLE(u8, true);
+
+#undef SETTING
+#undef SWITCHABLE
+#endif
/**
* The InputSetting class allows for getting a reference to either the global or custom members.
@@ -391,208 +125,388 @@ struct TouchFromButtonMap {
};
struct Values {
+ Linkage linkage{};
+
// Audio
- Setting<std::string> sink_id{"auto", "output_engine"};
- Setting<std::string> audio_output_device_id{"auto", "output_device"};
- Setting<std::string> audio_input_device_id{"auto", "input_device"};
- Setting<bool> audio_muted{false, "audio_muted"};
- SwitchableSetting<u8, true> volume{100, 0, 200, "volume"};
- Setting<bool> dump_audio_commands{false, "dump_audio_commands"};
+ Setting<AudioEngine> sink_id{linkage, AudioEngine::Auto, "output_engine", Category::Audio,
+ Specialization::RuntimeList};
+ Setting<std::string> audio_output_device_id{linkage, "auto", "output_device", Category::Audio,
+ Specialization::RuntimeList};
+ Setting<std::string> audio_input_device_id{linkage, "auto", "input_device", Category::Audio,
+ Specialization::RuntimeList};
+ SwitchableSetting<AudioMode, true> sound_index{
+ linkage, AudioMode::Stereo, AudioMode::Mono, AudioMode::Surround,
+ "sound_index", Category::SystemAudio, Specialization::Default, true,
+ true};
+ SwitchableSetting<u8, true> volume{linkage,
+ 100,
+ 0,
+ 200,
+ "volume",
+ Category::Audio,
+ Specialization::Scalar | Specialization::Percentage,
+ true,
+ true};
+ Setting<bool, false> audio_muted{
+ linkage, false, "audio_muted", Category::Audio, Specialization::Default, false, true};
+ Setting<bool, false> dump_audio_commands{
+ linkage, false, "dump_audio_commands", Category::Audio, Specialization::Default, false};
// Core
- SwitchableSetting<bool> use_multi_core{true, "use_multi_core"};
- SwitchableSetting<bool> use_unsafe_extended_memory_layout{false,
- "use_unsafe_extended_memory_layout"};
+ SwitchableSetting<bool> use_multi_core{linkage, true, "use_multi_core", Category::Core};
+ SwitchableSetting<MemoryLayout, true> memory_layout_mode{linkage,
+ MemoryLayout::Memory_4Gb,
+ MemoryLayout::Memory_4Gb,
+ MemoryLayout::Memory_8Gb,
+ "memory_layout_mode",
+ Category::Core};
+ SwitchableSetting<bool> use_speed_limit{
+ linkage, true, "use_speed_limit", Category::Core, Specialization::Paired, false, true};
+ SwitchableSetting<u16, true> speed_limit{linkage,
+ 100,
+ 0,
+ 9999,
+ "speed_limit",
+ Category::Core,
+ Specialization::Countable | Specialization::Percentage,
+ true,
+ true,
+ &use_speed_limit};
// Cpu
- SwitchableSetting<CPUAccuracy, true> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto,
- CPUAccuracy::Paranoid, "cpu_accuracy"};
- // TODO: remove cpu_accuracy_first_time, migration setting added 8 July 2021
- Setting<bool> cpu_accuracy_first_time{true, "cpu_accuracy_first_time"};
- Setting<bool> cpu_debug_mode{false, "cpu_debug_mode"};
-
- Setting<bool> cpuopt_page_tables{true, "cpuopt_page_tables"};
- Setting<bool> cpuopt_block_linking{true, "cpuopt_block_linking"};
- Setting<bool> cpuopt_return_stack_buffer{true, "cpuopt_return_stack_buffer"};
- Setting<bool> cpuopt_fast_dispatcher{true, "cpuopt_fast_dispatcher"};
- Setting<bool> cpuopt_context_elimination{true, "cpuopt_context_elimination"};
- Setting<bool> cpuopt_const_prop{true, "cpuopt_const_prop"};
- Setting<bool> cpuopt_misc_ir{true, "cpuopt_misc_ir"};
- Setting<bool> cpuopt_reduce_misalign_checks{true, "cpuopt_reduce_misalign_checks"};
- Setting<bool> cpuopt_fastmem{true, "cpuopt_fastmem"};
- Setting<bool> cpuopt_fastmem_exclusives{true, "cpuopt_fastmem_exclusives"};
- Setting<bool> cpuopt_recompile_exclusives{true, "cpuopt_recompile_exclusives"};
- Setting<bool> cpuopt_ignore_memory_aborts{true, "cpuopt_ignore_memory_aborts"};
-
- SwitchableSetting<bool> cpuopt_unsafe_unfuse_fma{true, "cpuopt_unsafe_unfuse_fma"};
- SwitchableSetting<bool> cpuopt_unsafe_reduce_fp_error{true, "cpuopt_unsafe_reduce_fp_error"};
+ SwitchableSetting<CpuAccuracy, true> cpu_accuracy{linkage, CpuAccuracy::Auto,
+ CpuAccuracy::Auto, CpuAccuracy::Paranoid,
+ "cpu_accuracy", Category::Cpu};
+ Setting<bool> cpu_debug_mode{linkage, false, "cpu_debug_mode", Category::CpuDebug};
+
+ Setting<bool> cpuopt_page_tables{linkage, true, "cpuopt_page_tables", Category::CpuDebug};
+ Setting<bool> cpuopt_block_linking{linkage, true, "cpuopt_block_linking", Category::CpuDebug};
+ Setting<bool> cpuopt_return_stack_buffer{linkage, true, "cpuopt_return_stack_buffer",
+ Category::CpuDebug};
+ Setting<bool> cpuopt_fast_dispatcher{linkage, true, "cpuopt_fast_dispatcher",
+ Category::CpuDebug};
+ Setting<bool> cpuopt_context_elimination{linkage, true, "cpuopt_context_elimination",
+ Category::CpuDebug};
+ Setting<bool> cpuopt_const_prop{linkage, true, "cpuopt_const_prop", Category::CpuDebug};
+ Setting<bool> cpuopt_misc_ir{linkage, true, "cpuopt_misc_ir", Category::CpuDebug};
+ Setting<bool> cpuopt_reduce_misalign_checks{linkage, true, "cpuopt_reduce_misalign_checks",
+ Category::CpuDebug};
+ Setting<bool> cpuopt_fastmem{linkage, true, "cpuopt_fastmem", Category::CpuDebug};
+ Setting<bool> cpuopt_fastmem_exclusives{linkage, true, "cpuopt_fastmem_exclusives",
+ Category::CpuDebug};
+ Setting<bool> cpuopt_recompile_exclusives{linkage, true, "cpuopt_recompile_exclusives",
+ Category::CpuDebug};
+ Setting<bool> cpuopt_ignore_memory_aborts{linkage, true, "cpuopt_ignore_memory_aborts",
+ Category::CpuDebug};
+
+ SwitchableSetting<bool> cpuopt_unsafe_unfuse_fma{linkage, true, "cpuopt_unsafe_unfuse_fma",
+ Category::CpuUnsafe};
+ SwitchableSetting<bool> cpuopt_unsafe_reduce_fp_error{
+ linkage, true, "cpuopt_unsafe_reduce_fp_error", Category::CpuUnsafe};
SwitchableSetting<bool> cpuopt_unsafe_ignore_standard_fpcr{
- true, "cpuopt_unsafe_ignore_standard_fpcr"};
- SwitchableSetting<bool> cpuopt_unsafe_inaccurate_nan{true, "cpuopt_unsafe_inaccurate_nan"};
- SwitchableSetting<bool> cpuopt_unsafe_fastmem_check{true, "cpuopt_unsafe_fastmem_check"};
+ linkage, true, "cpuopt_unsafe_ignore_standard_fpcr", Category::CpuUnsafe};
+ SwitchableSetting<bool> cpuopt_unsafe_inaccurate_nan{
+ linkage, true, "cpuopt_unsafe_inaccurate_nan", Category::CpuUnsafe};
+ SwitchableSetting<bool> cpuopt_unsafe_fastmem_check{
+ linkage, true, "cpuopt_unsafe_fastmem_check", Category::CpuUnsafe};
SwitchableSetting<bool> cpuopt_unsafe_ignore_global_monitor{
- true, "cpuopt_unsafe_ignore_global_monitor"};
+ linkage, true, "cpuopt_unsafe_ignore_global_monitor", Category::CpuUnsafe};
// Renderer
SwitchableSetting<RendererBackend, true> renderer_backend{
- RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Null, "backend"};
- SwitchableSetting<bool> async_presentation{false, "async_presentation"};
- SwitchableSetting<bool> renderer_force_max_clock{false, "force_max_clock"};
- Setting<bool> renderer_debug{false, "debug"};
- Setting<bool> renderer_shader_feedback{false, "shader_feedback"};
- Setting<bool> enable_nsight_aftermath{false, "nsight_aftermath"};
- Setting<bool> disable_shader_loop_safety_checks{false, "disable_shader_loop_safety_checks"};
- SwitchableSetting<int> vulkan_device{0, "vulkan_device"};
-
- ResolutionScalingInfo resolution_info{};
- SwitchableSetting<ResolutionSetup> resolution_setup{ResolutionSetup::Res1X, "resolution_setup"};
- SwitchableSetting<ScalingFilter> scaling_filter{ScalingFilter::Bilinear, "scaling_filter"};
- SwitchableSetting<int, true> fsr_sharpening_slider{25, 0, 200, "fsr_sharpening_slider"};
- SwitchableSetting<AntiAliasing> anti_aliasing{AntiAliasing::None, "anti_aliasing"};
+ linkage, RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Null,
+ "backend", Category::Renderer};
+ SwitchableSetting<ShaderBackend, true> shader_backend{
+ linkage, ShaderBackend::Glsl, ShaderBackend::Glsl, ShaderBackend::SpirV,
+ "shader_backend", Category::Renderer, Specialization::RuntimeList};
+ SwitchableSetting<int> vulkan_device{linkage, 0, "vulkan_device", Category::Renderer,
+ Specialization::RuntimeList};
+
+ SwitchableSetting<bool> use_disk_shader_cache{linkage, true, "use_disk_shader_cache",
+ Category::Renderer};
+ SwitchableSetting<bool> use_asynchronous_gpu_emulation{
+ linkage, true, "use_asynchronous_gpu_emulation", Category::Renderer};
+ SwitchableSetting<AstcDecodeMode, true> accelerate_astc{linkage,
+ AstcDecodeMode::Gpu,
+ AstcDecodeMode::Cpu,
+ AstcDecodeMode::CpuAsynchronous,
+ "accelerate_astc",
+ Category::Renderer};
+ Setting<VSyncMode, true> vsync_mode{
+ linkage, VSyncMode::Fifo, VSyncMode::Immediate, VSyncMode::FifoRelaxed,
+ "use_vsync", Category::Renderer, Specialization::RuntimeList, true,
+ true};
+ SwitchableSetting<NvdecEmulation> nvdec_emulation{linkage, NvdecEmulation::Gpu,
+ "nvdec_emulation", Category::Renderer};
// *nix platforms may have issues with the borderless windowed fullscreen mode.
// Default to exclusive fullscreen on these platforms for now.
- SwitchableSetting<FullscreenMode, true> fullscreen_mode{
+ SwitchableSetting<FullscreenMode, true> fullscreen_mode{linkage,
#ifdef _WIN32
- FullscreenMode::Borderless,
+ FullscreenMode::Borderless,
#else
- FullscreenMode::Exclusive,
+ FullscreenMode::Exclusive,
#endif
- FullscreenMode::Borderless, FullscreenMode::Exclusive, "fullscreen_mode"};
- SwitchableSetting<int, true> aspect_ratio{0, 0, 4, "aspect_ratio"};
- SwitchableSetting<int, true> max_anisotropy{0, 0, 5, "max_anisotropy"};
- SwitchableSetting<bool> use_speed_limit{true, "use_speed_limit"};
- SwitchableSetting<u16, true> speed_limit{100, 0, 9999, "speed_limit"};
- SwitchableSetting<bool> use_disk_shader_cache{true, "use_disk_shader_cache"};
- SwitchableSetting<GPUAccuracy, true> gpu_accuracy{GPUAccuracy::High, GPUAccuracy::Normal,
- GPUAccuracy::Extreme, "gpu_accuracy"};
- SwitchableSetting<bool> use_asynchronous_gpu_emulation{true, "use_asynchronous_gpu_emulation"};
- SwitchableSetting<NvdecEmulation> nvdec_emulation{NvdecEmulation::GPU, "nvdec_emulation"};
- SwitchableSetting<bool> accelerate_astc{true, "accelerate_astc"};
- SwitchableSetting<bool> async_astc{false, "async_astc"};
- Setting<VSyncMode, true> vsync_mode{VSyncMode::FIFO, VSyncMode::Immediate,
- VSyncMode::FIFORelaxed, "use_vsync"};
- SwitchableSetting<bool> use_reactive_flushing{true, "use_reactive_flushing"};
- SwitchableSetting<ShaderBackend, true> shader_backend{ShaderBackend::GLSL, ShaderBackend::GLSL,
- ShaderBackend::SPIRV, "shader_backend"};
- SwitchableSetting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"};
- SwitchableSetting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"};
- SwitchableSetting<bool> use_vulkan_driver_pipeline_cache{true,
- "use_vulkan_driver_pipeline_cache"};
- SwitchableSetting<bool> enable_compute_pipelines{false, "enable_compute_pipelines"};
- SwitchableSetting<AstcRecompression, true> astc_recompression{
- AstcRecompression::Uncompressed, AstcRecompression::Uncompressed, AstcRecompression::Bc3,
- "astc_recompression"};
- SwitchableSetting<bool> use_video_framerate{false, "use_video_framerate"};
- SwitchableSetting<bool> barrier_feedback_loops{true, "barrier_feedback_loops"};
-
- SwitchableSetting<u8> bg_red{0, "bg_red"};
- SwitchableSetting<u8> bg_green{0, "bg_green"};
- SwitchableSetting<u8> bg_blue{0, "bg_blue"};
+ FullscreenMode::Borderless,
+ FullscreenMode::Exclusive,
+ "fullscreen_mode",
+ Category::Renderer,
+ Specialization::Default,
+ true,
+ true};
+ SwitchableSetting<AspectRatio, true> aspect_ratio{linkage,
+ AspectRatio::R16_9,
+ AspectRatio::R16_9,
+ AspectRatio::Stretch,
+ "aspect_ratio",
+ Category::Renderer,
+ Specialization::Default,
+ true,
+ true};
+
+ ResolutionScalingInfo resolution_info{};
+ SwitchableSetting<ResolutionSetup> resolution_setup{linkage, ResolutionSetup::Res1X,
+ "resolution_setup", Category::Renderer};
+ SwitchableSetting<ScalingFilter> scaling_filter{linkage,
+ ScalingFilter::Bilinear,
+ "scaling_filter",
+ Category::Renderer,
+ Specialization::Default,
+ true,
+ true};
+ SwitchableSetting<AntiAliasing> anti_aliasing{linkage,
+ AntiAliasing::None,
+ "anti_aliasing",
+ Category::Renderer,
+ Specialization::Default,
+ true,
+ true};
+ SwitchableSetting<int, true> fsr_sharpening_slider{linkage,
+ 25,
+ 0,
+ 200,
+ "fsr_sharpening_slider",
+ Category::Renderer,
+ Specialization::Scalar |
+ Specialization::Percentage,
+ true,
+ true};
+
+ SwitchableSetting<u8, false> bg_red{
+ linkage, 0, "bg_red", Category::Renderer, Specialization::Default, true, true};
+ SwitchableSetting<u8, false> bg_green{
+ linkage, 0, "bg_green", Category::Renderer, Specialization::Default, true, true};
+ SwitchableSetting<u8, false> bg_blue{
+ linkage, 0, "bg_blue", Category::Renderer, Specialization::Default, true, true};
+
+ SwitchableSetting<GpuAccuracy, true> gpu_accuracy{linkage,
+ GpuAccuracy::High,
+ GpuAccuracy::Normal,
+ GpuAccuracy::Extreme,
+ "gpu_accuracy",
+ Category::RendererAdvanced,
+ Specialization::Default,
+ true,
+ true};
+ SwitchableSetting<AnisotropyMode, true> max_anisotropy{
+ linkage, AnisotropyMode::Automatic, AnisotropyMode::Automatic, AnisotropyMode::X16,
+ "max_anisotropy", Category::RendererAdvanced};
+ SwitchableSetting<AstcRecompression, true> astc_recompression{linkage,
+ AstcRecompression::Uncompressed,
+ AstcRecompression::Uncompressed,
+ AstcRecompression::Bc3,
+ "astc_recompression",
+ Category::RendererAdvanced};
+ SwitchableSetting<bool> async_presentation{linkage, false, "async_presentation",
+ Category::RendererAdvanced};
+ SwitchableSetting<bool> renderer_force_max_clock{linkage, false, "force_max_clock",
+ Category::RendererAdvanced};
+ SwitchableSetting<bool> use_reactive_flushing{linkage, true, "use_reactive_flushing",
+ Category::RendererAdvanced};
+ SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders",
+ Category::RendererAdvanced};
+ SwitchableSetting<bool> use_fast_gpu_time{
+ linkage, true, "use_fast_gpu_time", Category::RendererAdvanced, Specialization::Default,
+ true, true};
+ SwitchableSetting<bool> use_vulkan_driver_pipeline_cache{linkage,
+ true,
+ "use_vulkan_driver_pipeline_cache",
+ Category::RendererAdvanced,
+ Specialization::Default,
+ true,
+ true};
+ SwitchableSetting<bool> enable_compute_pipelines{linkage, false, "enable_compute_pipelines",
+ Category::RendererAdvanced};
+ SwitchableSetting<bool> use_video_framerate{linkage, false, "use_video_framerate",
+ Category::RendererAdvanced};
+ SwitchableSetting<bool> barrier_feedback_loops{linkage, true, "barrier_feedback_loops",
+ Category::RendererAdvanced};
+
+ Setting<bool> renderer_debug{linkage, false, "debug", Category::RendererDebug};
+ Setting<bool> renderer_shader_feedback{linkage, false, "shader_feedback",
+ Category::RendererDebug};
+ Setting<bool> enable_nsight_aftermath{linkage, false, "nsight_aftermath",
+ Category::RendererDebug};
+ Setting<bool> disable_shader_loop_safety_checks{
+ linkage, false, "disable_shader_loop_safety_checks", Category::RendererDebug};
// System
- SwitchableSetting<std::optional<u32>> rng_seed{std::optional<u32>(), "rng_seed"};
- Setting<std::string> device_name{"Yuzu", "device_name"};
+ SwitchableSetting<Language, true> language_index{linkage,
+ Language::EnglishAmerican,
+ Language::Japanese,
+ Language::PortugueseBrazilian,
+ "language_index",
+ Category::System};
+ SwitchableSetting<Region, true> region_index{linkage, Region::Usa, Region::Japan,
+ Region::Taiwan, "region_index", Category::System};
+ SwitchableSetting<TimeZone, true> time_zone_index{linkage, TimeZone::Auto,
+ TimeZone::Auto, TimeZone::Zulu,
+ "time_zone_index", Category::System};
// Measured in seconds since epoch
- std::optional<s64> custom_rtc;
+ SwitchableSetting<bool> custom_rtc_enabled{
+ linkage, false, "custom_rtc_enabled", Category::System, Specialization::Paired, true, true};
+ SwitchableSetting<s64> custom_rtc{
+ linkage, 0, "custom_rtc", Category::System, Specialization::Time,
+ true, true, &custom_rtc_enabled};
// Set on game boot, reset on stop. Seconds difference between current time and `custom_rtc`
s64 custom_rtc_differential;
+ SwitchableSetting<bool> rng_seed_enabled{
+ linkage, false, "rng_seed_enabled", Category::System, Specialization::Paired, true, true};
+ SwitchableSetting<u32> rng_seed{
+ linkage, 0, "rng_seed", Category::System, Specialization::Hex,
+ true, true, &rng_seed_enabled};
+ Setting<std::string> device_name{
+ linkage, "yuzu", "device_name", Category::System, Specialization::Default, true, true};
- Setting<s32> current_user{0, "current_user"};
- SwitchableSetting<s32, true> language_index{1, 0, 17, "language_index"};
- SwitchableSetting<s32, true> region_index{1, 0, 6, "region_index"};
- SwitchableSetting<s32, true> time_zone_index{0, 0, 45, "time_zone_index"};
- SwitchableSetting<s32, true> sound_index{1, 0, 2, "sound_index"};
+ Setting<s32> current_user{linkage, 0, "current_user", Category::System};
+
+ SwitchableSetting<bool> use_docked_mode{linkage, true, "use_docked_mode", Category::System};
// Controls
InputSetting<std::array<PlayerInput, 10>> players;
- SwitchableSetting<bool> use_docked_mode{true, "use_docked_mode"};
-
- Setting<bool> enable_raw_input{false, "enable_raw_input"};
- Setting<bool> controller_navigation{true, "controller_navigation"};
- Setting<bool> enable_joycon_driver{true, "enable_joycon_driver"};
- Setting<bool> enable_procon_driver{false, "enable_procon_driver"};
-
- SwitchableSetting<bool> vibration_enabled{true, "vibration_enabled"};
- SwitchableSetting<bool> enable_accurate_vibrations{false, "enable_accurate_vibrations"};
-
- SwitchableSetting<bool> motion_enabled{true, "motion_enabled"};
- Setting<std::string> udp_input_servers{"127.0.0.1:26760", "udp_input_servers"};
- Setting<bool> enable_udp_controller{false, "enable_udp_controller"};
-
- Setting<bool> pause_tas_on_load{true, "pause_tas_on_load"};
- Setting<bool> tas_enable{false, "tas_enable"};
- Setting<bool> tas_loop{false, "tas_loop"};
-
- Setting<bool> mouse_panning{false, "mouse_panning"};
- Setting<u8, true> mouse_panning_x_sensitivity{50, 1, 100, "mouse_panning_x_sensitivity"};
- Setting<u8, true> mouse_panning_y_sensitivity{50, 1, 100, "mouse_panning_y_sensitivity"};
- Setting<u8, true> mouse_panning_deadzone_counterweight{20, 0, 100,
- "mouse_panning_deadzone_counterweight"};
- Setting<u8, true> mouse_panning_decay_strength{18, 0, 100, "mouse_panning_decay_strength"};
- Setting<u8, true> mouse_panning_min_decay{6, 0, 100, "mouse_panning_min_decay"};
-
- Setting<bool> mouse_enabled{false, "mouse_enabled"};
- Setting<bool> emulate_analog_keyboard{false, "emulate_analog_keyboard"};
- Setting<bool> keyboard_enabled{false, "keyboard_enabled"};
-
- Setting<bool> debug_pad_enabled{false, "debug_pad_enabled"};
+ Setting<bool> enable_raw_input{
+ linkage, false, "enable_raw_input", Category::Controls, Specialization::Default,
+// Only read/write enable_raw_input on Windows platforms
+#ifdef _WIN32
+ true
+#else
+ false
+#endif
+ };
+ Setting<bool> controller_navigation{linkage, true, "controller_navigation", Category::Controls};
+ Setting<bool> enable_joycon_driver{linkage, true, "enable_joycon_driver", Category::Controls};
+ Setting<bool> enable_procon_driver{linkage, false, "enable_procon_driver", Category::Controls};
+
+ SwitchableSetting<bool> vibration_enabled{linkage, true, "vibration_enabled",
+ Category::Controls};
+ SwitchableSetting<bool> enable_accurate_vibrations{linkage, false, "enable_accurate_vibrations",
+ Category::Controls};
+
+ SwitchableSetting<bool> motion_enabled{linkage, true, "motion_enabled", Category::Controls};
+ Setting<std::string> udp_input_servers{linkage, "127.0.0.1:26760", "udp_input_servers",
+ Category::Controls};
+ Setting<bool> enable_udp_controller{linkage, false, "enable_udp_controller",
+ Category::Controls};
+
+ Setting<bool> pause_tas_on_load{linkage, true, "pause_tas_on_load", Category::Controls};
+ Setting<bool> tas_enable{linkage, false, "tas_enable", Category::Controls};
+ Setting<bool> tas_loop{linkage, false, "tas_loop", Category::Controls};
+
+ Setting<bool> mouse_panning{
+ linkage, false, "mouse_panning", Category::Controls, Specialization::Default, false};
+ Setting<u8, true> mouse_panning_sensitivity{
+ linkage, 50, 1, 100, "mouse_panning_sensitivity", Category::Controls};
+ Setting<bool> mouse_enabled{linkage, false, "mouse_enabled", Category::Controls};
+
+ Setting<u8, true> mouse_panning_x_sensitivity{
+ linkage, 50, 1, 100, "mouse_panning_x_sensitivity", Category::Controls};
+ Setting<u8, true> mouse_panning_y_sensitivity{
+ linkage, 50, 1, 100, "mouse_panning_y_sensitivity", Category::Controls};
+ Setting<u8, true> mouse_panning_deadzone_counterweight{
+ linkage, 20, 0, 100, "mouse_panning_deadzone_counterweight", Category::Controls};
+ Setting<u8, true> mouse_panning_decay_strength{
+ linkage, 18, 0, 100, "mouse_panning_decay_strength", Category::Controls};
+ Setting<u8, true> mouse_panning_min_decay{
+ linkage, 6, 0, 100, "mouse_panning_min_decay", Category::Controls};
+
+ Setting<bool> emulate_analog_keyboard{linkage, false, "emulate_analog_keyboard",
+ Category::Controls};
+ Setting<bool> keyboard_enabled{linkage, false, "keyboard_enabled", Category::Controls};
+
+ Setting<bool> debug_pad_enabled{linkage, false, "debug_pad_enabled", Category::Controls};
ButtonsRaw debug_pad_buttons;
AnalogsRaw debug_pad_analogs;
TouchscreenInput touchscreen;
- Setting<std::string> touch_device{"min_x:100,min_y:50,max_x:1800,max_y:850", "touch_device"};
- Setting<int> touch_from_button_map_index{0, "touch_from_button_map"};
+ Setting<std::string> touch_device{linkage, "min_x:100,min_y:50,max_x:1800,max_y:850",
+ "touch_device", Category::Controls};
+ Setting<int> touch_from_button_map_index{linkage, 0, "touch_from_button_map",
+ Category::Controls};
std::vector<TouchFromButtonMap> touch_from_button_maps;
- Setting<bool> enable_ring_controller{true, "enable_ring_controller"};
+ Setting<bool> enable_ring_controller{linkage, true, "enable_ring_controller",
+ Category::Controls};
RingconRaw ringcon_analogs;
- Setting<bool> enable_ir_sensor{false, "enable_ir_sensor"};
- Setting<std::string> ir_sensor_device{"auto", "ir_sensor_device"};
+ Setting<bool> enable_ir_sensor{linkage, false, "enable_ir_sensor", Category::Controls};
+ Setting<std::string> ir_sensor_device{linkage, "auto", "ir_sensor_device", Category::Controls};
- Setting<bool> random_amiibo_id{false, "random_amiibo_id"};
+ Setting<bool> random_amiibo_id{linkage, false, "random_amiibo_id", Category::Controls};
// Data Storage
- Setting<bool> use_virtual_sd{true, "use_virtual_sd"};
- Setting<bool> gamecard_inserted{false, "gamecard_inserted"};
- Setting<bool> gamecard_current_game{false, "gamecard_current_game"};
- Setting<std::string> gamecard_path{std::string(), "gamecard_path"};
+ Setting<bool> use_virtual_sd{linkage, true, "use_virtual_sd", Category::DataStorage};
+ Setting<bool> gamecard_inserted{linkage, false, "gamecard_inserted", Category::DataStorage};
+ Setting<bool> gamecard_current_game{linkage, false, "gamecard_current_game",
+ Category::DataStorage};
+ Setting<std::string> gamecard_path{linkage, std::string(), "gamecard_path",
+ Category::DataStorage};
// Debugging
bool record_frame_times;
- Setting<bool> use_gdbstub{false, "use_gdbstub"};
- Setting<u16> gdbstub_port{6543, "gdbstub_port"};
- Setting<std::string> program_args{std::string(), "program_args"};
- Setting<bool> dump_exefs{false, "dump_exefs"};
- Setting<bool> dump_nso{false, "dump_nso"};
- Setting<bool> dump_shaders{false, "dump_shaders"};
- Setting<bool> dump_macros{false, "dump_macros"};
- Setting<bool> enable_fs_access_log{false, "enable_fs_access_log"};
- Setting<bool> reporting_services{false, "reporting_services"};
- Setting<bool> quest_flag{false, "quest_flag"};
- Setting<bool> disable_macro_jit{false, "disable_macro_jit"};
- Setting<bool> disable_macro_hle{false, "disable_macro_hle"};
- Setting<bool> extended_logging{false, "extended_logging"};
- Setting<bool> use_debug_asserts{false, "use_debug_asserts"};
- Setting<bool> use_auto_stub{false, "use_auto_stub"};
- Setting<bool> enable_all_controllers{false, "enable_all_controllers"};
- Setting<bool> create_crash_dumps{false, "create_crash_dumps"};
- Setting<bool> perform_vulkan_check{true, "perform_vulkan_check"};
+ Setting<bool> use_gdbstub{linkage, false, "use_gdbstub", Category::Debugging};
+ Setting<u16> gdbstub_port{linkage, 6543, "gdbstub_port", Category::Debugging};
+ Setting<std::string> program_args{linkage, std::string(), "program_args", Category::Debugging};
+ Setting<bool> dump_exefs{linkage, false, "dump_exefs", Category::Debugging};
+ Setting<bool> dump_nso{linkage, false, "dump_nso", Category::Debugging};
+ Setting<bool> dump_shaders{
+ linkage, false, "dump_shaders", Category::DebuggingGraphics, Specialization::Default,
+ false};
+ Setting<bool> dump_macros{
+ linkage, false, "dump_macros", Category::DebuggingGraphics, Specialization::Default, false};
+ Setting<bool> enable_fs_access_log{linkage, false, "enable_fs_access_log", Category::Debugging};
+ Setting<bool> reporting_services{
+ linkage, false, "reporting_services", Category::Debugging, Specialization::Default, false};
+ Setting<bool> quest_flag{linkage, false, "quest_flag", Category::Debugging};
+ Setting<bool> disable_macro_jit{linkage, false, "disable_macro_jit",
+ Category::DebuggingGraphics};
+ Setting<bool> disable_macro_hle{linkage, false, "disable_macro_hle",
+ Category::DebuggingGraphics};
+ Setting<bool> extended_logging{
+ linkage, false, "extended_logging", Category::Debugging, Specialization::Default, false};
+ Setting<bool> use_debug_asserts{linkage, false, "use_debug_asserts", Category::Debugging};
+ Setting<bool> use_auto_stub{
+ linkage, false, "use_auto_stub", Category::Debugging, Specialization::Default, false};
+ Setting<bool> enable_all_controllers{linkage, false, "enable_all_controllers",
+ Category::Debugging};
+ Setting<bool> create_crash_dumps{linkage, false, "create_crash_dumps", Category::Debugging};
+ Setting<bool> perform_vulkan_check{linkage, true, "perform_vulkan_check", Category::Debugging};
// Miscellaneous
- Setting<std::string> log_filter{"*:Info", "log_filter"};
- Setting<bool> use_dev_keys{false, "use_dev_keys"};
+ Setting<std::string> log_filter{linkage, "*:Info", "log_filter", Category::Miscellaneous};
+ Setting<bool> use_dev_keys{linkage, false, "use_dev_keys", Category::Miscellaneous};
// Network
- Setting<std::string> network_interface{std::string(), "network_interface"};
+ Setting<std::string> network_interface{linkage, std::string(), "network_interface",
+ Category::Network};
// WebService
- Setting<bool> enable_telemetry{true, "enable_telemetry"};
- Setting<std::string> web_api_url{"https://api.yuzu-emu.org", "web_api_url"};
- Setting<std::string> yuzu_username{std::string(), "yuzu_username"};
- Setting<std::string> yuzu_token{std::string(), "yuzu_token"};
+ Setting<bool> enable_telemetry{linkage, true, "enable_telemetry", Category::WebService};
+ Setting<std::string> web_api_url{linkage, "https://api.yuzu-emu.org", "web_api_url",
+ Category::WebService};
+ Setting<std::string> yuzu_username{linkage, std::string(), "yuzu_username",
+ Category::WebService};
+ Setting<std::string> yuzu_token{linkage, std::string(), "yuzu_token", Category::WebService};
// Add-Ons
std::map<u64, std::vector<std::string>> disabled_addons;
@@ -600,9 +514,6 @@ struct Values {
extern Values values;
-bool IsConfiguringGlobal();
-void SetConfiguringGlobal(bool is_global);
-
bool IsGPULevelExtreme();
bool IsGPULevelHigh();
@@ -610,7 +521,7 @@ bool IsFastmemEnabled();
float Volume();
-std::string GetTimeZoneString();
+std::string GetTimeZoneString(TimeZone time_zone);
void LogSettings();
@@ -619,4 +530,7 @@ void UpdateRescalingInfo();
// Restore the global state of all applicable settings in the Values struct
void RestoreGlobalState(bool is_powered_on);
+bool IsConfiguringGlobal();
+void SetConfiguringGlobal(bool is_global);
+
} // namespace Settings
diff --git a/src/common/settings_common.cpp b/src/common/settings_common.cpp
new file mode 100644
index 000000000..dedf5ef90
--- /dev/null
+++ b/src/common/settings_common.cpp
@@ -0,0 +1,58 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <string>
+#include "common/settings_common.h"
+
+namespace Settings {
+
+BasicSetting::BasicSetting(Linkage& linkage, const std::string& name, enum Category category_,
+ bool save_, bool runtime_modifiable_, u32 specialization_,
+ BasicSetting* other_setting_)
+ : label{name}, category{category_}, id{linkage.count}, save{save_},
+ runtime_modifiable{runtime_modifiable_}, specialization{specialization_},
+ other_setting{other_setting_} {
+ linkage.by_category[category].push_back(this);
+ linkage.count++;
+}
+
+BasicSetting::~BasicSetting() = default;
+
+std::string BasicSetting::ToStringGlobal() const {
+ return this->ToString();
+}
+
+bool BasicSetting::UsingGlobal() const {
+ return true;
+}
+
+void BasicSetting::SetGlobal(bool global) {}
+
+bool BasicSetting::Save() const {
+ return save;
+}
+
+bool BasicSetting::RuntimeModfiable() const {
+ return runtime_modifiable;
+}
+
+Category BasicSetting::GetCategory() const {
+ return category;
+}
+
+u32 BasicSetting::Specialization() const {
+ return specialization;
+}
+
+BasicSetting* BasicSetting::PairedSetting() const {
+ return other_setting;
+}
+
+const std::string& BasicSetting::GetLabel() const {
+ return label;
+}
+
+Linkage::Linkage(u32 initial_count) : count{initial_count} {}
+Linkage::~Linkage() = default;
+
+} // namespace Settings
diff --git a/src/common/settings_common.h b/src/common/settings_common.h
new file mode 100644
index 000000000..2efb329b0
--- /dev/null
+++ b/src/common/settings_common.h
@@ -0,0 +1,256 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <functional>
+#include <map>
+#include <string>
+#include <typeindex>
+#include "common/common_types.h"
+
+namespace Settings {
+
+enum class Category : u32 {
+ Audio,
+ Core,
+ Cpu,
+ CpuDebug,
+ CpuUnsafe,
+ Renderer,
+ RendererAdvanced,
+ RendererDebug,
+ System,
+ SystemAudio,
+ DataStorage,
+ Debugging,
+ DebuggingGraphics,
+ Miscellaneous,
+ Network,
+ WebService,
+ AddOns,
+ Controls,
+ Ui,
+ UiGeneral,
+ UiLayout,
+ UiGameList,
+ Screenshots,
+ Shortcuts,
+ Multiplayer,
+ Services,
+ Paths,
+ MaxEnum,
+};
+
+constexpr u8 SpecializationTypeMask = 0xf;
+constexpr u8 SpecializationAttributeMask = 0xf0;
+constexpr u8 SpecializationAttributeOffset = 4;
+
+// Scalar and countable could have better names
+enum Specialization : u8 {
+ Default = 0,
+ Time = 1, // Duration or specific moment in time
+ Hex = 2, // Hexadecimal number
+ List = 3, // Setting has specific members
+ RuntimeList = 4, // Members of the list are determined during runtime
+ Scalar = 5, // Values are continuous
+ Countable = 6, // Can be stepped through
+ Paired = 7, // Another setting is associated with this setting
+
+ Percentage = (1 << SpecializationAttributeOffset), // Should be represented as a percentage
+};
+
+class BasicSetting;
+
+class Linkage {
+public:
+ explicit Linkage(u32 initial_count = 0);
+ ~Linkage();
+ std::map<Category, std::vector<BasicSetting*>> by_category{};
+ std::vector<std::function<void()>> restore_functions{};
+ u32 count;
+};
+
+/**
+ * BasicSetting is an abstract class that only keeps track of metadata. The string methods are
+ * available to get data values out.
+ */
+class BasicSetting {
+protected:
+ explicit BasicSetting(Linkage& linkage, const std::string& name, Category category_, bool save_,
+ bool runtime_modifiable_, u32 specialization,
+ BasicSetting* other_setting);
+
+public:
+ virtual ~BasicSetting();
+
+ /*
+ * Data retrieval
+ */
+
+ /**
+ * Returns a string representation of the internal data. If the Setting is Switchable, it
+ * respects the internal global state: it is based on GetValue().
+ *
+ * @returns A string representation of the internal data.
+ */
+ [[nodiscard]] virtual std::string ToString() const = 0;
+
+ /**
+ * Returns a string representation of the global version of internal data. If the Setting is
+ * not Switchable, it behaves like ToString.
+ *
+ * @returns A string representation of the global version of internal data.
+ */
+ [[nodiscard]] virtual std::string ToStringGlobal() const;
+
+ /**
+ * @returns A string representation of the Setting's default value.
+ */
+ [[nodiscard]] virtual std::string DefaultToString() const = 0;
+
+ /**
+ * Returns a string representation of the minimum value of the setting. If the Setting is not
+ * ranged, the string represents the default initialization of the data type.
+ *
+ * @returns A string representation of the minimum value of the setting.
+ */
+ [[nodiscard]] virtual std::string MinVal() const = 0;
+
+ /**
+ * Returns a string representation of the maximum value of the setting. If the Setting is not
+ * ranged, the string represents the default initialization of the data type.
+ *
+ * @returns A string representation of the maximum value of the setting.
+ */
+ [[nodiscard]] virtual std::string MaxVal() const = 0;
+
+ /**
+ * Takes a string input, converts it to the internal data type if necessary, and then runs
+ * SetValue with it.
+ *
+ * @param load String of the input data.
+ */
+ virtual void LoadString(const std::string& load) = 0;
+
+ /**
+ * Returns a string representation of the data. If the data is an enum, it returns a string of
+ * the enum value. If the internal data type is not an enum, this is equivalent to ToString.
+ *
+ * e.g. renderer_backend.Canonicalize() == "OpenGL"
+ *
+ * @returns Canonicalized string representation of the internal data
+ */
+ [[nodiscard]] virtual std::string Canonicalize() const = 0;
+
+ /*
+ * Metadata
+ */
+
+ /**
+ * @returns A unique identifier for the Setting's internal data type.
+ */
+ [[nodiscard]] virtual std::type_index TypeId() const = 0;
+
+ /**
+ * Returns true if the Setting's internal data type is an enum.
+ *
+ * @returns True if the Setting's internal data type is an enum
+ */
+ [[nodiscard]] virtual constexpr bool IsEnum() const = 0;
+
+ /**
+ * Returns true if the current setting is Switchable.
+ *
+ * @returns If the setting is a SwitchableSetting
+ */
+ [[nodiscard]] virtual constexpr bool Switchable() const {
+ return false;
+ }
+
+ /**
+ * Returns true to suggest that a frontend can read or write the setting to a configuration
+ * file.
+ *
+ * @returns The save preference
+ */
+ [[nodiscard]] bool Save() const;
+
+ /**
+ * @returns true if the current setting can be changed while the guest is running.
+ */
+ [[nodiscard]] bool RuntimeModfiable() const;
+
+ /**
+ * @returns A unique number corresponding to the setting.
+ */
+ [[nodiscard]] constexpr u32 Id() const {
+ return id;
+ }
+
+ /**
+ * Returns the setting's category AKA INI group.
+ *
+ * @returns The setting's category
+ */
+ [[nodiscard]] Category GetCategory() const;
+
+ /**
+ * @returns Extra metadata for data representation in frontend implementations.
+ */
+ [[nodiscard]] u32 Specialization() const;
+
+ /**
+ * @returns Another BasicSetting if one is paired, or nullptr otherwise.
+ */
+ [[nodiscard]] BasicSetting* PairedSetting() const;
+
+ /**
+ * Returns the label this setting was created with.
+ *
+ * @returns A reference to the label
+ */
+ [[nodiscard]] const std::string& GetLabel() const;
+
+ /**
+ * @returns If the Setting checks input values for valid ranges.
+ */
+ [[nodiscard]] virtual constexpr bool Ranged() const = 0;
+
+ /**
+ * @returns The index of the enum if the underlying setting type is an enum, else max of u32.
+ */
+ [[nodiscard]] virtual constexpr u32 EnumIndex() const = 0;
+
+ /*
+ * Switchable settings
+ */
+
+ /**
+ * Sets a setting's global state. True means use the normal setting, false to use a custom
+ * value. Has no effect if the Setting is not Switchable.
+ *
+ * @param global The desired state
+ */
+ virtual void SetGlobal(bool global);
+
+ /**
+ * Returns true if the setting is using the normal setting value. Always true if the setting is
+ * not Switchable.
+ *
+ * @returns The Setting's global state
+ */
+ [[nodiscard]] virtual bool UsingGlobal() const;
+
+private:
+ const std::string label; ///< The setting's label
+ const Category category; ///< The setting's category AKA INI group
+ const u32 id; ///< Unique integer for the setting
+ const bool save; ///< Suggests if the setting should be saved and read to a frontend config
+ const bool
+ runtime_modifiable; ///< Suggests if the setting can be modified while a guest is running
+ const u32 specialization; ///< Extra data to identify representation of a setting
+ BasicSetting* const other_setting; ///< A paired setting
+};
+
+} // namespace Settings
diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h
new file mode 100644
index 000000000..a1a29ebf6
--- /dev/null
+++ b/src/common/settings_enums.h
@@ -0,0 +1,214 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <string>
+#include <utility>
+#include <vector>
+#include "common/common_types.h"
+
+namespace Settings {
+
+template <typename T>
+struct EnumMetadata {
+ static constexpr std::vector<std::pair<std::string, T>> Canonicalizations();
+ static constexpr u32 Index();
+};
+
+#define PAIR_45(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_46(N, __VA_ARGS__))
+#define PAIR_44(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_45(N, __VA_ARGS__))
+#define PAIR_43(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_44(N, __VA_ARGS__))
+#define PAIR_42(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_43(N, __VA_ARGS__))
+#define PAIR_41(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_42(N, __VA_ARGS__))
+#define PAIR_40(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_41(N, __VA_ARGS__))
+#define PAIR_39(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_40(N, __VA_ARGS__))
+#define PAIR_38(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_39(N, __VA_ARGS__))
+#define PAIR_37(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_38(N, __VA_ARGS__))
+#define PAIR_36(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_37(N, __VA_ARGS__))
+#define PAIR_35(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_36(N, __VA_ARGS__))
+#define PAIR_34(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_35(N, __VA_ARGS__))
+#define PAIR_33(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_34(N, __VA_ARGS__))
+#define PAIR_32(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_33(N, __VA_ARGS__))
+#define PAIR_31(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_32(N, __VA_ARGS__))
+#define PAIR_30(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_31(N, __VA_ARGS__))
+#define PAIR_29(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_30(N, __VA_ARGS__))
+#define PAIR_28(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_29(N, __VA_ARGS__))
+#define PAIR_27(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_28(N, __VA_ARGS__))
+#define PAIR_26(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_27(N, __VA_ARGS__))
+#define PAIR_25(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_26(N, __VA_ARGS__))
+#define PAIR_24(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_25(N, __VA_ARGS__))
+#define PAIR_23(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_24(N, __VA_ARGS__))
+#define PAIR_22(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_23(N, __VA_ARGS__))
+#define PAIR_21(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_22(N, __VA_ARGS__))
+#define PAIR_20(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_21(N, __VA_ARGS__))
+#define PAIR_19(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_20(N, __VA_ARGS__))
+#define PAIR_18(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_19(N, __VA_ARGS__))
+#define PAIR_17(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_18(N, __VA_ARGS__))
+#define PAIR_16(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_17(N, __VA_ARGS__))
+#define PAIR_15(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_16(N, __VA_ARGS__))
+#define PAIR_14(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_15(N, __VA_ARGS__))
+#define PAIR_13(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_14(N, __VA_ARGS__))
+#define PAIR_12(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_13(N, __VA_ARGS__))
+#define PAIR_11(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_12(N, __VA_ARGS__))
+#define PAIR_10(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_11(N, __VA_ARGS__))
+#define PAIR_9(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_10(N, __VA_ARGS__))
+#define PAIR_8(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_9(N, __VA_ARGS__))
+#define PAIR_7(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_8(N, __VA_ARGS__))
+#define PAIR_6(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_7(N, __VA_ARGS__))
+#define PAIR_5(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_6(N, __VA_ARGS__))
+#define PAIR_4(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_5(N, __VA_ARGS__))
+#define PAIR_3(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_4(N, __VA_ARGS__))
+#define PAIR_2(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_3(N, __VA_ARGS__))
+#define PAIR_1(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_2(N, __VA_ARGS__))
+#define PAIR(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_1(N, __VA_ARGS__))
+
+#define ENUM(NAME, ...) \
+ enum class NAME : u32 { __VA_ARGS__ }; \
+ template <> \
+ constexpr std::vector<std::pair<std::string, NAME>> EnumMetadata<NAME>::Canonicalizations() { \
+ return {PAIR(NAME, __VA_ARGS__)}; \
+ } \
+ template <> \
+ constexpr u32 EnumMetadata<NAME>::Index() { \
+ return __COUNTER__; \
+ }
+
+// AudioEngine must be specified discretely due to having existing but slightly different
+// canonicalizations
+// TODO (lat9nq): Remove explicit definition of AudioEngine/sink_id
+enum class AudioEngine : u32 {
+ Auto,
+ Cubeb,
+ Sdl2,
+ Null,
+};
+
+template <>
+constexpr std::vector<std::pair<std::string, AudioEngine>>
+EnumMetadata<AudioEngine>::Canonicalizations() {
+ return {
+ {"auto", AudioEngine::Auto},
+ {"cubeb", AudioEngine::Cubeb},
+ {"sdl2", AudioEngine::Sdl2},
+ {"null", AudioEngine::Null},
+ };
+}
+
+template <>
+constexpr u32 EnumMetadata<AudioEngine>::Index() {
+ // This is just a sufficiently large number that is more than the number of other enums declared
+ // here
+ return 100;
+}
+
+ENUM(AudioMode, Mono, Stereo, Surround);
+
+ENUM(Language, Japanese, EnglishAmerican, French, German, Italian, Spanish, Chinese, Korean, Dutch,
+ Portuguese, Russian, Taiwanese, EnglishBritish, FrenchCanadian, SpanishLatin,
+ ChineseSimplified, ChineseTraditional, PortugueseBrazilian);
+
+ENUM(Region, Japan, Usa, Europe, Australia, China, Korea, Taiwan);
+
+ENUM(TimeZone, Auto, Default, Cet, Cst6Cdt, Cuba, Eet, Egypt, Eire, Est, Est5Edt, Gb, GbEire, Gmt,
+ GmtPlusZero, GmtMinusZero, GmtZero, Greenwich, Hongkong, Hst, Iceland, Iran, Israel, Jamaica,
+ Japan, Kwajalein, Libya, Met, Mst, Mst7Mdt, Navajo, Nz, NzChat, Poland, Portugal, Prc, Pst8Pdt,
+ Roc, Rok, Singapore, Turkey, Uct, Universal, Utc, WSu, Wet, Zulu);
+
+ENUM(AnisotropyMode, Automatic, Default, X2, X4, X8, X16);
+
+ENUM(AstcDecodeMode, Cpu, Gpu, CpuAsynchronous);
+
+ENUM(AstcRecompression, Uncompressed, Bc1, Bc3);
+
+ENUM(VSyncMode, Immediate, Mailbox, Fifo, FifoRelaxed);
+
+ENUM(RendererBackend, OpenGL, Vulkan, Null);
+
+ENUM(ShaderBackend, Glsl, Glasm, SpirV);
+
+ENUM(GpuAccuracy, Normal, High, Extreme);
+
+ENUM(CpuAccuracy, Auto, Accurate, Unsafe, Paranoid);
+
+ENUM(MemoryLayout, Memory_4Gb, Memory_6Gb, Memory_8Gb);
+
+ENUM(FullscreenMode, Borderless, Exclusive);
+
+ENUM(NvdecEmulation, Off, Cpu, Gpu);
+
+ENUM(ResolutionSetup, Res1_2X, Res3_4X, Res1X, Res3_2X, Res2X, Res3X, Res4X, Res5X, Res6X, Res7X,
+ Res8X);
+
+ENUM(ScalingFilter, NearestNeighbor, Bilinear, Bicubic, Gaussian, ScaleForce, Fsr, MaxEnum);
+
+ENUM(AntiAliasing, None, Fxaa, Smaa, MaxEnum);
+
+ENUM(AspectRatio, R16_9, R4_3, R21_9, R16_10, Stretch);
+
+template <typename Type>
+constexpr std::string CanonicalizeEnum(Type id) {
+ const auto group = EnumMetadata<Type>::Canonicalizations();
+ for (auto& [name, value] : group) {
+ if (value == id) {
+ return name;
+ }
+ }
+ return "unknown";
+}
+
+template <typename Type>
+constexpr Type ToEnum(const std::string& canonicalization) {
+ const auto group = EnumMetadata<Type>::Canonicalizations();
+ for (auto& [name, value] : group) {
+ if (name == canonicalization) {
+ return value;
+ }
+ }
+ return {};
+}
+} // namespace Settings
+
+#undef ENUM
+#undef PAIR
+#undef PAIR_1
+#undef PAIR_2
+#undef PAIR_3
+#undef PAIR_4
+#undef PAIR_5
+#undef PAIR_6
+#undef PAIR_7
+#undef PAIR_8
+#undef PAIR_9
+#undef PAIR_10
+#undef PAIR_12
+#undef PAIR_13
+#undef PAIR_14
+#undef PAIR_15
+#undef PAIR_16
+#undef PAIR_17
+#undef PAIR_18
+#undef PAIR_19
+#undef PAIR_20
+#undef PAIR_22
+#undef PAIR_23
+#undef PAIR_24
+#undef PAIR_25
+#undef PAIR_26
+#undef PAIR_27
+#undef PAIR_28
+#undef PAIR_29
+#undef PAIR_30
+#undef PAIR_32
+#undef PAIR_33
+#undef PAIR_34
+#undef PAIR_35
+#undef PAIR_36
+#undef PAIR_37
+#undef PAIR_38
+#undef PAIR_39
+#undef PAIR_40
+#undef PAIR_42
+#undef PAIR_43
+#undef PAIR_44
+#undef PAIR_45
diff --git a/src/common/settings_setting.h b/src/common/settings_setting.h
new file mode 100644
index 000000000..a8beb06e9
--- /dev/null
+++ b/src/common/settings_setting.h
@@ -0,0 +1,394 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <limits>
+#include <map>
+#include <optional>
+#include <stdexcept>
+#include <string>
+#include <typeindex>
+#include <typeinfo>
+#include "common/common_types.h"
+#include "common/settings_common.h"
+#include "common/settings_enums.h"
+
+namespace Settings {
+
+/** The Setting class is a simple resource manager. It defines a label and default value
+ * alongside the actual value of the setting for simpler and less-error prone use with frontend
+ * configurations. Specifying a default value and label is required. A minimum and maximum range
+ * can be specified for sanitization.
+ */
+template <typename Type, bool ranged = false>
+class Setting : public BasicSetting {
+protected:
+ Setting() = default;
+
+public:
+ /**
+ * Sets a default value, label, and setting value.
+ *
+ * @param linkage Setting registry
+ * @param default_val Initial value of the setting, and default value of the setting
+ * @param name Label for the setting
+ * @param category_ Category of the setting AKA INI group
+ * @param specialization_ Suggestion for how frontend implementations represent this in a config
+ * @param save_ Suggests that this should or should not be saved to a frontend config file
+ * @param runtime_modifiable_ Suggests whether this is modifiable while a guest is loaded
+ * @param other_setting_ A second Setting to associate to this one in metadata
+ */
+ explicit Setting(Linkage& linkage, const Type& default_val, const std::string& name,
+ Category category_, u32 specialization_ = Specialization::Default,
+ bool save_ = true, bool runtime_modifiable_ = false,
+ BasicSetting* other_setting_ = nullptr)
+ requires(!ranged)
+ : BasicSetting(linkage, name, category_, save_, runtime_modifiable_, specialization_,
+ other_setting_),
+ value{default_val}, default_value{default_val} {}
+ virtual ~Setting() = default;
+
+ /**
+ * Sets a default value, minimum value, maximum value, and label.
+ *
+ * @param linkage Setting registry
+ * @param default_val Initial value of the setting, and default value of the setting
+ * @param min_val Sets the minimum allowed value of the setting
+ * @param max_val Sets the maximum allowed value of the setting
+ * @param name Label for the setting
+ * @param category_ Category of the setting AKA INI group
+ * @param specialization_ Suggestion for how frontend implementations represent this in a config
+ * @param save_ Suggests that this should or should not be saved to a frontend config file
+ * @param runtime_modifiable_ Suggests whether this is modifiable while a guest is loaded
+ * @param other_setting_ A second Setting to associate to this one in metadata
+ */
+ explicit Setting(Linkage& linkage, const Type& default_val, const Type& min_val,
+ const Type& max_val, const std::string& name, Category category_,
+ u32 specialization_ = Specialization::Default, bool save_ = true,
+ bool runtime_modifiable_ = false, BasicSetting* other_setting_ = nullptr)
+ requires(ranged)
+ : BasicSetting(linkage, name, category_, save_, runtime_modifiable_, specialization_,
+ other_setting_),
+ value{default_val}, default_value{default_val}, maximum{max_val}, minimum{min_val} {}
+
+ /**
+ * Returns a reference to the setting's value.
+ *
+ * @returns A reference to the setting
+ */
+ [[nodiscard]] virtual const Type& GetValue() const {
+ return value;
+ }
+
+ /**
+ * Sets the setting to the given value.
+ *
+ * @param val The desired value
+ */
+ virtual void SetValue(const Type& val) {
+ Type temp{ranged ? std::clamp(val, minimum, maximum) : val};
+ std::swap(value, temp);
+ }
+
+ /**
+ * Returns the value that this setting was created with.
+ *
+ * @returns A reference to the default value
+ */
+ [[nodiscard]] const Type& GetDefault() const {
+ return default_value;
+ }
+
+ [[nodiscard]] constexpr bool IsEnum() const override {
+ return std::is_enum_v<Type>;
+ }
+
+protected:
+ [[nodiscard]] std::string ToString(const Type& value_) const {
+ if constexpr (std::is_same_v<Type, std::string>) {
+ return value_;
+ } else if constexpr (std::is_same_v<Type, std::optional<u32>>) {
+ return value_.has_value() ? std::to_string(*value_) : "none";
+ } else if constexpr (std::is_same_v<Type, bool>) {
+ return value_ ? "true" : "false";
+ } else if constexpr (std::is_same_v<Type, AudioEngine>) {
+ // Compatibility with old AudioEngine setting being a string
+ return CanonicalizeEnum(value_);
+ } else {
+ return std::to_string(static_cast<u64>(value_));
+ }
+ }
+
+public:
+ /**
+ * Converts the value of the setting to a std::string. Respects the global state if the setting
+ * has one.
+ *
+ * @returns The current setting as a std::string
+ */
+ [[nodiscard]] std::string ToString() const override {
+ return ToString(this->GetValue());
+ }
+
+ /**
+ * Returns the default value of the setting as a std::string.
+ *
+ * @returns The default value as a string.
+ */
+ [[nodiscard]] std::string DefaultToString() const override {
+ return ToString(default_value);
+ }
+
+ /**
+ * Assigns a value to the setting.
+ *
+ * @param val The desired setting value
+ *
+ * @returns A reference to the setting
+ */
+ virtual const Type& operator=(const Type& val) {
+ Type temp{ranged ? std::clamp(val, minimum, maximum) : val};
+ std::swap(value, temp);
+ return value;
+ }
+
+ /**
+ * Returns a reference to the setting.
+ *
+ * @returns A reference to the setting
+ */
+ explicit virtual operator const Type&() const {
+ return value;
+ }
+
+ /**
+ * Converts the given value to the Setting's type of value. Uses SetValue to enter the setting,
+ * thus respecting its constraints.
+ *
+ * @param input The desired value
+ */
+ void LoadString(const std::string& input) override final {
+ if (input.empty()) {
+ this->SetValue(this->GetDefault());
+ return;
+ }
+ try {
+ if constexpr (std::is_same_v<Type, std::string>) {
+ this->SetValue(input);
+ } else if constexpr (std::is_same_v<Type, std::optional<u32>>) {
+ this->SetValue(static_cast<u32>(std::stoul(input)));
+ } else if constexpr (std::is_same_v<Type, bool>) {
+ this->SetValue(input == "true");
+ } else if constexpr (std::is_same_v<Type, AudioEngine>) {
+ this->SetValue(ToEnum<Type>(input));
+ } else {
+ this->SetValue(static_cast<Type>(std::stoll(input)));
+ }
+ } catch (std::invalid_argument&) {
+ this->SetValue(this->GetDefault());
+ }
+ }
+
+ [[nodiscard]] std::string constexpr Canonicalize() const override final {
+ if constexpr (std::is_enum_v<Type>) {
+ return CanonicalizeEnum(this->GetValue());
+ } else {
+ return ToString(this->GetValue());
+ }
+ }
+
+ /**
+ * Gives us another way to identify the setting without having to go through a string.
+ *
+ * @returns the type_index of the setting's type
+ */
+ [[nodiscard]] std::type_index TypeId() const override final {
+ return std::type_index(typeid(Type));
+ }
+
+ [[nodiscard]] constexpr u32 EnumIndex() const override final {
+ if constexpr (std::is_enum_v<Type>) {
+ return EnumMetadata<Type>::Index();
+ } else {
+ return std::numeric_limits<u32>::max();
+ }
+ }
+
+ [[nodiscard]] std::string MinVal() const override final {
+ return this->ToString(minimum);
+ }
+ [[nodiscard]] std::string MaxVal() const override final {
+ return this->ToString(maximum);
+ }
+
+ [[nodiscard]] constexpr bool Ranged() const override {
+ return ranged;
+ }
+
+protected:
+ Type value{}; ///< The setting
+ const Type default_value{}; ///< The default value
+ const Type maximum{}; ///< Maximum allowed value of the setting
+ const Type minimum{}; ///< Minimum allowed value of the setting
+};
+
+/**
+ * The SwitchableSetting class is a slightly more complex version of the Setting class. This adds a
+ * custom setting to switch to when a guest application specifically requires it. The effect is that
+ * other components of the emulator can access the setting's intended value without any need for the
+ * component to ask whether the custom or global setting is needed at the moment.
+ *
+ * By default, the global setting is used.
+ */
+template <typename Type, bool ranged = false>
+class SwitchableSetting : virtual public Setting<Type, ranged> {
+public:
+ /**
+ * Sets a default value, label, and setting value.
+ *
+ * @param linkage Setting registry
+ * @param default_val Initial value of the setting, and default value of the setting
+ * @param name Label for the setting
+ * @param category_ Category of the setting AKA INI group
+ * @param specialization_ Suggestion for how frontend implementations represent this in a config
+ * @param save_ Suggests that this should or should not be saved to a frontend config file
+ * @param runtime_modifiable_ Suggests whether this is modifiable while a guest is loaded
+ * @param other_setting_ A second Setting to associate to this one in metadata
+ */
+ explicit SwitchableSetting(Linkage& linkage, const Type& default_val, const std::string& name,
+ Category category_, u32 specialization_ = Specialization::Default,
+ bool save_ = true, bool runtime_modifiable_ = false,
+ BasicSetting* other_setting_ = nullptr)
+ requires(!ranged)
+ : Setting<Type, false>{
+ linkage, default_val, name, category_, specialization_,
+ save_, runtime_modifiable_, other_setting_} {
+ linkage.restore_functions.emplace_back([this]() { this->SetGlobal(true); });
+ }
+ virtual ~SwitchableSetting() = default;
+
+ /**
+ * Sets a default value, minimum value, maximum value, and label.
+ *
+ * @param linkage Setting registry
+ * @param default_val Initial value of the setting, and default value of the setting
+ * @param min_val Sets the minimum allowed value of the setting
+ * @param max_val Sets the maximum allowed value of the setting
+ * @param name Label for the setting
+ * @param category_ Category of the setting AKA INI group
+ * @param specialization_ Suggestion for how frontend implementations represent this in a config
+ * @param save_ Suggests that this should or should not be saved to a frontend config file
+ * @param runtime_modifiable_ Suggests whether this is modifiable while a guest is loaded
+ * @param other_setting_ A second Setting to associate to this one in metadata
+ */
+ explicit SwitchableSetting(Linkage& linkage, const Type& default_val, const Type& min_val,
+ const Type& max_val, const std::string& name, Category category_,
+ u32 specialization_ = Specialization::Default, bool save_ = true,
+ bool runtime_modifiable_ = false,
+ BasicSetting* other_setting_ = nullptr)
+ requires(ranged)
+ : Setting<Type, true>{linkage, default_val, min_val,
+ max_val, name, category_,
+ specialization_, save_, runtime_modifiable_,
+ other_setting_} {
+ linkage.restore_functions.emplace_back([this]() { this->SetGlobal(true); });
+ }
+
+ /**
+ * Tells this setting to represent either the global or custom setting when other member
+ * functions are used.
+ *
+ * @param to_global Whether to use the global or custom setting.
+ */
+ void SetGlobal(bool to_global) override final {
+ use_global = to_global;
+ }
+
+ /**
+ * Returns whether this setting is using the global setting or not.
+ *
+ * @returns The global state
+ */
+ [[nodiscard]] bool UsingGlobal() const override final {
+ return use_global;
+ }
+
+ /**
+ * Returns either the global or custom setting depending on the values of this setting's global
+ * state or if the global value was specifically requested.
+ *
+ * @param need_global Request global value regardless of setting's state; defaults to false
+ *
+ * @returns The required value of the setting
+ */
+ [[nodiscard]] const Type& GetValue() const override final {
+ if (use_global) {
+ return this->value;
+ }
+ return custom;
+ }
+ [[nodiscard]] const Type& GetValue(bool need_global) const {
+ if (use_global || need_global) {
+ return this->value;
+ }
+ return custom;
+ }
+
+ /**
+ * Sets the current setting value depending on the global state.
+ *
+ * @param val The new value
+ */
+ void SetValue(const Type& val) override final {
+ Type temp{ranged ? std::clamp(val, this->minimum, this->maximum) : val};
+ if (use_global) {
+ std::swap(this->value, temp);
+ } else {
+ std::swap(custom, temp);
+ }
+ }
+
+ [[nodiscard]] constexpr bool Switchable() const override final {
+ return true;
+ }
+
+ [[nodiscard]] std::string ToStringGlobal() const override final {
+ return this->ToString(this->value);
+ }
+
+ /**
+ * Assigns the current setting value depending on the global state.
+ *
+ * @param val The new value
+ *
+ * @returns A reference to the current setting value
+ */
+ const Type& operator=(const Type& val) override final {
+ Type temp{ranged ? std::clamp(val, this->minimum, this->maximum) : val};
+ if (use_global) {
+ std::swap(this->value, temp);
+ return this->value;
+ }
+ std::swap(custom, temp);
+ return custom;
+ }
+
+ /**
+ * Returns the current setting value depending on the global state.
+ *
+ * @returns A reference to the current setting value
+ */
+ explicit operator const Type&() const override final {
+ if (use_global) {
+ return this->value;
+ }
+ return custom;
+ }
+
+protected:
+ bool use_global{true}; ///< The setting's global state
+ Type custom{}; ///< The custom value of the setting
+};
+
+} // namespace Settings