rpcsx/rpcs3/util/Config.h

1055 lines
20 KiB
C
Raw Permalink Normal View History

2020-12-25 03:18:36 +01:00
#pragma once
2016-02-01 22:55:43 +01:00
2020-12-12 13:01:29 +01:00
#include "util/types.hpp"
#include "util/StrUtil.h"
2020-03-07 10:29:23 +01:00
#include "util/logs.hpp"
#include "util/atomic.hpp"
#include "util/shared_ptr.hpp"
2025-03-17 17:31:41 +01:00
#include "nlohmann/json.hpp"
#include <algorithm>
2025-03-19 23:17:31 +01:00
#include <functional>
#include <utility>
#include <string>
#include <vector>
2016-02-01 22:55:43 +01:00
#include <set>
#include <map>
namespace cfg
{
2017-08-02 12:22:53 +02:00
// Format min and max values
std::vector<std::string> make_int_range(s64 min, s64 max);
// Format min and max unsigned values
std::vector<std::string> make_uint_range(u64 min, u64 max);
// Format min and max float values
std::vector<std::string> make_float_range(f64 min, f64 max);
// Internal hack
bool try_to_enum_value(u64* out, decltype(&fmt_class_string<int>::format) func, std::string_view);
// Internal hack
std::vector<std::string> try_to_enum_list(decltype(&fmt_class_string<int>::format) func);
2016-02-01 22:55:43 +01:00
// Config tree entry type.
enum class type : unsigned
2016-02-01 22:55:43 +01:00
{
node = 0, // cfg::node type
_bool, // cfg::_bool type
_enum, // cfg::_enum type
_int, // cfg::_int type
uint, // cfg::uint type
string, // cfg::string type
set, // cfg::set_entry type
map, // cfg::map_entry type
node_map, // cfg::node_map_entry type
log, // cfg::log_entry type
device, // cfg::device_entry type
2016-02-01 22:55:43 +01:00
};
// Config tree entry abstract base class
2017-05-20 13:45:02 +02:00
class _base
2016-02-01 22:55:43 +01:00
{
2021-03-30 17:31:46 +02:00
const type m_type{};
2016-02-01 22:55:43 +01:00
protected:
_base* m_parent = nullptr;
bool m_dynamic = true;
2021-03-30 17:31:46 +02:00
const std::string m_name{};
static u32 id_counter;
u32 m_id = 0;
2016-02-01 22:55:43 +01:00
// Ownerless entry constructor
2017-05-20 13:45:02 +02:00
_base(type _type);
2016-02-01 22:55:43 +01:00
// Owned entry constructor
_base(type _type, class node* owner, std::string name, bool dynamic);
2016-02-01 22:55:43 +01:00
public:
2017-05-20 13:45:02 +02:00
_base(const _base&) = delete;
2016-02-01 22:55:43 +01:00
2018-09-22 21:35:52 +02:00
_base& operator=(const _base&) = delete;
2021-03-30 17:31:46 +02:00
virtual ~_base() = default;
// Get unique ID
u32 get_id() const
{
return m_id;
}
// Get parent
_base* get_parent() const
{
return m_parent;
}
2016-02-01 22:55:43 +01:00
// Get type
type get_type() const
{
return m_type;
}
2016-02-01 22:55:43 +01:00
// Get name
const std::string& get_name() const
{
return m_name;
}
2021-03-20 17:06:43 +01:00
// Get dynamic property for reloading configs during games
bool get_is_dynamic() const
{
return m_dynamic;
}
2016-02-01 22:55:43 +01:00
// Reset defaults
virtual void from_default() = 0;
// Convert to string (optional)
virtual std::string to_string() const
{
return {};
}
virtual nlohmann::ordered_json to_json() const = 0;
virtual bool from_json(const nlohmann::json&, bool dynamic = false) = 0;
2025-03-17 17:31:41 +01:00
// Convert default to string (optional)
virtual std::string def_to_string() const
{
return {};
2016-02-01 22:55:43 +01:00
}
// Try to convert from string (optional)
virtual bool from_string(std::string_view, bool /*dynamic*/ = false);
2016-02-01 22:55:43 +01:00
// Get string list (optional)
virtual std::vector<std::string> to_list() const
{
return {};
2016-02-01 22:55:43 +01:00
}
// Set multiple values. Implementation-specific, optional.
virtual bool from_list(std::vector<std::string>&&);
2023-12-29 18:33:29 +01:00
bool save(std::string_view cfg_name) const;
2016-02-01 22:55:43 +01:00
};
// Config tree node which contains another nodes
2017-05-20 13:45:02 +02:00
class node : public _base
2016-02-01 22:55:43 +01:00
{
2021-03-30 17:31:46 +02:00
std::vector<_base*> m_nodes{};
2016-02-01 22:55:43 +01:00
2017-05-20 13:45:02 +02:00
friend class _base;
2016-02-01 22:55:43 +01:00
public:
// Root node constructor
node()
2017-05-20 13:45:02 +02:00
: _base(type::node)
2016-02-01 22:55:43 +01:00
{
}
// Registered node constructor
node(node* owner, std::string name, bool dynamic = true)
: _base(type::node, owner, std::move(name), dynamic)
2016-02-01 22:55:43 +01:00
{
}
// Get child nodes
2017-05-20 13:45:02 +02:00
const auto& get_nodes() const
2016-02-01 22:55:43 +01:00
{
return m_nodes;
}
// Serialize node
std::string to_string() const override;
nlohmann::ordered_json to_json() const override;
2016-02-01 22:55:43 +01:00
// Deserialize node
bool from_string(std::string_view value, bool dynamic = false) override;
bool from_json(const nlohmann::json&, bool dynamic = false) override;
2016-02-01 22:55:43 +01:00
// Set default values
void from_default() override;
};
2017-05-20 13:45:02 +02:00
class _bool final : public _base
2016-02-01 22:55:43 +01:00
{
atomic_t<bool> m_value;
2016-02-01 22:55:43 +01:00
2017-05-20 13:45:02 +02:00
public:
2020-03-05 19:02:28 +01:00
bool def;
2016-02-01 22:55:43 +01:00
_bool(node* owner, std::string name, bool def = false, bool dynamic = false)
: _base(type::_bool, owner, std::move(name), dynamic), m_value(def), def(def)
2016-02-01 22:55:43 +01:00
{
}
explicit operator bool() const
{
2017-05-20 13:45:02 +02:00
return m_value;
2016-02-01 22:55:43 +01:00
}
bool get() const
2018-05-04 22:48:05 +02:00
{
return m_value;
}
2017-01-25 00:22:19 +01:00
void from_default() override;
2016-02-01 22:55:43 +01:00
std::string to_string() const override
{
2017-05-20 13:45:02 +02:00
return m_value ? "true" : "false";
2016-02-01 22:55:43 +01:00
}
nlohmann::ordered_json to_json() const override
2025-03-17 17:31:41 +01:00
{
return {
2025-03-17 17:31:41 +01:00
{"type", "bool"},
{"value", m_value.load()},
{"default", def},
};
}
std::string def_to_string() const override
{
return def ? "true" : "false";
}
bool from_string(std::string_view value, bool /*dynamic*/ = false) override
2016-02-01 22:55:43 +01:00
{
if (value.size() != 4 && value.size() != 5)
{
return false;
}
char copy[5];
std::transform(value.begin(), value.end(), std::begin(copy), ::tolower);
if (value.size() == 5 && std::string_view{copy, 5} == "false")
2017-05-20 13:45:02 +02:00
m_value = false;
else if (value.size() == 4 && std::string_view{copy, 4} == "true")
2017-05-20 13:45:02 +02:00
m_value = true;
2016-02-01 22:55:43 +01:00
else
return false;
return true;
}
[Qt/Input] Improve pad_settings_dialog a bit (#3611) * Input: further work on remapping Xinput and begin work on remapping DS4 * Input: Improve pad_settings_dialog a bit and begin Remapping for XInput * Input: begin evdev remapping and change all handlers to use cfg::string * Input: finish work on remapping evdev and some more crap * Input: finish work on remapping Xinput and DS4 * Input: add DS4 Colors to DS4 config * Input: Improve DS4 deadzone scaling Jarves made some mistakes, so I'll fix them in the follow up commit * Input: fix Jarves fixes on DS4 deadzone and remove unnecessary usage of toUtf8 * Input: add primitive batterychecks to XInput and DS4 * Input: add mmjoystick remapping * Input: Fix evdev and some Vibration issues * Input: adjust capabilities to fix stick input for games like LoS 2 also fix threshold slider minimum also add ps button to all the handlers * Input: Further evdev work based on danilaml code review and own debugging: Fixed path issue, <= 0 issue, some captures, const, axis with same codes. Adds a map to each device that differentiates negative and positive axis mappings. adjusted rest of the file to tabs (ListDevices and beginning of threadProc) * Input: use 20ms vibration update time for xbox one elite controllers. * Input: Fix return type of Clamp() * Input: Evdev Fix * Input: Evdev Optional GetNextButtonPress presumably better than the other * Input: review changes * Input: evdev: fix wrong index in axis handling move bindpadtodevice down to keep consistency between handlers and not get crazy * Input: evdev: fix expensive add_device in GetNextButtonPress * cleanup * Input: mmjoy: fix type * Input: evdev: final fixes * Input: evdev: exclude unnecessary buttons while mapping Xbox 360 or DS4 * Input: add deadzone preview by passing necessary values in callback use 0.5 of max value for threshold in pad dialog * Input: get rid of all-uppercase variables
2017-11-27 22:31:15 +01:00
bool from_json(const nlohmann::json& json, bool) override
2025-03-17 17:31:41 +01:00
{
if (!json.is_boolean())
{
return false;
}
m_value = json.get<nlohmann::json::boolean_t>();
return true;
}
[Qt/Input] Improve pad_settings_dialog a bit (#3611) * Input: further work on remapping Xinput and begin work on remapping DS4 * Input: Improve pad_settings_dialog a bit and begin Remapping for XInput * Input: begin evdev remapping and change all handlers to use cfg::string * Input: finish work on remapping evdev and some more crap * Input: finish work on remapping Xinput and DS4 * Input: add DS4 Colors to DS4 config * Input: Improve DS4 deadzone scaling Jarves made some mistakes, so I'll fix them in the follow up commit * Input: fix Jarves fixes on DS4 deadzone and remove unnecessary usage of toUtf8 * Input: add primitive batterychecks to XInput and DS4 * Input: add mmjoystick remapping * Input: Fix evdev and some Vibration issues * Input: adjust capabilities to fix stick input for games like LoS 2 also fix threshold slider minimum also add ps button to all the handlers * Input: Further evdev work based on danilaml code review and own debugging: Fixed path issue, <= 0 issue, some captures, const, axis with same codes. Adds a map to each device that differentiates negative and positive axis mappings. adjusted rest of the file to tabs (ListDevices and beginning of threadProc) * Input: use 20ms vibration update time for xbox one elite controllers. * Input: Fix return type of Clamp() * Input: Evdev Fix * Input: Evdev Optional GetNextButtonPress presumably better than the other * Input: review changes * Input: evdev: fix wrong index in axis handling move bindpadtodevice down to keep consistency between handlers and not get crazy * Input: evdev: fix expensive add_device in GetNextButtonPress * cleanup * Input: mmjoy: fix type * Input: evdev: final fixes * Input: evdev: exclude unnecessary buttons while mapping Xbox 360 or DS4 * Input: add deadzone preview by passing necessary values in callback use 0.5 of max value for threshold in pad dialog * Input: get rid of all-uppercase variables
2017-11-27 22:31:15 +01:00
void set(const bool& value)
{
m_value = value;
}
2016-02-01 22:55:43 +01:00
};
// Value node with fixed set of possible values, each maps to an enum value of type T.
2017-05-20 13:45:02 +02:00
template <typename T>
class _enum : public _base
2016-02-01 22:55:43 +01:00
{
atomic_t<T> m_value;
2016-02-01 22:55:43 +01:00
public:
const T def;
_enum(node* owner, const std::string& name, T value = {}, bool dynamic = false)
: _base(type::_enum, owner, name, dynamic), m_value(value), def(value)
2016-02-01 22:55:43 +01:00
{
}
operator T() const
{
2017-05-20 13:45:02 +02:00
return m_value;
2016-02-01 22:55:43 +01:00
}
T get() const
2018-05-04 22:48:05 +02:00
{
return m_value;
}
2023-05-22 22:05:16 +02:00
T get_default() const
{
return def;
}
2020-09-29 18:26:54 +02:00
void set(T value)
{
m_value = value;
}
2016-02-01 22:55:43 +01:00
void from_default() override
{
m_value = def;
}
std::string to_string() const override
{
std::string result;
fmt_class_string<T>::format(result, fmt_unveil<T>::get(m_value.load()));
return result; // TODO: ???
2016-02-01 22:55:43 +01:00
}
nlohmann::ordered_json to_json() const override
2025-03-17 17:31:41 +01:00
{
return {
2025-03-17 17:31:41 +01:00
{"type", "enum"},
{"value", to_string()},
{"default", def_to_string()},
{"variants", to_list()},
};
}
std::string def_to_string() const override
{
std::string result;
fmt_class_string<T>::format(result, fmt_unveil<T>::get(def));
return result; // TODO: ???
}
bool from_string(std::string_view value, bool /*dynamic*/ = false) override
2016-02-01 22:55:43 +01:00
{
u64 result;
if (try_to_enum_value(&result, &fmt_class_string<T>::format, value))
2016-02-01 22:55:43 +01:00
{
2016-08-07 15:59:46 +02:00
// No narrowing check, it's hard to do right there
m_value = static_cast<T>(static_cast<std::underlying_type_t<T>>(result));
return true;
2016-02-01 22:55:43 +01:00
}
return false;
}
bool from_json(const nlohmann::json& json, bool dynamic) override
2025-03-17 17:31:41 +01:00
{
if (!json.is_string())
{
return false;
}
return from_string(json.get<nlohmann::json::string_t>(), dynamic);
}
2016-02-01 22:55:43 +01:00
std::vector<std::string> to_list() const override
{
return try_to_enum_list(&fmt_class_string<T>::format);
2016-02-01 22:55:43 +01:00
}
};
// Signed 32/64-bit integer entry with custom Min/Max range.
2017-05-20 13:45:02 +02:00
template <s64 Min, s64 Max>
class _int final : public _base
2016-02-01 22:55:43 +01:00
{
2017-05-20 13:45:02 +02:00
static_assert(Min < Max, "Invalid cfg::_int range");
2016-02-01 22:55:43 +01:00
// Prefer 32 bit type if possible
using int_type = std::conditional_t<Min >= s32{smin} && Max <= s32{smax}, s32, s64>;
2016-02-01 22:55:43 +01:00
atomic_t<int_type> m_value;
2025-03-19 23:17:31 +01:00
std::function<s64()> m_min_fn;
std::function<s64()> m_max_fn;
2016-02-01 22:55:43 +01:00
public:
[Qt/Input] Improve pad_settings_dialog a bit (#3611) * Input: further work on remapping Xinput and begin work on remapping DS4 * Input: Improve pad_settings_dialog a bit and begin Remapping for XInput * Input: begin evdev remapping and change all handlers to use cfg::string * Input: finish work on remapping evdev and some more crap * Input: finish work on remapping Xinput and DS4 * Input: add DS4 Colors to DS4 config * Input: Improve DS4 deadzone scaling Jarves made some mistakes, so I'll fix them in the follow up commit * Input: fix Jarves fixes on DS4 deadzone and remove unnecessary usage of toUtf8 * Input: add primitive batterychecks to XInput and DS4 * Input: add mmjoystick remapping * Input: Fix evdev and some Vibration issues * Input: adjust capabilities to fix stick input for games like LoS 2 also fix threshold slider minimum also add ps button to all the handlers * Input: Further evdev work based on danilaml code review and own debugging: Fixed path issue, <= 0 issue, some captures, const, axis with same codes. Adds a map to each device that differentiates negative and positive axis mappings. adjusted rest of the file to tabs (ListDevices and beginning of threadProc) * Input: use 20ms vibration update time for xbox one elite controllers. * Input: Fix return type of Clamp() * Input: Evdev Fix * Input: Evdev Optional GetNextButtonPress presumably better than the other * Input: review changes * Input: evdev: fix wrong index in axis handling move bindpadtodevice down to keep consistency between handlers and not get crazy * Input: evdev: fix expensive add_device in GetNextButtonPress * cleanup * Input: mmjoy: fix type * Input: evdev: final fixes * Input: evdev: exclude unnecessary buttons while mapping Xbox 360 or DS4 * Input: add deadzone preview by passing necessary values in callback use 0.5 of max value for threshold in pad dialog * Input: get rid of all-uppercase variables
2017-11-27 22:31:15 +01:00
int_type def;
2016-02-01 22:55:43 +01:00
// Expose range
2023-01-21 00:53:49 +01:00
static constexpr s64 max = Max;
static constexpr s64 min = Min;
2025-03-19 23:17:31 +01:00
_int(node* owner, const std::string& name, int_type def = std::min<int_type>(Max, std::max<int_type>(Min, 0)), bool dynamic = false,
std::function<s64()> min_fn = nullptr,
std::function<s64()> max_fn = nullptr)
: _base(type::_int, owner, name, dynamic), m_value(def), m_min_fn(std::move(min_fn)), m_max_fn(std::move(max_fn)), def(def)
2016-02-01 22:55:43 +01:00
{
}
operator int_type() const
{
2017-05-20 13:45:02 +02:00
return m_value;
2016-02-01 22:55:43 +01:00
}
int_type get() const
2018-05-04 22:48:05 +02:00
{
return m_value;
}
2016-02-01 22:55:43 +01:00
void from_default() override
{
m_value = def;
}
std::string to_string() const override
{
2017-05-20 13:45:02 +02:00
return std::to_string(m_value);
2016-02-01 22:55:43 +01:00
}
2025-03-19 23:17:31 +01:00
s64 get_min() const
{
if (m_min_fn != nullptr)
{
return m_min_fn();
}
return min;
}
s64 get_max() const
{
if (m_max_fn != nullptr)
{
return m_max_fn();
}
return max;
}
nlohmann::ordered_json to_json() const override
2025-03-17 17:31:41 +01:00
{
return {
2025-03-17 17:31:41 +01:00
{"type", "int"},
2025-03-19 02:57:09 +01:00
{"value", to_string()},
{"default", def_to_string()},
2025-03-19 23:17:31 +01:00
{"min", std::to_string(get_min())},
{"max", std::to_string(get_max())},
2025-03-17 17:31:41 +01:00
};
}
std::string def_to_string() const override
{
return std::to_string(def);
}
bool from_string(std::string_view value, bool /*dynamic*/ = false) override
2016-02-01 22:55:43 +01:00
{
s64 result;
if (try_to_int64(&result, value, Min, Max))
{
m_value = static_cast<int_type>(result);
return true;
}
return false;
}
2017-08-02 12:22:53 +02:00
bool from_json(const nlohmann::json& json, bool) override
2025-03-17 17:31:41 +01:00
{
if (!json.is_number_integer())
{
return false;
}
auto value = json.get<nlohmann::json::number_integer_t>();
2025-03-19 23:17:31 +01:00
if (value < get_min() || value > get_max())
2025-03-17 17:31:41 +01:00
{
return false;
}
m_value = value;
return true;
}
[Qt/Input] Improve pad_settings_dialog a bit (#3611) * Input: further work on remapping Xinput and begin work on remapping DS4 * Input: Improve pad_settings_dialog a bit and begin Remapping for XInput * Input: begin evdev remapping and change all handlers to use cfg::string * Input: finish work on remapping evdev and some more crap * Input: finish work on remapping Xinput and DS4 * Input: add DS4 Colors to DS4 config * Input: Improve DS4 deadzone scaling Jarves made some mistakes, so I'll fix them in the follow up commit * Input: fix Jarves fixes on DS4 deadzone and remove unnecessary usage of toUtf8 * Input: add primitive batterychecks to XInput and DS4 * Input: add mmjoystick remapping * Input: Fix evdev and some Vibration issues * Input: adjust capabilities to fix stick input for games like LoS 2 also fix threshold slider minimum also add ps button to all the handlers * Input: Further evdev work based on danilaml code review and own debugging: Fixed path issue, <= 0 issue, some captures, const, axis with same codes. Adds a map to each device that differentiates negative and positive axis mappings. adjusted rest of the file to tabs (ListDevices and beginning of threadProc) * Input: use 20ms vibration update time for xbox one elite controllers. * Input: Fix return type of Clamp() * Input: Evdev Fix * Input: Evdev Optional GetNextButtonPress presumably better than the other * Input: review changes * Input: evdev: fix wrong index in axis handling move bindpadtodevice down to keep consistency between handlers and not get crazy * Input: evdev: fix expensive add_device in GetNextButtonPress * cleanup * Input: mmjoy: fix type * Input: evdev: final fixes * Input: evdev: exclude unnecessary buttons while mapping Xbox 360 or DS4 * Input: add deadzone preview by passing necessary values in callback use 0.5 of max value for threshold in pad dialog * Input: get rid of all-uppercase variables
2017-11-27 22:31:15 +01:00
void set(const s64& value)
{
2023-01-21 00:53:49 +01:00
ensure(value >= Min && value <= Max);
[Qt/Input] Improve pad_settings_dialog a bit (#3611) * Input: further work on remapping Xinput and begin work on remapping DS4 * Input: Improve pad_settings_dialog a bit and begin Remapping for XInput * Input: begin evdev remapping and change all handlers to use cfg::string * Input: finish work on remapping evdev and some more crap * Input: finish work on remapping Xinput and DS4 * Input: add DS4 Colors to DS4 config * Input: Improve DS4 deadzone scaling Jarves made some mistakes, so I'll fix them in the follow up commit * Input: fix Jarves fixes on DS4 deadzone and remove unnecessary usage of toUtf8 * Input: add primitive batterychecks to XInput and DS4 * Input: add mmjoystick remapping * Input: Fix evdev and some Vibration issues * Input: adjust capabilities to fix stick input for games like LoS 2 also fix threshold slider minimum also add ps button to all the handlers * Input: Further evdev work based on danilaml code review and own debugging: Fixed path issue, <= 0 issue, some captures, const, axis with same codes. Adds a map to each device that differentiates negative and positive axis mappings. adjusted rest of the file to tabs (ListDevices and beginning of threadProc) * Input: use 20ms vibration update time for xbox one elite controllers. * Input: Fix return type of Clamp() * Input: Evdev Fix * Input: Evdev Optional GetNextButtonPress presumably better than the other * Input: review changes * Input: evdev: fix wrong index in axis handling move bindpadtodevice down to keep consistency between handlers and not get crazy * Input: evdev: fix expensive add_device in GetNextButtonPress * cleanup * Input: mmjoy: fix type * Input: evdev: final fixes * Input: evdev: exclude unnecessary buttons while mapping Xbox 360 or DS4 * Input: add deadzone preview by passing necessary values in callback use 0.5 of max value for threshold in pad dialog * Input: get rid of all-uppercase variables
2017-11-27 22:31:15 +01:00
m_value = static_cast<int_type>(value);
}
2017-08-02 12:22:53 +02:00
std::vector<std::string> to_list() const override
{
return make_int_range(Min, Max);
}
2016-02-01 22:55:43 +01:00
};
// Float entry with custom Min/Max range.
template <s32 Min, s32 Max>
class _float final : public _base
{
static_assert(Min < Max, "Invalid cfg::_float range");
using float_type = f64;
atomic_t<float_type> m_value;
public:
float_type def;
// Expose range
static constexpr float_type max = Max;
static constexpr float_type min = Min;
_float(node* owner, const std::string& name, float_type def = std::min<float_type>(Max, std::max<float_type>(Min, 0)), bool dynamic = false)
: _base(type::_int, owner, name, dynamic), m_value(def), def(def)
{
}
operator float_type() const
{
return m_value;
}
float_type get() const
{
return m_value;
}
void from_default() override
{
m_value = def;
}
std::string to_string() const override
{
2023-02-24 15:26:00 +01:00
std::string result;
if (try_to_string(&result, m_value))
{
return result;
}
return "0.0";
}
nlohmann::ordered_json to_json() const override
2025-03-17 17:31:41 +01:00
{
return {
2025-03-17 17:31:41 +01:00
{"type", "float"},
2025-03-19 02:57:09 +01:00
{"value", to_string()},
{"default", def_to_string()},
{"min", std::to_string(min)},
{"max", std::to_string(max)},
2025-03-17 17:31:41 +01:00
};
}
std::string def_to_string() const override
{
2023-02-24 15:26:00 +01:00
std::string result;
if (try_to_string(&result, def))
{
return result;
}
return "0.0";
}
bool from_string(std::string_view value, bool /*dynamic*/ = false) override
{
f64 result;
if (try_to_float(&result, value, Min, Max))
{
m_value = static_cast<float_type>(result);
return true;
}
return false;
}
bool from_json(const nlohmann::json& json, bool) override
2025-03-17 17:31:41 +01:00
{
if (!json.is_number_float())
{
return false;
}
auto value = json.get<nlohmann::json::number_float_t>();
if (value < min || value > max)
{
return false;
}
m_value = value;
return true;
}
void set(const f64& value)
{
2023-01-21 00:53:49 +01:00
ensure(value >= Min && value <= Max);
m_value = static_cast<float_type>(value);
}
std::vector<std::string> to_list() const override
{
return make_float_range(Min, Max);
}
};
2016-02-01 22:55:43 +01:00
// Alias for 32 bit int
using int32 = _int<s32{smin}, s32{smax}>;
2016-02-01 22:55:43 +01:00
// Alias for 64 bit int
using int64 = _int<s64{smin}, s64{smax}>;
2016-02-01 22:55:43 +01:00
// Unsigned 32/64-bit integer entry with custom Min/Max range.
template <u64 Min, u64 Max>
class uint final : public _base
{
static_assert(Min < Max, "Invalid cfg::uint range");
// Prefer 32 bit type if possible
using int_type = std::conditional_t<Max <= u32{umax}, u32, u64>;
atomic_t<int_type> m_value;
public:
int_type def;
// Expose range
2023-01-21 00:53:49 +01:00
static constexpr u64 max = Max;
static constexpr u64 min = Min;
uint(node* owner, const std::string& name, int_type def = std::max<int_type>(Min, 0), bool dynamic = false)
: _base(type::uint, owner, name, dynamic), m_value(def), def(def)
{
}
operator int_type() const
{
return m_value;
}
int_type get() const
{
return m_value;
}
void from_default() override
{
m_value = def;
}
std::string to_string() const override
{
return std::to_string(m_value);
}
nlohmann::ordered_json to_json() const override
2025-03-17 17:31:41 +01:00
{
return {
2025-03-17 17:31:41 +01:00
{"type", "uint"},
2025-03-19 02:57:09 +01:00
{"value", to_string()},
{"default", def_to_string()},
{"min", std::to_string(min)},
{"max", std::to_string(max)},
2025-03-17 17:31:41 +01:00
};
}
std::string def_to_string() const override
{
return std::to_string(def);
}
bool from_string(std::string_view value, bool /*dynamic*/ = false) override
{
u64 result;
if (try_to_uint64(&result, value, Min, Max))
{
m_value = static_cast<int_type>(result);
return true;
}
return false;
}
bool from_json(const nlohmann::json& json, bool) override
2025-03-17 17:31:41 +01:00
{
if (!json.is_number_unsigned())
{
return false;
}
auto value = json.get<nlohmann::json::number_unsigned_t>();
if (value < min || value > max)
{
return false;
}
m_value = value;
return true;
}
void set(const u64& value)
{
2023-01-21 00:53:49 +01:00
ensure(value >= Min && value <= Max);
m_value = static_cast<int_type>(value);
}
std::vector<std::string> to_list() const override
{
return make_uint_range(Min, Max);
}
};
// Alias for 32 bit uint
using uint32 = uint<0, u32{umax}>;
// Alias for 64 bit int
using uint64 = uint<0, u64{umax}>;
2016-02-01 22:55:43 +01:00
// Simple string entry with mutex
2020-08-27 21:47:04 +02:00
class string : public _base
2016-02-01 22:55:43 +01:00
{
atomic_ptr<std::string> m_value;
2016-02-01 22:55:43 +01:00
public:
[Qt/Input] Improve pad_settings_dialog a bit (#3611) * Input: further work on remapping Xinput and begin work on remapping DS4 * Input: Improve pad_settings_dialog a bit and begin Remapping for XInput * Input: begin evdev remapping and change all handlers to use cfg::string * Input: finish work on remapping evdev and some more crap * Input: finish work on remapping Xinput and DS4 * Input: add DS4 Colors to DS4 config * Input: Improve DS4 deadzone scaling Jarves made some mistakes, so I'll fix them in the follow up commit * Input: fix Jarves fixes on DS4 deadzone and remove unnecessary usage of toUtf8 * Input: add primitive batterychecks to XInput and DS4 * Input: add mmjoystick remapping * Input: Fix evdev and some Vibration issues * Input: adjust capabilities to fix stick input for games like LoS 2 also fix threshold slider minimum also add ps button to all the handlers * Input: Further evdev work based on danilaml code review and own debugging: Fixed path issue, <= 0 issue, some captures, const, axis with same codes. Adds a map to each device that differentiates negative and positive axis mappings. adjusted rest of the file to tabs (ListDevices and beginning of threadProc) * Input: use 20ms vibration update time for xbox one elite controllers. * Input: Fix return type of Clamp() * Input: Evdev Fix * Input: Evdev Optional GetNextButtonPress presumably better than the other * Input: review changes * Input: evdev: fix wrong index in axis handling move bindpadtodevice down to keep consistency between handlers and not get crazy * Input: evdev: fix expensive add_device in GetNextButtonPress * cleanup * Input: mmjoy: fix type * Input: evdev: final fixes * Input: evdev: exclude unnecessary buttons while mapping Xbox 360 or DS4 * Input: add deadzone preview by passing necessary values in callback use 0.5 of max value for threshold in pad dialog * Input: get rid of all-uppercase variables
2017-11-27 22:31:15 +01:00
std::string def;
2016-02-01 22:55:43 +01:00
string(node* owner, std::string name, std::string def = {}, bool dynamic = false)
: _base(type::string, owner, std::move(name), dynamic), m_value(def), def(std::move(def))
2016-02-01 22:55:43 +01:00
{
}
operator std::string() const
{
return *m_value.load().get();
2016-02-01 22:55:43 +01:00
}
2017-01-25 00:22:19 +01:00
void from_default() override;
2016-02-01 22:55:43 +01:00
std::string to_string() const override
{
return *m_value.load().get();
2016-02-01 22:55:43 +01:00
}
nlohmann::ordered_json to_json() const override
2025-03-17 17:31:41 +01:00
{
return {
2025-03-17 17:31:41 +01:00
{"type", "string"},
{"value", to_string()},
{"default", def_to_string()},
};
}
std::string def_to_string() const override
{
return def;
}
bool from_string(std::string_view value, bool /*dynamic*/ = false) override
2016-02-01 22:55:43 +01:00
{
m_value = std::string(value);
2016-02-01 22:55:43 +01:00
return true;
}
2025-03-17 17:31:41 +01:00
bool from_json(const nlohmann::json& json, bool) override
2025-03-17 17:31:41 +01:00
{
if (!json.is_string())
{
return false;
}
m_value = json.get<nlohmann::json::string_t>();
return true;
}
2016-02-01 22:55:43 +01:00
};
2020-03-31 20:50:23 +02:00
// Simple set entry (TODO: template for various types)
2017-05-20 13:45:02 +02:00
class set_entry final : public _base
2016-02-01 22:55:43 +01:00
{
2021-03-30 17:31:46 +02:00
std::set<std::string> m_set{};
2016-02-01 22:55:43 +01:00
public:
// Default value is empty list in current implementation
2020-03-31 20:50:23 +02:00
set_entry(node* owner, const std::string& name)
: _base(type::set, owner, name, false)
2016-02-01 22:55:43 +01:00
{
}
const std::set<std::string>& get_set() const
2016-02-01 22:55:43 +01:00
{
return m_set;
}
void set_set(std::set<std::string>&& set)
{
m_set = std::move(set);
}
2017-01-25 00:22:19 +01:00
void from_default() override;
2016-02-01 22:55:43 +01:00
std::vector<std::string> to_list() const override
{
return {m_set.begin(), m_set.end()};
2016-02-01 22:55:43 +01:00
}
nlohmann::ordered_json to_json() const override
2025-03-17 17:31:41 +01:00
{
return {
2025-03-17 17:31:41 +01:00
{"type", "set"},
{"value", to_list()},
};
}
2016-02-01 22:55:43 +01:00
bool from_list(std::vector<std::string>&& list) override
{
m_set = {std::make_move_iterator(list.begin()), std::make_move_iterator(list.end())};
2016-02-01 22:55:43 +01:00
return true;
}
2025-03-17 17:31:41 +01:00
bool from_json(const nlohmann::json& json, bool) override
2025-03-17 17:31:41 +01:00
{
if (!json.is_array())
{
return false;
}
auto array = json.get<nlohmann::json::array_t>();
std::vector<std::string> string_array;
string_array.reserve(array.size());
for (auto& elem : array)
2025-03-17 17:31:41 +01:00
{
if (!elem.is_string())
{
return false;
}
string_array.push_back(elem.get<nlohmann::json::string_t>());
}
return from_list(std::move(string_array));
}
2016-02-01 22:55:43 +01:00
};
template <typename T>
using map_of_type = std::map<std::string, T, std::less<>>;
class map_entry : public _base
2021-08-10 21:45:26 +02:00
{
map_of_type<std::string> m_map{};
2021-08-10 21:45:26 +02:00
public:
map_entry(node* owner, const std::string& name, type _type = type::map)
: _base(_type, owner, name, true)
2021-08-10 21:45:26 +02:00
{
}
const map_of_type<std::string>& get_map() const
2021-08-10 21:45:26 +02:00
{
return m_map;
}
nlohmann::ordered_json to_json() const override
2025-03-17 17:31:41 +01:00
{
return {
2025-03-17 17:31:41 +01:00
{"type", "map"},
{"value", m_map},
};
}
bool from_json(const nlohmann::json& json, bool) override
2025-03-17 17:31:41 +01:00
{
if (!json.is_object())
{
return false;
}
for (auto& elem : json.get<nlohmann::json::object_t>())
2025-03-17 17:31:41 +01:00
{
set_value(elem.first, elem.second);
}
return true;
}
std::string get_value(std::string_view key);
2021-08-10 21:45:26 +02:00
void set_value(std::string key, std::string value);
void set_map(map_of_type<std::string>&& map);
2021-08-10 21:45:26 +02:00
void erase(std::string_view key);
2021-09-05 02:15:08 +02:00
2021-08-10 21:45:26 +02:00
void from_default() override;
};
class node_map_entry final : public map_entry
{
public:
node_map_entry(node* owner, const std::string& name)
: map_entry(owner, name, type::node_map)
{
}
};
2017-05-20 13:45:02 +02:00
class log_entry final : public _base
2017-05-13 20:30:37 +02:00
{
map_of_type<logs::level> m_map{};
2017-05-13 20:30:37 +02:00
public:
2017-05-20 13:45:02 +02:00
log_entry(node* owner, const std::string& name)
2020-03-31 20:50:23 +02:00
: _base(type::log, owner, name, true)
2017-05-13 20:30:37 +02:00
{
}
const map_of_type<logs::level>& get_map() const
2017-05-13 20:30:37 +02:00
{
return m_map;
}
nlohmann::ordered_json to_json() const override
2025-03-17 17:31:41 +01:00
{
auto levels = try_to_enum_list(&fmt_class_string<logs::level>::format);
auto values = nlohmann::json::object();
for (auto [key, level] : m_map)
{
std::string level_string;
fmt_class_string<logs::level>::format(level_string, fmt_unveil<logs::level>::get(level));
values[key] = level_string;
}
return {
2025-03-17 17:31:41 +01:00
{"type", "log_map"},
{"values", values},
{"levels", levels},
};
}
bool from_json(const nlohmann::json& json, bool) override
2025-03-17 17:31:41 +01:00
{
if (!json.is_object())
{
return false;
}
for (auto [key, valueString] : json.get<nlohmann::json::object_t>())
2025-03-17 17:31:41 +01:00
{
if (!valueString.is_string())
{
continue;
}
logs::level value;
if (u64 int_value;
try_to_enum_value(&int_value, &fmt_class_string<logs::level>::format, valueString.get<std::string>()))
{
value = static_cast<logs::level>(static_cast<std::underlying_type_t<logs::level>>(int_value));
}
else
{
continue;
}
m_map[key] = value;
}
return true;
}
void set_map(map_of_type<logs::level>&& map);
2017-05-13 20:30:37 +02:00
void from_default() override;
};
2022-05-08 00:13:21 +02:00
struct device_info
{
std::string path;
std::string serial;
std::string vid;
std::string pid;
std::pair<u16, u16> get_usb_ids() const;
2025-03-17 17:31:41 +01:00
nlohmann::json to_json() const
{
return {
{"path", path},
{"serial", serial},
{"vid", vid},
{"pid", pid},
};
}
bool from_json(const nlohmann::json& json)
2025-03-17 17:31:41 +01:00
{
if (json.contains("path"))
{
if (!json["path"].is_string())
{
return false;
}
path = json["path"];
}
if (json.contains("serial"))
{
if (!json["serial"].is_string())
{
return false;
}
path = json["serial"];
}
if (json.contains("vid"))
{
if (!json["vid"].is_string())
{
return false;
}
path = json["vid"];
}
if (json.contains("pid"))
{
if (!json["pid"].is_string())
{
return false;
}
path = json["pid"];
}
return true;
}
2022-05-08 00:13:21 +02:00
};
class device_entry final : public _base
{
map_of_type<device_info> m_map{};
2022-05-08 20:18:42 +02:00
map_of_type<device_info> m_default{};
2022-05-08 00:13:21 +02:00
public:
device_entry(node* owner, const std::string& name, map_of_type<device_info> def = {})
: _base(type::device, owner, name, true), m_map(std::move(def))
2022-05-08 00:13:21 +02:00
{
2022-05-08 20:18:42 +02:00
m_default = m_map;
2022-05-08 00:13:21 +02:00
}
nlohmann::ordered_json to_json() const override
2025-03-17 17:31:41 +01:00
{
return {};
}
bool from_json(const nlohmann::json&, bool) override
2025-03-17 17:31:41 +01:00
{
return false;
}
2022-05-08 00:13:21 +02:00
const map_of_type<device_info>& get_map() const
{
return m_map;
}
const map_of_type<device_info>& get_default() const
{
2022-05-08 20:18:42 +02:00
return m_default;
2022-05-08 00:13:21 +02:00
}
void set_map(map_of_type<device_info>&& map);
void from_default() override;
};
} // namespace cfg