1
0
mirror of https://github.com/OpenTTD/OpenTTD synced 2026-01-18 18:02:37 +01:00

Codechange: Support scoped enums as type for settings. (#15065)

This commit is contained in:
Michael Lutz
2026-01-09 19:55:19 +01:00
committed by GitHub
parent 241b5fcdfe
commit bd9ced2ec2
22 changed files with 83 additions and 44 deletions

View File

@@ -16,6 +16,9 @@
template <typename enum_type>
constexpr std::underlying_type_t<enum_type> to_underlying(enum_type e) { return static_cast<std::underlying_type_t<enum_type>>(e); }
/** Implementation of std::is_scoped_enum_v (from C++23) */
template <class T> constexpr bool is_scoped_enum_v = std::conjunction_v<std::is_enum<T>, std::negation<std::is_convertible<T, int>>>;
/** Trait to enable prefix/postfix incrementing operators. */
template <typename enum_type>
struct is_enum_incrementable {

View File

@@ -12,6 +12,7 @@
#include <variant>
#include "saveload/saveload.h"
#include "core/enum_type.hpp"
enum class SettingFlag : uint8_t {
GuiZeroIsSpecial, ///< A value of zero is possible and has a custom string (the one after "strval").
@@ -67,6 +68,14 @@ enum SettingType : uint8_t {
struct IniItem;
/**
* Type is convertible to TTo, either directly, through ConvertibleThroughBase or through to_underlying.
* @tparam T The type under consideration.
* @tparam TTo The type to convert to.
*/
template <typename T, typename TTo>
concept ConvertibleThroughBaseOrUnderlyingOrTo = ConvertibleThroughBaseOrTo<T, TTo> || (is_scoped_enum_v<T> && std::is_convertible_v<std::underlying_type_t<T>, TTo>);
/** Properties of config file settings. */
struct SettingDesc {
SettingDesc(const SaveLoad &save, SettingFlags flags, bool startup) :
@@ -167,7 +176,7 @@ struct IntSettingDesc : SettingDesc {
*/
using PostChangeCallback = void(int32_t value);
template <ConvertibleThroughBaseOrTo<int32_t> Tdef, ConvertibleThroughBaseOrTo<int32_t> Tmin, ConvertibleThroughBaseOrTo<uint32_t> Tmax, ConvertibleThroughBaseOrTo<int32_t> Tinterval>
template <ConvertibleThroughBaseOrUnderlyingOrTo<int32_t> Tdef, ConvertibleThroughBaseOrUnderlyingOrTo<int32_t> Tmin, ConvertibleThroughBaseOrUnderlyingOrTo<uint32_t> Tmax, ConvertibleThroughBaseOrUnderlyingOrTo<int32_t> Tinterval>
IntSettingDesc(const SaveLoad &save, SettingFlags flags, bool startup, Tdef def,
Tmin min, Tmax max, Tinterval interval, StringID str, StringID str_help, StringID str_val,
SettingCategory cat, PreChangeCheck pre_check, PostChangeCallback post_callback,
@@ -177,27 +186,36 @@ struct IntSettingDesc : SettingDesc {
str(str), str_help(str_help), str_val(str_val), cat(cat), pre_check(pre_check),
post_callback(post_callback),
get_title_cb(get_title_cb), get_help_cb(get_help_cb), get_value_params_cb(get_value_params_cb),
get_def_cb(get_def_cb), get_range_cb(get_range_cb) {
get_def_cb(get_def_cb), get_range_cb(get_range_cb)
{
if constexpr (ConvertibleThroughBase<Tdef>) {
this->def = def.base();
} else if constexpr (is_scoped_enum_v<Tdef>) {
this->def = to_underlying(def);
} else {
this->def = def;
}
if constexpr (ConvertibleThroughBase<Tmin>) {
this->min = min.base();
} else if constexpr (is_scoped_enum_v<Tmin>) {
this->min = to_underlying(min);
} else {
this->min = min;
}
if constexpr (ConvertibleThroughBase<Tmax>) {
this->max = max.base();
} else if constexpr (is_scoped_enum_v<Tmax>) {
this->max = to_underlying(max);
} else {
this->max = max;
}
if constexpr (ConvertibleThroughBase<Tinterval>) {
this->interval = interval.base();
} else if constexpr (is_scoped_enum_v<Tinterval>) {
this->interval = to_underlying(interval);
} else {
this->interval = interval;
}
@@ -269,7 +287,7 @@ struct BoolSettingDesc : IntSettingDesc {
struct OneOfManySettingDesc : IntSettingDesc {
typedef std::optional<uint32_t> OnConvert(std::string_view value); ///< callback prototype for conversion error
template <ConvertibleThroughBaseOrTo<int32_t> Tdef, ConvertibleThroughBaseOrTo<uint32_t> Tmax>
template <ConvertibleThroughBaseOrUnderlyingOrTo<int32_t> Tdef, ConvertibleThroughBaseOrUnderlyingOrTo<uint32_t> Tmax>
OneOfManySettingDesc(const SaveLoad &save, SettingFlags flags, bool startup, Tdef def,
Tmax max, StringID str, StringID str_help, StringID str_val, SettingCategory cat,
PreChangeCheck pre_check, PostChangeCallback post_callback,

View File

@@ -21,6 +21,24 @@ static StringID SettingHelpWallclock(const IntSettingDesc &sd);
* DO NOT edit this file. This file is automatically generated from the contents of /src/table/
*/
/* Helpers for validation. When deducing template arguments for functions overloads,
* the compiler will create a 'partial ordering of overloaded function templates' by
* preferring more specialized templates over more generic ones.
* For scoped enums the first overload is more specialized and gets chosen. For signed
* integers it is the second overload as we always compare with an unsigned limit. For anything
* else, the concepts reject the specific overloads, leaving only the last overload. */
template <typename T> requires is_scoped_enum_v<T>
constexpr auto ConvertEnumClass(T value)
{
return to_underlying(value);
}
template <std::signed_integral T>
constexpr auto ConvertEnumClass(T value) { return std::make_unsigned_t<T>(value); }
template <typename T> constexpr T ConvertEnumClass(T value) { return value; }
/**
* Settings-macro usage:
* The list might look daunting at first, but is in general easy to understand.

View File

@@ -24,7 +24,7 @@ SDT_BOOL = SDT_BOOL(CompanySettings, $var, SettingFlags({$flags}), $def,
SDT_VAR = SDT_VAR(CompanySettings, $var, $type, SettingFlags({$flags}), $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $def_cb, $range_cb, $from, $to, $cat, $extra, $startup),
[validation]
SDT_VAR = static_assert($max <= MAX_$type, "Maximum value for CompanySettings.$var exceeds storage size");
SDT_VAR = static_assert(ConvertEnumClass($max) <= MAX_$type, "Maximum value for CompanySettings.$var exceeds storage size");
[defaults]
flags = SettingFlag::PerCompany

View File

@@ -15,7 +15,7 @@ SDT_VAR = SDT_VAR (CurrencySpec, $var, $type, SettingFlags({$flags}), $def, $mi
SDT_SSTR = SDT_SSTR(CurrencySpec, $var, $type, SettingFlags({$flags}), $def, $pre_cb, $post_cb, $from, $to, $cat, $extra, $startup),
[validation]
SDT_VAR = static_assert($max <= MAX_$type, "Maximum value for CurrencySpec.$var exceeds storage size");
SDT_VAR = static_assert(ConvertEnumClass($max) <= MAX_$type, "Maximum value for CurrencySpec.$var exceeds storage size");
[defaults]
flags = SettingFlag::NotInSave, SettingFlag::NoNetworkSync

View File

@@ -25,8 +25,8 @@ SDT_BOOL = SDT_BOOL(GameSettings, $var, SettingFlags({$flags}), $def,
SDT_VAR = SDT_VAR (GameSettings, $var, $type, SettingFlags({$flags}), $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $def_cb, $range_cb, $from, $to, $cat, $extra, $startup),
[validation]
SDTG_VAR = static_assert($max <= MAX_$type, "Maximum value for $var exceeds storage size");
SDT_VAR = static_assert($max <= MAX_$type, "Maximum value for GameSettings.$var exceeds storage size");
SDTG_VAR = static_assert(ConvertEnumClass($max) <= MAX_$type, "Maximum value for $var exceeds storage size");
SDT_VAR = static_assert(ConvertEnumClass($max) <= MAX_$type, "Maximum value for GameSettings.$var exceeds storage size");
[defaults]
flags =

View File

@@ -23,7 +23,7 @@ SDT_BOOL = SDT_BOOL(GameSettings, $var, SettingFlags({$flags}), $def,
SDT_VAR = SDT_VAR (GameSettings, $var, $type, SettingFlags({$flags}), $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $def_cb, $range_cb, $from, $to, $cat, $extra, $startup),
[validation]
SDT_VAR = static_assert($max <= MAX_$type, "Maximum value for GameSettings.$var exceeds storage size");
SDT_VAR = static_assert(ConvertEnumClass($max) <= MAX_$type, "Maximum value for GameSettings.$var exceeds storage size");
[defaults]
flags =

View File

@@ -34,9 +34,9 @@ SDT_OMANY = SDT_OMANY(GameSettings, $var, $type, SettingFlags({$flags}), $def,
SDT_VAR = SDT_VAR(GameSettings, $var, $type, SettingFlags({$flags}), $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $def_cb, $range_cb, $from, $to, $cat, $extra, $startup),
[validation]
SDTG_VAR = static_assert($max <= MAX_$type, "Maximum value for $var exceeds storage size");
SDT_OMANY = static_assert($max <= MAX_$type, "Maximum value for GameSettings.$var exceeds storage size");
SDT_VAR = static_assert($max <= MAX_$type, "Maximum value for GameSettings.$var exceeds storage size");
SDTG_VAR = static_assert(ConvertEnumClass($max) <= MAX_$type, "Maximum value for $var exceeds storage size");
SDT_OMANY = static_assert(ConvertEnumClass($max) <= MAX_$type, "Maximum value for GameSettings.$var exceeds storage size");
SDT_VAR = static_assert(ConvertEnumClass($max) <= MAX_$type, "Maximum value for GameSettings.$var exceeds storage size");
[defaults]
flags =

View File

@@ -30,8 +30,8 @@ SDTC_OMANY = SDTC_OMANY( $var, $type, SettingFlags({$flags}), $def,
SDTC_VAR = SDTC_VAR( $var, $type, SettingFlags({$flags}), $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $def_cb, $range_cb, $from, $to, $cat, $extra, $startup),
[validation]
SDTC_OMANY = static_assert($max <= MAX_$type, "Maximum value for $var exceeds storage size");
SDTC_VAR = static_assert($max <= MAX_$type, "Maximum value for $var exceeds storage size");
SDTC_OMANY = static_assert(ConvertEnumClass($max) <= MAX_$type, "Maximum value for $var exceeds storage size");
SDTC_VAR = static_assert(ConvertEnumClass($max) <= MAX_$type, "Maximum value for $var exceeds storage size");
[defaults]
flags =
@@ -268,9 +268,9 @@ cat = SC_EXPERT
var = gui.zoom_min
type = SLE_UINT8
flags = SettingFlag::NotInSave, SettingFlag::NoNetworkSync, SettingFlag::GuiDropdown
def = to_underlying(ZoomLevel::Min)
min = to_underlying(ZoomLevel::Min)
max = to_underlying(ZoomLevel::Normal)
def = ZoomLevel::Min
min = ZoomLevel::Min
max = ZoomLevel::Normal
str = STR_CONFIG_SETTING_ZOOM_MIN
strhelp = STR_CONFIG_SETTING_ZOOM_MIN_HELPTEXT
strval = STR_CONFIG_SETTING_ZOOM_LVL_MIN
@@ -281,9 +281,9 @@ startup = true
var = gui.zoom_max
type = SLE_UINT8
flags = SettingFlag::NotInSave, SettingFlag::NoNetworkSync, SettingFlag::GuiDropdown
def = to_underlying(ZoomLevel::Max)
min = to_underlying(ZoomLevel::Out2x)
max = to_underlying(ZoomLevel::Max)
def = ZoomLevel::Max
min = ZoomLevel::Out2x
max = ZoomLevel::Max
str = STR_CONFIG_SETTING_ZOOM_MAX
strhelp = STR_CONFIG_SETTING_ZOOM_MAX_HELPTEXT
strval = STR_CONFIG_SETTING_ZOOM_LVL_OUT_2X
@@ -294,9 +294,9 @@ startup = true
var = gui.sprite_zoom_min
type = SLE_UINT8
flags = SettingFlag::NotInSave, SettingFlag::NoNetworkSync, SettingFlag::GuiDropdown
def = to_underlying(ZoomLevel::Min)
min = to_underlying(ZoomLevel::Min)
max = to_underlying(ZoomLevel::Normal)
def = ZoomLevel::Min
min = ZoomLevel::Min
max = ZoomLevel::Normal
str = STR_CONFIG_SETTING_SPRITE_ZOOM_MIN
strhelp = STR_CONFIG_SETTING_SPRITE_ZOOM_MIN_HELPTEXT
strval = STR_CONFIG_SETTING_SPRITE_ZOOM_LVL_MIN

View File

@@ -15,7 +15,7 @@ static const SettingVariant _linkgraph_settings_table[] = {
SDT_VAR = SDT_VAR(GameSettings, $var, $type, SettingFlags({$flags}), $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $def_cb, $range_cb, $from, $to, $cat, $extra, $startup),
[validation]
SDT_VAR = static_assert($max <= MAX_$type, "Maximum value for GameSettings.$var exceeds storage size");
SDT_VAR = static_assert(ConvertEnumClass($max) <= MAX_$type, "Maximum value for GameSettings.$var exceeds storage size");
[defaults]
flags =

View File

@@ -26,8 +26,8 @@ SDT_OMANY = SDT_OMANY(GameSettings, $var, $type, SettingFlags({$flags}), $def,
SDT_SSTR = SDT_SSTR(GameSettings, $var, $type, SettingFlags({$flags}), $def, $pre_cb, $post_cb, $from, $to, $cat, $extra, $startup),
[validation]
SDTG_OMANY = static_assert($max <= MAX_$type, "Maximum value for $var exceeds storage size");
SDT_OMANY = static_assert($max <= MAX_$type, "Maximum value for GameSettings.$var exceeds storage size");
SDTG_OMANY = static_assert(ConvertEnumClass($max) <= MAX_$type, "Maximum value for $var exceeds storage size");
SDT_OMANY = static_assert(ConvertEnumClass($max) <= MAX_$type, "Maximum value for GameSettings.$var exceeds storage size");
[defaults]
flags =

View File

@@ -33,8 +33,8 @@ SDTG_BOOL = SDTG_BOOL($name, SettingFlags({$flags}), $var, $def,
SDTG_VAR = SDTG_VAR($name, $type, SettingFlags({$flags}), $var, $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $def_cb, $range_cb, $from, $to, $cat, $extra, $startup),
[validation]
SDTG_VAR = static_assert($max <= MAX_$type, "Maximum value for $var exceeds storage size");
SDTG_OMANY = static_assert($max <= MAX_$type, "Maximum value for $var exceeds storage size");
SDTG_VAR = static_assert(ConvertEnumClass($max) <= MAX_$type, "Maximum value for $var exceeds storage size");
SDTG_OMANY = static_assert(ConvertEnumClass($max) <= MAX_$type, "Maximum value for $var exceeds storage size");
[defaults]
flags = SettingFlag::NotInSave, SettingFlag::NoNetworkSync

View File

@@ -17,7 +17,7 @@ SDTC_LIST = SDTC_LIST( $var, $type, SettingFlags({$flags}), $def,
SDTC_VAR = SDTC_VAR( $var, $type, SettingFlags({$flags}), $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $def_cb, $range_cb, $from, $to, $cat, $extra, $startup),
[validation]
SDTC_VAR = static_assert($max <= MAX_$type, "Maximum value for $var exceeds storage size");
SDTC_VAR = static_assert(ConvertEnumClass($max) <= MAX_$type, "Maximum value for $var exceeds storage size");
[defaults]
flags =

View File

@@ -19,7 +19,7 @@ SDTC_OMANY = SDTC_OMANY( $var, $type, SettingFlags({$flags}), $def,
SDTC_SSTR = SDTC_SSTR( $var, $type, SettingFlags({$flags}), $def, $length, $pre_cb, $post_cb, $from, $to, $cat, $extra, $startup),
[validation]
SDTC_OMANY = static_assert($max <= MAX_$type, "Maximum value for $var exceeds storage size");
SDTC_OMANY = static_assert(ConvertEnumClass($max) <= MAX_$type, "Maximum value for $var exceeds storage size");
[defaults]
flags =

View File

@@ -21,8 +21,8 @@ SDTC_OMANY = SDTC_OMANY( $var, $type, SettingFlags({$flags}), $def,
SDTC_VAR = SDTC_VAR( $var, $type, SettingFlags({$flags}), $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $def_cb, $range_cb, $from, $to, $cat, $extra, $startup),
[validation]
SDTC_OMANY = static_assert($max <= MAX_$type, "Maximum value for $var exceeds storage size");
SDTC_VAR = static_assert($max <= MAX_$type, "Maximum value for $var exceeds storage size");
SDTC_OMANY = static_assert(ConvertEnumClass($max) <= MAX_$type, "Maximum value for $var exceeds storage size");
SDTC_VAR = static_assert(ConvertEnumClass($max) <= MAX_$type, "Maximum value for $var exceeds storage size");
[defaults]
flags =

View File

@@ -16,7 +16,7 @@ static const SettingVariant _news_display_settings_table[] = {
SDTC_OMANY = SDTC_OMANY( $var, $type, SettingFlags({$flags}), $def, $max, $full, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $def_cb, $from, $to, $cat, $extra, $startup),
[validation]
SDTC_OMANY = static_assert($max <= MAX_$type, "Maximum value for $var exceeds storage size");
SDTC_OMANY = static_assert(ConvertEnumClass($max) <= MAX_$type, "Maximum value for $var exceeds storage size");
[defaults]
flags =

View File

@@ -39,11 +39,11 @@ SDT_OMANY = SDT_OMANY(GameSettings, $var, $type, SettingFlags({$flags}), $de
SDT_VAR = SDT_VAR(GameSettings, $var, $type, SettingFlags({$flags}), $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $def_cb, $range_cb, $from, $to, $cat, $extra, $startup),
[validation]
SDTG_VAR = static_assert($max <= MAX_$type, "Maximum value for $var exceeds storage size");
SDTG_OMANY = static_assert($max <= MAX_$type, "Maximum value for $var exceeds storage size");
SDTC_OMANY = static_assert($max <= MAX_$type, "Maximum value for $var exceeds storage size");
SDT_OMANY = static_assert($max <= MAX_$type, "Maximum value for GameSettings.$var exceeds storage size");
SDT_VAR = static_assert($max <= MAX_$type, "Maximum value for GameSettings.$var exceeds storage size");
SDTG_VAR = static_assert(ConvertEnumClass($max) <= MAX_$type, "Maximum value for $var exceeds storage size");
SDTG_OMANY = static_assert(ConvertEnumClass($max) <= MAX_$type, "Maximum value for $var exceeds storage size");
SDTC_OMANY = static_assert(ConvertEnumClass($max) <= MAX_$type, "Maximum value for $var exceeds storage size");
SDT_OMANY = static_assert(ConvertEnumClass($max) <= MAX_$type, "Maximum value for GameSettings.$var exceeds storage size");
SDT_VAR = static_assert(ConvertEnumClass($max) <= MAX_$type, "Maximum value for GameSettings.$var exceeds storage size");
[defaults]
flags =

View File

@@ -16,7 +16,7 @@ SDT_BOOL = SDT_BOOL(GameSettings, $var, SettingFlags({$flags}), $def,
SDT_VAR = SDT_VAR(GameSettings, $var, $type, SettingFlags({$flags}), $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $def_cb, $range_cb, $from, $to, $cat, $extra, $startup),
[validation]
SDT_VAR = static_assert($max <= MAX_$type, "Maximum value for GameSettings.$var exceeds storage size");
SDT_VAR = static_assert(ConvertEnumClass($max) <= MAX_$type, "Maximum value for GameSettings.$var exceeds storage size");
[defaults]
flags =

View File

@@ -18,8 +18,8 @@ SDT_OMANY = SDT_OMANY(GameSettings, $var, $type, SettingFlags({$flags}), $def,
SDT_VAR = SDT_VAR(GameSettings, $var, $type, SettingFlags({$flags}), $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $def_cb, $range_cb, $from, $to, $cat, $extra, $startup),
[validation]
SDT_OMANY = static_assert($max <= MAX_$type, "Maximum value for GameSettings.$var exceeds storage size");
SDT_VAR = static_assert($max <= MAX_$type, "Maximum value for GameSettings.$var exceeds storage size");
SDT_OMANY = static_assert(ConvertEnumClass($max) <= MAX_$type, "Maximum value for GameSettings.$var exceeds storage size");
SDT_VAR = static_assert(ConvertEnumClass($max) <= MAX_$type, "Maximum value for GameSettings.$var exceeds storage size");
[defaults]
flags =

View File

@@ -21,7 +21,7 @@ SDTG_BOOL = SDTG_BOOL($name, SettingFlags({$flags}), $var, $def,
SDTG_VAR = SDTG_VAR($name, $type, SettingFlags({$flags}), $var, $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $def_cb, $range_cb, $from, $to, $cat, $extra, $startup),
[validation]
SDTG_VAR = static_assert($max <= MAX_$type, "Maximum value for $var exceeds storage size");
SDTG_VAR = static_assert(ConvertEnumClass($max) <= MAX_$type, "Maximum value for $var exceeds storage size");
[defaults]
flags = SettingFlag::NotInSave, SettingFlag::NoNetworkSync

View File

@@ -17,7 +17,7 @@ SDT_BOOL = SDT_BOOL(WindowDesc, $var, SettingFlags({$flags}), $def,
SDT_VAR = SDT_VAR(WindowDesc, $var, $type, SettingFlags({$flags}), $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $def_cb, $range_cb, $from, $to, $cat, $extra, $startup),
[validation]
SDT_VAR = static_assert($max <= MAX_$type, "Maximum value for WindowDesc.$var exceeds storage size");
SDT_VAR = static_assert(ConvertEnumClass($max) <= MAX_$type, "Maximum value for WindowDesc.$var exceeds storage size");
[defaults]
flags = SettingFlag::NotInSave, SettingFlag::NoNetworkSync

View File

@@ -23,8 +23,8 @@ SDT_OMANY = SDT_OMANY(GameSettings, $var, $type, SettingFlags({$flags}), $def,
SDT_VAR = SDT_VAR(GameSettings, $var, $type, SettingFlags({$flags}), $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $def_cb, $range_cb, $from, $to, $cat, $extra, $startup),
[validation]
SDT_OMANY = static_assert($max <= MAX_$type, "Maximum value for GameSettings.$var exceeds storage size");
SDT_VAR = static_assert($max <= MAX_$type, "Maximum value for GameSettings.$var exceeds storage size");
SDT_OMANY = static_assert(ConvertEnumClass($max) <= MAX_$type, "Maximum value for GameSettings.$var exceeds storage size");
SDT_VAR = static_assert(ConvertEnumClass($max) <= MAX_$type, "Maximum value for GameSettings.$var exceeds storage size");
[defaults]
flags =