summaryrefslogtreecommitdiff
path: root/src/common/settings_setting.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/settings_setting.h')
-rw-r--r--src/common/settings_setting.h394
1 files changed, 394 insertions, 0 deletions
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 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <limits>
7#include <map>
8#include <optional>
9#include <stdexcept>
10#include <string>
11#include <typeindex>
12#include <typeinfo>
13#include "common/common_types.h"
14#include "common/settings_common.h"
15#include "common/settings_enums.h"
16
17namespace Settings {
18
19/** The Setting class is a simple resource manager. It defines a label and default value
20 * alongside the actual value of the setting for simpler and less-error prone use with frontend
21 * configurations. Specifying a default value and label is required. A minimum and maximum range
22 * can be specified for sanitization.
23 */
24template <typename Type, bool ranged = false>
25class Setting : public BasicSetting {
26protected:
27 Setting() = default;
28
29public:
30 /**
31 * Sets a default value, label, and setting value.
32 *
33 * @param linkage Setting registry
34 * @param default_val Initial value of the setting, and default value of the setting
35 * @param name Label for the setting
36 * @param category_ Category of the setting AKA INI group
37 * @param specialization_ Suggestion for how frontend implementations represent this in a config
38 * @param save_ Suggests that this should or should not be saved to a frontend config file
39 * @param runtime_modifiable_ Suggests whether this is modifiable while a guest is loaded
40 * @param other_setting_ A second Setting to associate to this one in metadata
41 */
42 explicit Setting(Linkage& linkage, const Type& default_val, const std::string& name,
43 Category category_, u32 specialization_ = Specialization::Default,
44 bool save_ = true, bool runtime_modifiable_ = false,
45 BasicSetting* other_setting_ = nullptr)
46 requires(!ranged)
47 : BasicSetting(linkage, name, category_, save_, runtime_modifiable_, specialization_,
48 other_setting_),
49 value{default_val}, default_value{default_val} {}
50 virtual ~Setting() = default;
51
52 /**
53 * Sets a default value, minimum value, maximum value, and label.
54 *
55 * @param linkage Setting registry
56 * @param default_val Initial value of the setting, and default value of the setting
57 * @param min_val Sets the minimum allowed value of the setting
58 * @param max_val Sets the maximum allowed value of the setting
59 * @param name Label for the setting
60 * @param category_ Category of the setting AKA INI group
61 * @param specialization_ Suggestion for how frontend implementations represent this in a config
62 * @param save_ Suggests that this should or should not be saved to a frontend config file
63 * @param runtime_modifiable_ Suggests whether this is modifiable while a guest is loaded
64 * @param other_setting_ A second Setting to associate to this one in metadata
65 */
66 explicit Setting(Linkage& linkage, const Type& default_val, const Type& min_val,
67 const Type& max_val, const std::string& name, Category category_,
68 u32 specialization_ = Specialization::Default, bool save_ = true,
69 bool runtime_modifiable_ = false, BasicSetting* other_setting_ = nullptr)
70 requires(ranged)
71 : BasicSetting(linkage, name, category_, save_, runtime_modifiable_, specialization_,
72 other_setting_),
73 value{default_val}, default_value{default_val}, maximum{max_val}, minimum{min_val} {}
74
75 /**
76 * Returns a reference to the setting's value.
77 *
78 * @returns A reference to the setting
79 */
80 [[nodiscard]] virtual const Type& GetValue() const {
81 return value;
82 }
83
84 /**
85 * Sets the setting to the given value.
86 *
87 * @param val The desired value
88 */
89 virtual void SetValue(const Type& val) {
90 Type temp{ranged ? std::clamp(val, minimum, maximum) : val};
91 std::swap(value, temp);
92 }
93
94 /**
95 * Returns the value that this setting was created with.
96 *
97 * @returns A reference to the default value
98 */
99 [[nodiscard]] const Type& GetDefault() const {
100 return default_value;
101 }
102
103 [[nodiscard]] constexpr bool IsEnum() const override {
104 return std::is_enum_v<Type>;
105 }
106
107protected:
108 [[nodiscard]] std::string ToString(const Type& value_) const {
109 if constexpr (std::is_same_v<Type, std::string>) {
110 return value_;
111 } else if constexpr (std::is_same_v<Type, std::optional<u32>>) {
112 return value_.has_value() ? std::to_string(*value_) : "none";
113 } else if constexpr (std::is_same_v<Type, bool>) {
114 return value_ ? "true" : "false";
115 } else if constexpr (std::is_same_v<Type, AudioEngine>) {
116 // Compatibility with old AudioEngine setting being a string
117 return CanonicalizeEnum(value_);
118 } else {
119 return std::to_string(static_cast<u64>(value_));
120 }
121 }
122
123public:
124 /**
125 * Converts the value of the setting to a std::string. Respects the global state if the setting
126 * has one.
127 *
128 * @returns The current setting as a std::string
129 */
130 [[nodiscard]] std::string ToString() const override {
131 return ToString(this->GetValue());
132 }
133
134 /**
135 * Returns the default value of the setting as a std::string.
136 *
137 * @returns The default value as a string.
138 */
139 [[nodiscard]] std::string DefaultToString() const override {
140 return ToString(default_value);
141 }
142
143 /**
144 * Assigns a value to the setting.
145 *
146 * @param val The desired setting value
147 *
148 * @returns A reference to the setting
149 */
150 virtual const Type& operator=(const Type& val) {
151 Type temp{ranged ? std::clamp(val, minimum, maximum) : val};
152 std::swap(value, temp);
153 return value;
154 }
155
156 /**
157 * Returns a reference to the setting.
158 *
159 * @returns A reference to the setting
160 */
161 explicit virtual operator const Type&() const {
162 return value;
163 }
164
165 /**
166 * Converts the given value to the Setting's type of value. Uses SetValue to enter the setting,
167 * thus respecting its constraints.
168 *
169 * @param input The desired value
170 */
171 void LoadString(const std::string& input) override final {
172 if (input.empty()) {
173 this->SetValue(this->GetDefault());
174 return;
175 }
176 try {
177 if constexpr (std::is_same_v<Type, std::string>) {
178 this->SetValue(input);
179 } else if constexpr (std::is_same_v<Type, std::optional<u32>>) {
180 this->SetValue(static_cast<u32>(std::stoul(input)));
181 } else if constexpr (std::is_same_v<Type, bool>) {
182 this->SetValue(input == "true");
183 } else if constexpr (std::is_same_v<Type, AudioEngine>) {
184 this->SetValue(ToEnum<Type>(input));
185 } else {
186 this->SetValue(static_cast<Type>(std::stoll(input)));
187 }
188 } catch (std::invalid_argument&) {
189 this->SetValue(this->GetDefault());
190 }
191 }
192
193 [[nodiscard]] std::string constexpr Canonicalize() const override final {
194 if constexpr (std::is_enum_v<Type>) {
195 return CanonicalizeEnum(this->GetValue());
196 } else {
197 return ToString(this->GetValue());
198 }
199 }
200
201 /**
202 * Gives us another way to identify the setting without having to go through a string.
203 *
204 * @returns the type_index of the setting's type
205 */
206 [[nodiscard]] std::type_index TypeId() const override final {
207 return std::type_index(typeid(Type));
208 }
209
210 [[nodiscard]] constexpr u32 EnumIndex() const override final {
211 if constexpr (std::is_enum_v<Type>) {
212 return EnumMetadata<Type>::Index();
213 } else {
214 return std::numeric_limits<u32>::max();
215 }
216 }
217
218 [[nodiscard]] std::string MinVal() const override final {
219 return this->ToString(minimum);
220 }
221 [[nodiscard]] std::string MaxVal() const override final {
222 return this->ToString(maximum);
223 }
224
225 [[nodiscard]] constexpr bool Ranged() const override {
226 return ranged;
227 }
228
229protected:
230 Type value{}; ///< The setting
231 const Type default_value{}; ///< The default value
232 const Type maximum{}; ///< Maximum allowed value of the setting
233 const Type minimum{}; ///< Minimum allowed value of the setting
234};
235
236/**
237 * The SwitchableSetting class is a slightly more complex version of the Setting class. This adds a
238 * custom setting to switch to when a guest application specifically requires it. The effect is that
239 * other components of the emulator can access the setting's intended value without any need for the
240 * component to ask whether the custom or global setting is needed at the moment.
241 *
242 * By default, the global setting is used.
243 */
244template <typename Type, bool ranged = false>
245class SwitchableSetting : virtual public Setting<Type, ranged> {
246public:
247 /**
248 * Sets a default value, label, and setting value.
249 *
250 * @param linkage Setting registry
251 * @param default_val Initial value of the setting, and default value of the setting
252 * @param name Label for the setting
253 * @param category_ Category of the setting AKA INI group
254 * @param specialization_ Suggestion for how frontend implementations represent this in a config
255 * @param save_ Suggests that this should or should not be saved to a frontend config file
256 * @param runtime_modifiable_ Suggests whether this is modifiable while a guest is loaded
257 * @param other_setting_ A second Setting to associate to this one in metadata
258 */
259 explicit SwitchableSetting(Linkage& linkage, const Type& default_val, const std::string& name,
260 Category category_, u32 specialization_ = Specialization::Default,
261 bool save_ = true, bool runtime_modifiable_ = false,
262 BasicSetting* other_setting_ = nullptr)
263 requires(!ranged)
264 : Setting<Type, false>{
265 linkage, default_val, name, category_, specialization_,
266 save_, runtime_modifiable_, other_setting_} {
267 linkage.restore_functions.emplace_back([this]() { this->SetGlobal(true); });
268 }
269 virtual ~SwitchableSetting() = default;
270
271 /**
272 * Sets a default value, minimum value, maximum value, and label.
273 *
274 * @param linkage Setting registry
275 * @param default_val Initial value of the setting, and default value of the setting
276 * @param min_val Sets the minimum allowed value of the setting
277 * @param max_val Sets the maximum allowed value of the setting
278 * @param name Label for the setting
279 * @param category_ Category of the setting AKA INI group
280 * @param specialization_ Suggestion for how frontend implementations represent this in a config
281 * @param save_ Suggests that this should or should not be saved to a frontend config file
282 * @param runtime_modifiable_ Suggests whether this is modifiable while a guest is loaded
283 * @param other_setting_ A second Setting to associate to this one in metadata
284 */
285 explicit SwitchableSetting(Linkage& linkage, const Type& default_val, const Type& min_val,
286 const Type& max_val, const std::string& name, Category category_,
287 u32 specialization_ = Specialization::Default, bool save_ = true,
288 bool runtime_modifiable_ = false,
289 BasicSetting* other_setting_ = nullptr)
290 requires(ranged)
291 : Setting<Type, true>{linkage, default_val, min_val,
292 max_val, name, category_,
293 specialization_, save_, runtime_modifiable_,
294 other_setting_} {
295 linkage.restore_functions.emplace_back([this]() { this->SetGlobal(true); });
296 }
297
298 /**
299 * Tells this setting to represent either the global or custom setting when other member
300 * functions are used.
301 *
302 * @param to_global Whether to use the global or custom setting.
303 */
304 void SetGlobal(bool to_global) override final {
305 use_global = to_global;
306 }
307
308 /**
309 * Returns whether this setting is using the global setting or not.
310 *
311 * @returns The global state
312 */
313 [[nodiscard]] bool UsingGlobal() const override final {
314 return use_global;
315 }
316
317 /**
318 * Returns either the global or custom setting depending on the values of this setting's global
319 * state or if the global value was specifically requested.
320 *
321 * @param need_global Request global value regardless of setting's state; defaults to false
322 *
323 * @returns The required value of the setting
324 */
325 [[nodiscard]] const Type& GetValue() const override final {
326 if (use_global) {
327 return this->value;
328 }
329 return custom;
330 }
331 [[nodiscard]] const Type& GetValue(bool need_global) const {
332 if (use_global || need_global) {
333 return this->value;
334 }
335 return custom;
336 }
337
338 /**
339 * Sets the current setting value depending on the global state.
340 *
341 * @param val The new value
342 */
343 void SetValue(const Type& val) override final {
344 Type temp{ranged ? std::clamp(val, this->minimum, this->maximum) : val};
345 if (use_global) {
346 std::swap(this->value, temp);
347 } else {
348 std::swap(custom, temp);
349 }
350 }
351
352 [[nodiscard]] constexpr bool Switchable() const override final {
353 return true;
354 }
355
356 [[nodiscard]] std::string ToStringGlobal() const override final {
357 return this->ToString(this->value);
358 }
359
360 /**
361 * Assigns the current setting value depending on the global state.
362 *
363 * @param val The new value
364 *
365 * @returns A reference to the current setting value
366 */
367 const Type& operator=(const Type& val) override final {
368 Type temp{ranged ? std::clamp(val, this->minimum, this->maximum) : val};
369 if (use_global) {
370 std::swap(this->value, temp);
371 return this->value;
372 }
373 std::swap(custom, temp);
374 return custom;
375 }
376
377 /**
378 * Returns the current setting value depending on the global state.
379 *
380 * @returns A reference to the current setting value
381 */
382 explicit operator const Type&() const override final {
383 if (use_global) {
384 return this->value;
385 }
386 return custom;
387 }
388
389protected:
390 bool use_global{true}; ///< The setting's global state
391 Type custom{}; ///< The custom value of the setting
392};
393
394} // namespace Settings