diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index 8863e03191..554079672a 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -2214,7 +2214,7 @@ public: case WID_CL_SERVER_NAME_EDIT: { if (!_network_server) break; - SetSettingValue(GetSettingFromName("network.server_name"), StrEmpty(str) ? "Unnamed Server" : str); + SetSettingValue(GetSettingFromName("network.server_name")->AsStringSetting(), StrEmpty(str) ? "Unnamed Server" : str); this->InvalidateData(); break; } @@ -2223,7 +2223,7 @@ public: std::string client_name(str); if (!NetworkValidateClientName(client_name)) break; - SetSettingValue(GetSettingFromName("network.client_name"), client_name.c_str()); + SetSettingValue(GetSettingFromName("network.client_name")->AsStringSetting(), client_name.c_str()); this->InvalidateData(); break; } diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index 83b5ed197a..012a7d7676 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -600,7 +600,7 @@ typedef SaveLoad SaveLoadGlobVarList; * @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field * @note In general, it is better to use one of the SLE_* macros below. */ -#define SLE_GENERAL_X(cmd, base, variable, type, length, from, to, extver) {false, cmd, type, length, from, to, (void*)cpp_offsetof(base, variable), cpp_sizeof(base, variable), extver} +#define SLE_GENERAL_X(cmd, base, variable, type, length, from, to, extver) SaveLoad {false, cmd, type, length, from, to, (void*)cpp_offsetof(base, variable), cpp_sizeof(base, variable), extver} #define SLE_GENERAL(cmd, base, variable, type, length, from, to) SLE_GENERAL_X(cmd, base, variable, type, length, from, to, SlXvFeatureTest()) /** @@ -826,7 +826,7 @@ typedef SaveLoad SaveLoadGlobVarList; * @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field * @note In general, it is better to use one of the SLEG_* macros below. */ -#define SLEG_GENERAL_X(cmd, variable, type, length, from, to, extver) {true, cmd, type, length, from, to, (void*)&variable, sizeof(variable), extver} +#define SLEG_GENERAL_X(cmd, variable, type, length, from, to, extver) SaveLoad {true, cmd, type, length, from, to, (void*)&variable, sizeof(variable), extver} #define SLEG_GENERAL(cmd, variable, type, length, from, to) SLEG_GENERAL_X(cmd, variable, type, length, from, to, SlXvFeatureTest()) /** diff --git a/src/script/api/script_gamesettings.cpp b/src/script/api/script_gamesettings.cpp index f96048e153..8b0459367e 100644 --- a/src/script/api/script_gamesettings.cpp +++ b/src/script/api/script_gamesettings.cpp @@ -18,7 +18,7 @@ /* static */ bool ScriptGameSettings::IsValid(const char *setting) { const SettingDesc *sd = GetSettingFromName(setting); - return sd != nullptr && sd->cmd != SDT_STDSTRING; + return sd != nullptr && sd->IsIntSetting(); } /* static */ int32 ScriptGameSettings::GetValue(const char *setting) @@ -28,8 +28,6 @@ const SettingDesc *sd = GetSettingFromName(setting); void *ptr = GetVariableAddress(&_settings_game, &sd->save); - if (sd->cmd == SDT_BOOLX) return *(bool*)ptr; - return (int32)ReadValue(ptr, sd->save.conv); } @@ -40,7 +38,6 @@ const SettingDesc *sd = GetSettingFromName(setting); if ((sd->save.conv & SLF_NO_NETWORK_SYNC) != 0) return false; - if (sd->cmd != SDT_BOOLX && sd->cmd != SDT_NUMX) return false; return ScriptObject::DoCommand(0, GetSettingIndex(sd), value, CMD_CHANGE_SETTING); } diff --git a/src/settings.cpp b/src/settings.cpp index c744a7d7a5..2949bd955b 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -116,7 +116,7 @@ static bool IsSignedVarMemType(VarType vt); const SettingDesc *GetSettingDescription(uint index) { if (index >= _settings.size()) return nullptr; - return &_settings.begin()[index]; + return _settings.begin()[index].get(); } /** @@ -127,7 +127,7 @@ const SettingDesc *GetSettingDescription(uint index) static const SettingDesc *GetCompanySettingDescription(uint index) { if (index >= _company_settings.size()) return nullptr; - return &_company_settings.begin()[index]; + return _company_settings.begin()[index].get(); } /** @@ -143,31 +143,23 @@ static const char * const _list_group_names[] = { /** * Find the index value of a ONEofMANY type in a string separated by | + * @param str the current value of the setting for which a value needs found + * @param len length of the string * @param many full domain of values the ONEofMANY setting can have - * @param one the current value of the setting for which a value needs found - * @param onelen force calculation of the *one parameter * @return the integer index of the full-list, or -1 if not found */ -static size_t LookupOneOfMany(const char *many, const char *one, size_t onelen = 0) +size_t OneOfManySettingDesc::ParseSingleValue(const char *str, size_t len, const std::vector &many) { - const char *s; - size_t idx; - - if (onelen == 0) onelen = strlen(one); - /* check if it's an integer */ - if (*one >= '0' && *one <= '9') return strtoul(one, nullptr, 0); + if (isdigit(*str)) return strtoul(str, nullptr, 0); - idx = 0; - for (;;) { - /* find end of item */ - s = many; - while (*s != '|' && *s != 0) s++; - if ((size_t)(s - many) == onelen && !memcmp(one, many, onelen)) return idx; - if (*s == 0) return (size_t)-1; - many = s + 1; + size_t idx = 0; + for (auto one : many) { + if (one.size() == len && strncmp(one.c_str(), str, len) == 0) return idx; idx++; } + + return (size_t)-1; } /** @@ -177,7 +169,7 @@ static size_t LookupOneOfMany(const char *many, const char *one, size_t onelen = * of separated by a whitespace,tab or | character * @return the 'fully' set integer, or -1 if a set is not found */ -static size_t LookupManyOfMany(const char *many, const char *str) +static size_t LookupManyOfMany(const std::vector &many, const char *str) { const char *s; size_t r; @@ -191,7 +183,7 @@ static size_t LookupManyOfMany(const char *many, const char *str) s = str; while (*s != 0 && *s != ' ' && *s != '\t' && *s != '|') s++; - r = LookupOneOfMany(many, str, s - str); + r = OneOfManySettingDesc::ParseSingleValue(str, s - str, many); if (r == (size_t)-1) return r; SetBit(res, (uint8)r); // value found, set it @@ -301,13 +293,13 @@ static bool LoadIntList(const char *str, void *array, int nelems, VarType type) * @param nelems the number of elements the array holds. * @param type the type of elements the array holds (eg INT8, UINT16, etc.) */ -static void MakeIntList(char *buf, const char *last, const void *array, int nelems, VarType type) +void ListSettingDesc::FormatValue(char *buf, const char *last, const void *object) const { + const byte *p = static_cast(GetVariableAddress(object, &this->save)); int i, v = 0; - const byte *p = (const byte *)array; - for (i = 0; i != nelems; i++) { - switch (GetVarMemType(type)) { + for (i = 0; i != this->save.length; i++) { + switch (GetVarMemType(this->save.conv)) { case SLE_VAR_BL: case SLE_VAR_I8: v = *(const int8 *)p; p += 1; break; case SLE_VAR_U8: v = *(const uint8 *)p; p += 1; break; @@ -317,9 +309,9 @@ static void MakeIntList(char *buf, const char *last, const void *array, int nele case SLE_VAR_U32: v = *(const uint32 *)p; p += 4; break; default: NOT_REACHED(); } - if (IsSignedVarMemType(type)) { + if (IsSignedVarMemType(this->save.conv)) { buf += seprintf(buf, last, (i == 0) ? "%d" : ",%d", v); - } else if (type & SLF_HEX) { + } else if (this->save.conv & SLF_HEX) { buf += seprintf(buf, last, (i == 0) ? "0x%X" : ",0x%X", v); } else { buf += seprintf(buf, last, (i == 0) ? "%u" : ",%u", v); @@ -327,141 +319,95 @@ static void MakeIntList(char *buf, const char *last, const void *array, int nele } } -/** - * Convert a ONEofMANY structure to a string representation. - * @param buf output buffer where the string-representation will be stored - * @param last last item to write to in the output buffer - * @param many the full-domain string of possible values - * @param id the value of the variable and whose string-representation must be found - */ -static void MakeOneOfMany(char *buf, const char *last, const char *many, int id) +char *OneOfManySettingDesc::FormatSingleValue(char *buf, const char *last, uint id) const { - int orig_id = id; - - /* Look for the id'th element */ - while (--id >= 0) { - for (; *many != '|'; many++) { - if (*many == '\0') { // not found - seprintf(buf, last, "%d", orig_id); - return; - } - } - many++; // pass the |-character + if (id >= this->many.size()) { + return buf + seprintf(buf, last, "%d", id); } + return strecpy(buf, this->many[id].c_str(), last); +} - /* copy string until next item (|) or the end of the list if this is the last one */ - while (*many != '\0' && *many != '|' && buf < last) *buf++ = *many++; - *buf = '\0'; +void OneOfManySettingDesc::FormatValue(char *buf, const char *last, const void *object) const +{ + uint id = (uint)this->Read(object); + this->FormatSingleValue(buf, last, id); +} + +void ManyOfManySettingDesc::FormatValue(char *buf, const char *last, const void *object) const +{ + uint bitmask = (uint)this->Read(object); + uint id = 0; + bool first = true; + FOR_EACH_SET_BIT(id, bitmask) { + if (!first) buf = strecpy(buf, "|", last); + buf = this->FormatSingleValue(buf, last, id); + first = false; + } } /** - * Convert a MANYofMANY structure to a string representation. - * @param buf output buffer where the string-representation will be stored - * @param last last item to write to in the output buffer - * @param many the full-domain string of possible values - * @param x the value of the variable and whose string-representation must - * be found in the bitmasked many string + * Convert a string representation (external) of an integer-like setting to an integer. + * @param str Input string that will be parsed based on the type of desc. + * @return The value from the parse string, or the default value of the setting. */ -static void MakeManyOfMany(char *buf, const char *last, const char *many, uint32 x) +size_t IntSettingDesc::ParseValue(const char *str) const { - const char *start; - int i = 0; - bool init = true; - - for (; x != 0; x >>= 1, i++) { - start = many; - while (*many != 0 && *many != '|') many++; // advance to the next element - - if (HasBit(x, 0)) { // item found, copy it - if (!init) buf += seprintf(buf, last, "|"); - init = false; - if (start == many) { - buf += seprintf(buf, last, "%d", i); - } else { - memcpy(buf, start, many - start); - buf += many - start; - } - } - - if (*many == '|') many++; + char *end; + size_t val = strtoul(str, &end, 0); + if (end == str) { + ErrorMessageData msg(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_VALUE); + msg.SetDParamStr(0, str); + msg.SetDParamStr(1, this->name); + _settings_error_list.push_back(msg); + return this->def; } - - *buf = '\0'; + if (*end != '\0') { + ErrorMessageData msg(STR_CONFIG_ERROR, STR_CONFIG_ERROR_TRAILING_CHARACTERS); + msg.SetDParamStr(0, this->name); + _settings_error_list.push_back(msg); + } + return val; } -/** - * Convert a string representation (external) of a setting to the internal rep. - * @param desc SettingDesc struct that holds all information about the variable - * @param orig_str input string that will be parsed based on the type of desc - * @return return the parsed value of the setting - */ -static const void *StringToVal(const SettingDesc *desc, const char *orig_str) +size_t OneOfManySettingDesc::ParseValue(const char *str) const { - const char *str = orig_str == nullptr ? "" : orig_str; + size_t r = OneOfManySettingDesc::ParseSingleValue(str, strlen(str), this->many); + /* if the first attempt of conversion from string to the appropriate value fails, + * look if we have defined a converter from old value to new value. */ + if (r == (size_t)-1 && this->many_cnvt != nullptr) r = this->many_cnvt(str); + if (r != (size_t)-1) return r; // and here goes converted value - switch (desc->cmd) { - case SDT_NUMX: { - char *end; - size_t val = strtoul(str, &end, 0); - if (end == str) { - ErrorMessageData msg(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_VALUE); - msg.SetDParamStr(0, str); - msg.SetDParamStr(1, desc->name); - _settings_error_list.push_back(msg); - return desc->def; - } - if (*end != '\0') { - ErrorMessageData msg(STR_CONFIG_ERROR, STR_CONFIG_ERROR_TRAILING_CHARACTERS); - msg.SetDParamStr(0, desc->name); - _settings_error_list.push_back(msg); - } - return (void*)val; - } - - case SDT_ONEOFMANY: { - size_t r = LookupOneOfMany(desc->many, str); - /* if the first attempt of conversion from string to the appropriate value fails, - * look if we have defined a converter from old value to new value. */ - if (r == (size_t)-1 && desc->proc_cnvt != nullptr) r = desc->proc_cnvt(str); - if (r != (size_t)-1) return (void*)r; // and here goes converted value - - ErrorMessageData msg(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_VALUE); - msg.SetDParamStr(0, str); - msg.SetDParamStr(1, desc->name); - _settings_error_list.push_back(msg); - return desc->def; - } - - case SDT_MANYOFMANY: { - size_t r = LookupManyOfMany(desc->many, str); - if (r != (size_t)-1) return (void*)r; - ErrorMessageData msg(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_VALUE); - msg.SetDParamStr(0, str); - msg.SetDParamStr(1, desc->name); - _settings_error_list.push_back(msg); - return desc->def; - } - - case SDT_BOOLX: { - if (strcmp(str, "true") == 0 || strcmp(str, "on") == 0 || strcmp(str, "1") == 0) return (void*)true; - if (strcmp(str, "false") == 0 || strcmp(str, "off") == 0 || strcmp(str, "0") == 0) return (void*)false; - - ErrorMessageData msg(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_VALUE); - msg.SetDParamStr(0, str); - msg.SetDParamStr(1, desc->name); - _settings_error_list.push_back(msg); - return desc->def; - } - - case SDT_STDSTRING: return orig_str; - case SDT_INTLIST: return str; - default: break; - } - - return nullptr; + ErrorMessageData msg(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_VALUE); + msg.SetDParamStr(0, str); + msg.SetDParamStr(1, this->name); + _settings_error_list.push_back(msg); + return this->def; } -static bool ValidateEnumSetting(const SettingDescBase *sdb, int32 val) +size_t ManyOfManySettingDesc::ParseValue(const char *str) const +{ + size_t r = LookupManyOfMany(this->many, str); + if (r != (size_t)-1) return r; + ErrorMessageData msg(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_VALUE); + msg.SetDParamStr(0, str); + msg.SetDParamStr(1, this->name); + _settings_error_list.push_back(msg); + return this->def; +} + +size_t BoolSettingDesc::ParseValue(const char *str) const +{ + if (strcmp(str, "true") == 0 || strcmp(str, "on") == 0 || strcmp(str, "1") == 0) return true; + if (strcmp(str, "false") == 0 || strcmp(str, "off") == 0 || strcmp(str, "0") == 0) return false; + + ErrorMessageData msg(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_VALUE); + msg.SetDParamStr(0, str); + msg.SetDParamStr(1, this->name); + _settings_error_list.push_back(msg); + return this->def; +} + +static bool ValidateEnumSetting(const IntSettingDesc *sdb, int32 val) { for (const SettingDescEnumEntry *enumlist = sdb->enumlist; enumlist != nullptr && enumlist->str != STR_NULL; enumlist++) { if (enumlist->val == val) { @@ -472,102 +418,98 @@ static bool ValidateEnumSetting(const SettingDescBase *sdb, int32 val) } /** - * Set the value of a setting and if needed clamp the value to - * the preset minimum and maximum. - * @param ptr the variable itself - * @param sd pointer to the 'information'-database of the variable - * @param val signed long version of the new value - * @pre SettingDesc is of type SDT_BOOLX, SDT_NUMX, - * SDT_ONEOFMANY or SDT_MANYOFMANY. Other types are not supported as of now + * Set the value of a setting and if needed clamp the value to the preset minimum and maximum. + * @param object The object the setting is to be saved in. + * @param val Signed version of the new value. */ -static void Write_ValidateSetting(void *ptr, const SettingDesc *sd, int32 val) +void IntSettingDesc::Write_ValidateSetting(const void *object, int32 val) const { - const SettingDesc *sdb = sd; + void *ptr = GetVariableAddress(object, &this->save); - if (sdb->cmd != SDT_BOOLX && - sdb->cmd != SDT_NUMX && - sdb->cmd != SDT_ONEOFMANY && - sdb->cmd != SDT_MANYOFMANY) { - return; - } - - /* We cannot know the maximum value of a bitset variable, so just have faith */ - if (sdb->cmd != SDT_MANYOFMANY) { - /* We need to take special care of the uint32 type as we receive from the function - * a signed integer. While here also bail out on 64-bit settings as those are not - * supported. Unsigned 8 and 16-bit variables are safe since they fit into a signed - * 32-bit variable - * TODO: Support 64-bit settings/variables */ - switch (GetVarMemType(sd->save.conv)) { - case SLE_VAR_NULL: return; - case SLE_VAR_BL: - case SLE_VAR_I8: - case SLE_VAR_U8: - case SLE_VAR_I16: - case SLE_VAR_U16: - case SLE_VAR_I32: { - /* Override the minimum value. No value below sdb->min, except special value 0 */ - if (!(sdb->flags & SGF_0ISDISABLED) || val != 0) { - if (sdb->flags & SGF_ENUM) { - if (!ValidateEnumSetting(sdb, val)) val = (int32)(size_t)sdb->def; - } else if (!(sdb->flags & SGF_MULTISTRING)) { - /* Clamp value-type setting to its valid range */ - val = Clamp(val, sdb->min, sdb->max); - } else if (val < sdb->min || val > (int32)sdb->max) { - /* Reset invalid discrete setting (where different values change gameplay) to its default value */ - val = (int32)(size_t)sdb->def; - } + /* We need to take special care of the uint32 type as we receive from the function + * a signed integer. While here also bail out on 64-bit settings as those are not + * supported. Unsigned 8 and 16-bit variables are safe since they fit into a signed + * 32-bit variable + * TODO: Support 64-bit settings/variables; requires 64 bit over command protocol! */ + switch (GetVarMemType(this->save.conv)) { + case SLE_VAR_NULL: return; + case SLE_VAR_BL: + case SLE_VAR_I8: + case SLE_VAR_U8: + case SLE_VAR_I16: + case SLE_VAR_U16: + case SLE_VAR_I32: { + /* Override the minimum value. No value below this->min, except special value 0 */ + if (!(this->flags & SGF_0ISDISABLED) || val != 0) { + if (this->flags & SGF_ENUM) { + if (!ValidateEnumSetting(this, val)) val = (int32)(size_t)this->def; + } else if (!(this->flags & SGF_MULTISTRING)) { + /* Clamp value-type setting to its valid range */ + val = Clamp(val, this->min, this->max); + } else if (val < this->min || val > (int32)this->max) { + /* Reset invalid discrete setting (where different values change gameplay) to its default value */ + val = this->def; } - break; } - case SLE_VAR_U32: { - /* Override the minimum value. No value below sdb->min, except special value 0 */ - uint32 uval = (uint32)val; - if (!(sdb->flags & SGF_0ISDISABLED) || uval != 0) { - if (sdb->flags & SGF_ENUM) { - if (!ValidateEnumSetting(sdb, val)) uval = (uint32)(size_t)sdb->def; - } else if (!(sdb->flags & SGF_MULTISTRING)) { - /* Clamp value-type setting to its valid range */ - uval = ClampU(uval, sdb->min, sdb->max); - } else if (uval < (uint)sdb->min || uval > sdb->max) { - /* Reset invalid discrete setting to its default value */ - uval = (uint32)(size_t)sdb->def; - } - } - WriteValue(ptr, SLE_VAR_U32, (int64)uval); - return; - } - case SLE_VAR_I64: - case SLE_VAR_U64: - default: NOT_REACHED(); + break; } + case SLE_VAR_U32: { + /* Override the minimum value. No value below this->min, except special value 0 */ + uint32 uval = (uint32)val; + if (!(this->flags & SGF_0ISDISABLED) || uval != 0) { + if (this->flags & SGF_ENUM) { + if (!ValidateEnumSetting(this, val)) uval = (uint32)(size_t)this->def; + } else if (!(this->flags & SGF_MULTISTRING)) { + /* Clamp value-type setting to its valid range */ + uval = ClampU(uval, this->min, this->max); + } else if (uval < (uint)this->min || uval > this->max) { + /* Reset invalid discrete setting to its default value */ + uval = (uint32)this->def; + } + } + WriteValue(ptr, SLE_VAR_U32, (int64)uval); + return; + } + case SLE_VAR_I64: + case SLE_VAR_U64: + default: NOT_REACHED(); } - WriteValue(ptr, sd->save.conv, (int64)val); + WriteValue(ptr, this->save.conv, (int64)val); +} + +/** + * Read the integer from the the actual setting. + * @param object The object the setting is to be saved in. + * @return The value of the saved integer. + */ +int32 IntSettingDesc::Read(const void *object) const +{ + void *ptr = GetVariableAddress(object, &this->save); + return (int32)ReadValue(ptr, this->save.conv); } /** * Set the string value of a setting. - * @param ptr Pointer to the std::string. - * @param sd Pointer to the information for the conversions and limitations to apply. - * @param p The string to save. + * @param object The object the setting is to be saved in. + * @param str The string to save. */ -static void Write_ValidateStdString(void *ptr, const SettingDesc *sd, const char *p) +void StringSettingDesc::Write_ValidateSetting(const void *object, const char *str) const { - std::string *dst = reinterpret_cast(ptr); + std::string *dst = reinterpret_cast(GetVariableAddress(object, &this->save)); - switch (GetVarMemType(sd->save.conv)) { + switch (GetVarMemType(this->save.conv)) { case SLE_VAR_STR: case SLE_VAR_STRQ: - if (p != nullptr) { - if (sd->max != 0 && strlen(p) >= sd->max) { + if (str != nullptr) { + if (this->max_length != 0 && strlen(str) >= this->max_length) { /* In case a maximum length is imposed by the setting, the length * includes the '\0' termination for network transfer purposes. * Also ensure the string is valid after chopping of some bytes. */ - std::string str(p, sd->max - 1); - dst->assign(str_validate(str, SVS_NONE)); + std::string stdstr(str, this->max_length - 1); + dst->assign(str_validate(stdstr, SVS_NONE)); } else { - dst->assign(p); + dst->assign(str); } } else { dst->clear(); @@ -578,6 +520,16 @@ static void Write_ValidateStdString(void *ptr, const SettingDesc *sd, const char } } +/** + * Read the string from the the actual setting. + * @param object The object the setting is to be saved in. + * @return The value of the saved string. + */ +const std::string &StringSettingDesc::Read(const void *object) const +{ + return *reinterpret_cast(GetVariableAddress(object, &this->save)); +} + /** * Load values from a group of an IniFile structure into the internal representation * @param ini pointer to IniFile structure that holds administrative information @@ -593,17 +545,14 @@ static void IniLoadSettings(IniFile *ini, const SettingTable &settings_table, co IniGroup *group_def = ini->GetGroup(grpname); for (auto &sd : settings_table) { - const SettingDesc *sdb = &sd; - const SaveLoad *sld = &sd.save; - - if (!SlIsObjectCurrentlyValid(sld->version_from, sld->version_to, sld->ext_feature_test)) continue; - if (sd.startup != only_startup) continue; + if (!SlIsObjectCurrentlyValid(sd->save.version_from, sd->save.version_to, sd->save.ext_feature_test)) continue; + if (sd->startup != only_startup) continue; IniItem *item; - if (sdb->flags & SGF_NO_NEWGAME) { + if (sd->flags & SGF_NO_NEWGAME) { item = nullptr; } else { /* For settings.xx.yy load the settings from [xx] yy = ? */ - std::string s{ sdb->name }; + std::string s{ sd->name }; auto sc = s.find('.'); if (sc != std::string::npos) { group = ini->GetGroup(s.substr(0, sc)); @@ -626,36 +575,33 @@ static void IniLoadSettings(IniFile *ini, const SettingTable &settings_table, co } } - const void *p = (item == nullptr) ? sdb->def : StringToVal(sdb, item->value.has_value() ? item->value->c_str() : nullptr); - void *ptr = GetVariableAddress(object, sld); + sd->ParseValue(item, object); + } +} - switch (sdb->cmd) { - case SDT_BOOLX: // All four are various types of (integer) numbers - case SDT_NUMX: - case SDT_ONEOFMANY: - case SDT_MANYOFMANY: - Write_ValidateSetting(ptr, &sd, (int32)(size_t)p); - break; +void IntSettingDesc::ParseValue(const IniItem *item, void *object) const +{ + size_t val = (item == nullptr) ? this->def : this->ParseValue(item->value.has_value() ? item->value->c_str() : ""); + this->Write_ValidateSetting(object, (int32)val); +} - case SDT_STDSTRING: - Write_ValidateStdString(ptr, &sd, (const char *)p); - break; +void StringSettingDesc::ParseValue(const IniItem *item, void *object) const +{ + const char *str = (item == nullptr) ? this->def : item->value.has_value() ? item->value->c_str() : nullptr; + this->Write_ValidateSetting(object, str); +} - case SDT_INTLIST: { - if (!LoadIntList((const char*)p, ptr, sld->length, GetVarMemType(sld->conv))) { - ErrorMessageData msg(STR_CONFIG_ERROR, STR_CONFIG_ERROR_ARRAY); - msg.SetDParamStr(0, sdb->name); - _settings_error_list.push_back(msg); +void ListSettingDesc::ParseValue(const IniItem *item, void *object) const +{ + const char *str = (item == nullptr) ? this->def : item->value.has_value() ? item->value->c_str() : nullptr; + void *ptr = GetVariableAddress(object, &this->save); + if (!LoadIntList(str, ptr, this->save.length, GetVarMemType(this->save.conv))) { + ErrorMessageData msg(STR_CONFIG_ERROR, STR_CONFIG_ERROR_ARRAY); + msg.SetDParamStr(0, this->name); + _settings_error_list.push_back(msg); - /* Use default */ - LoadIntList((const char*)sdb->def, ptr, sld->length, GetVarMemType(sld->conv)); - } else if (sd.proc_cnvt != nullptr) { - sd.proc_cnvt((const char*)p); - } - break; - } - default: NOT_REACHED(); - } + /* Use default */ + LoadIntList(this->def, ptr, this->save.length, GetVarMemType(this->save.conv)); } } @@ -676,20 +622,16 @@ static void IniSaveSettings(IniFile *ini, const SettingTable &settings_table, co IniGroup *group_def = nullptr, *group; IniItem *item; char buf[512]; - void *ptr; for (auto &sd : settings_table) { - const SettingDesc *sdb = &sd; - const SaveLoad *sld = &sd.save; - /* If the setting is not saved to the configuration * file, just continue with the next setting */ - if (!SlIsObjectCurrentlyValid(sld->version_from, sld->version_to, sld->ext_feature_test)) continue; - if (sld->conv & SLF_NOT_IN_CONFIG) continue; - if (sdb->flags & SGF_NO_NEWGAME) continue; + if (!SlIsObjectCurrentlyValid(sd->save.version_from, sd->save.version_to, sd->save.ext_feature_test)) continue; + if (sd->save.conv & SLF_NOT_IN_CONFIG) continue; + if (sd->flags & SGF_NO_NEWGAME) continue; /* XXX - wtf is this?? (group override?) */ - std::string s{ sdb->name }; + std::string s{ sd->name }; auto sc = s.find('.'); if (sc != std::string::npos) { group = ini->GetGroup(s.substr(0, sc)); @@ -700,93 +642,70 @@ static void IniSaveSettings(IniFile *ini, const SettingTable &settings_table, co } item = group->GetItem(s, true); - ptr = GetVariableAddress(object, sld); - if (item->value.has_value()) { - /* check if the value is the same as the old value */ - const void *p = StringToVal(sdb, item->value->c_str()); + if (!item->value.has_value() || !sd->IsSameValue(item, object)) { + /* Value has changed, get the new value and put it into a buffer */ + sd->FormatValue(buf, lastof(buf), object); - /* The main type of a variable/setting is in bytes 8-15 - * The subtype (what kind of numbers do we have there) is in 0-7 */ - switch (sdb->cmd) { - case SDT_BOOLX: - case SDT_NUMX: - case SDT_ONEOFMANY: - case SDT_MANYOFMANY: - switch (GetVarMemType(sld->conv)) { - case SLE_VAR_BL: - if (*(bool*)ptr == (p != nullptr)) continue; - break; - - case SLE_VAR_I8: - case SLE_VAR_U8: - if (*(byte*)ptr == (byte)(size_t)p) continue; - break; - - case SLE_VAR_I16: - case SLE_VAR_U16: - if (*(uint16*)ptr == (uint16)(size_t)p) continue; - break; - - case SLE_VAR_I32: - case SLE_VAR_U32: - if (*(uint32*)ptr == (uint32)(size_t)p) continue; - break; - - default: NOT_REACHED(); - } - break; - - default: break; // Assume the other types are always changed - } + /* The value is different, that means we have to write it to the ini */ + item->value.emplace(buf); } - - /* Value has changed, get the new value and put it into a buffer */ - switch (sdb->cmd) { - case SDT_BOOLX: - case SDT_NUMX: - case SDT_ONEOFMANY: - case SDT_MANYOFMANY: { - uint32 i = (uint32)ReadValue(ptr, sld->conv); - - switch (sdb->cmd) { - case SDT_BOOLX: strecpy(buf, (i != 0) ? "true" : "false", lastof(buf)); break; - case SDT_NUMX: seprintf(buf, lastof(buf), IsSignedVarMemType(sld->conv) ? "%d" : (sld->conv & SLF_HEX) ? "%X" : "%u", i); break; - case SDT_ONEOFMANY: MakeOneOfMany(buf, lastof(buf), sdb->many, i); break; - case SDT_MANYOFMANY: MakeManyOfMany(buf, lastof(buf), sdb->many, i); break; - default: NOT_REACHED(); - } - break; - } - - case SDT_STDSTRING: - switch (GetVarMemType(sld->conv)) { - case SLE_VAR_STR: strecpy(buf, reinterpret_cast(ptr)->c_str(), lastof(buf)); break; - - case SLE_VAR_STRQ: - if (reinterpret_cast(ptr)->empty()) { - buf[0] = '\0'; - } else { - seprintf(buf, lastof(buf), "\"%s\"", reinterpret_cast(ptr)->c_str()); - } - break; - - default: NOT_REACHED(); - } - break; - - case SDT_INTLIST: - MakeIntList(buf, lastof(buf), ptr, sld->length, sld->conv); - break; - - default: NOT_REACHED(); - } - - /* The value is different, that means we have to write it to the ini */ - item->value.emplace(buf); } } +void IntSettingDesc::FormatValue(char *buf, const char *last, const void *object) const +{ + uint32 i = (uint32)this->Read(object); + seprintf(buf, last, IsSignedVarMemType(this->save.conv) ? "%d" : (this->save.conv & SLF_HEX) ? "%X" : "%u", i); +} + +void BoolSettingDesc::FormatValue(char *buf, const char *last, const void *object) const +{ + bool val = this->Read(object) != 0; + strecpy(buf, val ? "true" : "false", last); +} + +bool IntSettingDesc::IsSameValue(const IniItem *item, void *object) const +{ + int32 item_value = (int32)this->ParseValue(item->value->c_str()); + int32 object_value = this->Read(object); + return item_value == object_value; +} + +void StringSettingDesc::FormatValue(char *buf, const char *last, const void *object) const +{ + const std::string &str = this->Read(object); + switch (GetVarMemType(this->save.conv)) { + case SLE_VAR_STR: strecpy(buf, str.c_str(), last); break; + + case SLE_VAR_STRQ: + if (str.empty()) { + buf[0] = '\0'; + } else { + seprintf(buf, last, "\"%s\"", str.c_str()); + } + break; + + default: NOT_REACHED(); + } +} + +bool StringSettingDesc::IsSameValue(const IniItem *item, void *object) const +{ + /* The ini parsing removes the quotes, which are needed to retain the spaces in STRQs, + * so those values are always different in the parsed ini item than they should be. */ + if (GetVarMemType(this->save.conv) == SLE_VAR_STRQ) return false; + + const std::string &str = this->Read(object); + return item->value->compare(str) == 0; +} + +bool ListSettingDesc::IsSameValue(const IniItem *item, void *object) const +{ + /* Checking for equality is way more expensive than just writing the value. */ + return false; +} + /** * Loads all items from a 'grpname' section into a list * The list parameter can be a nullptr pointer, in this case nothing will be @@ -879,6 +798,26 @@ SettingType SettingDesc::GetType() const return (this->save.conv & SLF_NOT_IN_SAVE) ? ST_CLIENT : ST_GAME; } +/** + * Get the setting description of this setting as an integer setting. + * @return The integer setting description. + */ +const IntSettingDesc *SettingDesc::AsIntSetting() const +{ + assert_msg(this->IsIntSetting(), "name: %s", this->name); + return static_cast(this); +} + +/** + * Get the setting description of this setting as a string setting. + * @return The string setting description. + */ +const StringSettingDesc *SettingDesc::AsStringSetting() const +{ + assert_msg(this->IsStringSetting(), "name: %s", this->name); + return static_cast(this); +} + /* Begin - Callback Functions for the various settings. */ /** Reposition the main toolbar as the setting changed. */ @@ -1609,7 +1548,8 @@ static bool CheckRoadSide(int p1) static size_t ConvertLandscape(const char *value) { /* try with the old values */ - return LookupOneOfMany("normal|hilly|desert|candy", value); + static std::vector _old_landscape_values{"normal", "hilly", "desert", "candy"}; + return OneOfManySettingDesc::ParseSingleValue(value, strlen(value), _old_landscape_values); } static bool CheckFreeformEdges(int32 p1) @@ -1915,8 +1855,8 @@ static void HandleOldDiffCustom(bool savegame) const SettingDesc *sd = GetSettingDescription(i); /* Skip deprecated options */ if (!SlIsObjectCurrentlyValid(sd->save.version_from, sd->save.version_to, sd->save.ext_feature_test)) continue; - void *var = GetVariableAddress(savegame ? &_settings_game : &_settings_newgame, &sd->save); - Write_ValidateSetting(var, sd, (int32)((i == 4 ? 1000 : 1) * _old_diff_custom[i])); + int32 value = (int32)((i == 4 ? 1000 : 1) * _old_diff_custom[i]); + sd->AsIntSetting()->Write_ValidateSetting(savegame ? &_settings_game : &_settings_newgame, value); } } @@ -2346,6 +2286,38 @@ void DeleteGRFPresetFromConfig(const char *config_name) delete ini; } +/** + * Handle changing a value. This performs validation of the input value and + * calls the appropriate callbacks, and saves it when the value is changed. + * @param object The object the setting is in. + * @param newval The new value for the setting. + */ +void IntSettingDesc::ChangeValue(const void *object, int32 newval) const +{ + int32 oldval = this->Read(object); + + this->Write_ValidateSetting(object, newval); + newval = this->Read(object); + + if (oldval == newval) return; + + if (this->proc != nullptr && !this->proc(newval)) { + /* The change was not allowed, so revert. */ + WriteValue(GetVariableAddress(object, &this->save), this->save.conv, (int64)oldval); + return; + } + + if (this->flags & SGF_NO_NETWORK) { + GamelogStartAction(GLAT_SETTING); + GamelogSetting(this->name, oldval, newval); + GamelogStopAction(); + } + + SetWindowClassesDirty(WC_GAME_OPTIONS); + + if (_save_config) SaveToConfig(); +} + /** * Network-safe changing of settings (server-only). * @param tile unused @@ -2363,36 +2335,14 @@ CommandCost CmdChangeSetting(TileIndex tile, DoCommandFlag flags, uint32 p1, uin if (sd == nullptr) return CMD_ERROR; if (!SlIsObjectCurrentlyValid(sd->save.version_from, sd->save.version_to, sd->save.ext_feature_test)) return CMD_ERROR; + if (!sd->IsIntSetting()) return CMD_ERROR; if (!sd->IsEditable(true)) return CMD_ERROR; if (flags & DC_EXEC) { - void *var = GetVariableAddress(&GetGameSettings(), &sd->save); + SCOPE_INFO_FMT([=], "CmdChangeSetting: %s -> %d", sd->name, p2); - int32 oldval = (int32)ReadValue(var, sd->save.conv); - int32 newval = (int32)p2; - - SCOPE_INFO_FMT([=], "CmdChangeSetting: %s, %d -> %d", sd->name, oldval, newval); - - Write_ValidateSetting(var, sd, newval); - newval = (int32)ReadValue(var, sd->save.conv); - - if (oldval == newval) return CommandCost(); - - if (sd->proc != nullptr && !sd->proc(newval)) { - WriteValue(var, sd->save.conv, (int64)oldval); - return CommandCost(); - } - - if (sd->flags & SGF_NO_NETWORK) { - GamelogStartAction(GLAT_SETTING); - GamelogSetting(sd->name, oldval, newval); - GamelogStopAction(); - } - - SetWindowClassesDirty(WC_GAME_OPTIONS); - - if (_save_config) SaveToConfig(); + sd->AsIntSetting()->ChangeValue(&GetGameSettings(), p2); } return CommandCost(); @@ -2420,26 +2370,12 @@ CommandCost CmdChangeCompanySetting(TileIndex tile, DoCommandFlag flags, uint32 { const SettingDesc *sd = GetCompanySettingDescription(p1); if (sd == nullptr) return CMD_ERROR; + if (!sd->IsIntSetting()) return CMD_ERROR; if (flags & DC_EXEC) { - void *var = GetVariableAddress(&Company::Get(_current_company)->settings, &sd->save); + SCOPE_INFO_FMT([=], "CmdChangeCompanySetting: %s -> %d", sd->name, p2); - int32 oldval = (int32)ReadValue(var, sd->save.conv); - int32 newval = (int32)p2; - - SCOPE_INFO_FMT([=], "CmdChangeCompanySetting: %s, %d -> %d", sd->name, oldval, newval); - - Write_ValidateSetting(var, sd, newval); - newval = (int32)ReadValue(var, sd->save.conv); - - if (oldval == newval) return CommandCost(); - - if (sd->proc != nullptr && !sd->proc(newval)) { - WriteValue(var, sd->save.conv, (int64)oldval); - return CommandCost(); - } - - SetWindowClassesDirty(WC_GAME_OPTIONS); + sd->AsIntSetting()->ChangeValue(&Company::Get(_current_company)->settings, p2); } return CommandCost(); @@ -2452,6 +2388,22 @@ const char *GetCompanySettingNameByIndex(uint32 idx) return GetCompanySettingDescription(idx)->name; } +/** + * Get the index of the given setting in the setting table. + * @param settings The settings to look through. + * @param setting The setting to look for. + * @return The index, or UINT32_MAX when it has not been found. + */ +static uint GetSettingIndex(const SettingTable &settings, const SettingDesc *setting) +{ + uint index = 0; + for (auto &sd : settings) { + if (sd.get() == setting) return index; + index++; + } + return UINT32_MAX; +} + /** * Get the index of the setting with this description. * @param sd the setting to get the index for. @@ -2460,7 +2412,7 @@ const char *GetCompanySettingNameByIndex(uint32 idx) uint GetSettingIndex(const SettingDesc *sd) { assert(sd != nullptr && (sd->flags & SGF_PER_COMPANY) == 0); - return sd - _settings.begin(); + return GetSettingIndex(_settings, sd); } /** @@ -2471,7 +2423,7 @@ uint GetSettingIndex(const SettingDesc *sd) static uint GetCompanySettingIndex(const SettingDesc *sd) { assert(sd != nullptr && (sd->flags & SGF_PER_COMPANY) != 0); - return sd - _company_settings.begin(); + return GetSettingIndex(_company_settings, sd); } /** @@ -2481,18 +2433,17 @@ static uint GetCompanySettingIndex(const SettingDesc *sd) * @param value new value of the setting * @param force_newgame force the newgame settings */ -bool SetSettingValue(const SettingDesc *sd, int32 value, bool force_newgame) +bool SetSettingValue(const IntSettingDesc *sd, int32 value, bool force_newgame) { - if ((sd->flags & SGF_PER_COMPANY) != 0) { + const IntSettingDesc *setting = sd->AsIntSetting(); + if ((setting->flags & SGF_PER_COMPANY) != 0) { if (Company::IsValidID(_local_company) && _game_mode != GM_MENU) { - return DoCommandP(0, GetCompanySettingIndex(sd), value, CMD_CHANGE_COMPANY_SETTING); - } else if (sd->flags & SGF_NO_NEWGAME) { + return DoCommandP(0, GetCompanySettingIndex(setting), value, CMD_CHANGE_COMPANY_SETTING); + } else if (setting->flags & SGF_NO_NEWGAME) { return false; } - void *var = GetVariableAddress(&_settings_client.company, &sd->save); - Write_ValidateSetting(var, sd, value); - if (sd->proc != nullptr) sd->proc((int32)ReadValue(var, sd->save.conv)); + setting->ChangeValue(&_settings_client.company, value); return true; } @@ -2500,35 +2451,24 @@ bool SetSettingValue(const SettingDesc *sd, int32 value, bool force_newgame) * (if any) to change. Also *hack*hack* we update the _newgame version * of settings because changing a company-based setting in a game also * changes its defaults. At least that is the convention we have chosen */ - bool no_newgame = sd->flags & SGF_NO_NEWGAME; + bool no_newgame = setting->flags & SGF_NO_NEWGAME; if (no_newgame && _game_mode == GM_MENU) return false; - if (sd->save.conv & SLF_NO_NETWORK_SYNC) { - void *var = GetVariableAddress(&GetGameSettings(), &sd->save); - Write_ValidateSetting(var, sd, value); - + if (setting->save.conv & SLF_NO_NETWORK_SYNC) { if (_game_mode != GM_MENU && !no_newgame) { - void *var2 = GetVariableAddress(&_settings_newgame, &sd->save); - Write_ValidateSetting(var2, sd, value); + setting->ChangeValue(&_settings_newgame, value); } - if (sd->proc != nullptr) sd->proc((int32)ReadValue(var, sd->save.conv)); - - SetWindowClassesDirty(WC_GAME_OPTIONS); - - if (_save_config) SaveToConfig(); + setting->ChangeValue(&GetGameSettings(), value); return true; } if (force_newgame && !no_newgame) { - void *var2 = GetVariableAddress(&_settings_newgame, &sd->save); - Write_ValidateSetting(var2, sd, value); - - if (_save_config) SaveToConfig(); + setting->ChangeValue(&_settings_newgame, value); return true; } /* send non-company-based settings over the network */ if (!_networking || (_networking && (_network_server || _network_settings_access))) { - return DoCommandP(0, GetSettingIndex(sd), value, CMD_CHANGE_SETTING); + return DoCommandP(0, GetSettingIndex(setting), value, CMD_CHANGE_SETTING); } return false; } @@ -2540,8 +2480,10 @@ void SetDefaultCompanySettings(CompanyID cid) { Company *c = Company::Get(cid); for (auto &sd : _company_settings) { - void *var = GetVariableAddress(&c->settings, &sd.save); - Write_ValidateSetting(var, &sd, (int32)(size_t)sd.def); + if (sd->IsIntSetting()) { + const IntSettingDesc *int_setting = sd->AsIntSetting(); + int_setting->Write_ValidateSetting(&c->settings, int_setting->def); + } } } @@ -2550,13 +2492,14 @@ void SetDefaultCompanySettings(CompanyID cid) */ void SyncCompanySettings() { + const void *old_object = &Company::Get(_current_company)->settings; + const void *new_object = &_settings_client.company; uint i = 0; for (auto &sd : _company_settings) { - const void *old_var = GetVariableAddress(&Company::Get(_current_company)->settings, &sd.save); - const void *new_var = GetVariableAddress(&_settings_client.company, &sd.save); - uint32 old_value = (uint32)ReadValue(old_var, sd.save.conv); - uint32 new_value = (uint32)ReadValue(new_var, sd.save.conv); + uint32 old_value = (uint32)sd->AsIntSetting()->Read(new_object); + uint32 new_value = (uint32)sd->AsIntSetting()->Read(old_object); if (old_value != new_value) NetworkSendCommand(0, i, new_value, 0, CMD_CHANGE_COMPANY_SETTING, nullptr, nullptr, _local_company, 0); + i++; } } @@ -2577,7 +2520,7 @@ uint GetCompanySettingIndex(const char *name) * @param force_newgame force the newgame settings * @note Strings WILL NOT be synced over the network */ -bool SetSettingValue(const SettingDesc *sd, const char *value, bool force_newgame) +bool SetSettingValue(const StringSettingDesc *sd, const char *value, bool force_newgame) { assert(sd->save.conv & SLF_NO_NETWORK_SYNC); @@ -2585,12 +2528,33 @@ bool SetSettingValue(const SettingDesc *sd, const char *value, bool force_newgam value = nullptr; } - void *ptr = GetVariableAddress((_game_mode == GM_MENU || force_newgame) ? &_settings_newgame : &_settings_game, &sd->save); - Write_ValidateStdString(ptr, sd, value); - if (sd->proc != nullptr) sd->proc(0); + const void *object = (_game_mode == GM_MENU || force_newgame) ? &_settings_newgame : &_settings_game; + sd->AsStringSetting()->ChangeValue(object, value); + return true; +} + +/** + * Handle changing a string value. This performs validation of the input value + * and calls the appropriate callbacks, and saves it when the value is changed. + * @param object The object the setting is in. + * @param newval The new value for the setting. + */ +void StringSettingDesc::ChangeValue(const void *object, const char *newval) const +{ + this->Write_ValidateSetting(object, newval); + if (this->proc != nullptr) this->proc(0); if (_save_config) SaveToConfig(); - return true; +} + +uint GetSettingIndexByFullName(const char *name) +{ + uint index = 0; + for (auto &sd : _settings) { + if (sd->name != nullptr && strcmp(sd->name, name) == 0) return index; + index++; + } + return UINT32_MAX; } /** @@ -2605,28 +2569,28 @@ const SettingDesc *GetSettingFromName(const char *name, bool ignore_version) { /* First check all full names */ for (auto &sd : _settings) { - if (sd.name == nullptr) continue; - if (!ignore_version && !SlIsObjectCurrentlyValid(sd.save.version_from, sd.save.version_to, sd.save.ext_feature_test)) continue; - if (strcmp(sd.name, name) == 0) return &sd; + if (sd->name == nullptr) continue; + if (!ignore_version && !SlIsObjectCurrentlyValid(sd->save.version_from, sd->save.version_to, sd->save.ext_feature_test)) continue; + if (strcmp(sd->name, name) == 0) return sd.get(); } /* Then check the shortcut variant of the name. */ for (auto &sd : _settings) { - if (sd.name == nullptr) continue; - if (!ignore_version && !SlIsObjectCurrentlyValid(sd.save.version_from, sd.save.version_to, sd.save.ext_feature_test)) continue; - const char *short_name = strchr(sd.name, '.'); + if (sd->name == nullptr) continue; + if (!ignore_version && !SlIsObjectCurrentlyValid(sd->save.version_from, sd->save.version_to, sd->save.ext_feature_test)) continue; + const char *short_name = strchr(sd->name, '.'); if (short_name != nullptr) { short_name++; - if (strcmp(short_name, name) == 0) return &sd; + if (strcmp(short_name, name) == 0) return sd.get(); } } if (strncmp(name, "company.", 8) == 0) name += 8; /* And finally the company-based settings */ for (auto &sd : _company_settings) { - if (sd.name == nullptr) continue; - if (!ignore_version && !SlIsObjectCurrentlyValid(sd.save.version_from, sd.save.version_to, sd.save.ext_feature_test)) continue; - if (strcmp(sd.name, name) == 0) return &sd; + if (sd->name == nullptr) continue; + if (!ignore_version && !SlIsObjectCurrentlyValid(sd->save.version_from, sd->save.version_to, sd->save.ext_feature_test)) continue; + if (strcmp(sd->name, name) == 0) return sd.get(); } return nullptr; @@ -2643,10 +2607,10 @@ void IConsoleSetSetting(const char *name, const char *value, bool force_newgame) return; } - bool success; - if (sd->cmd == SDT_STDSTRING) { - success = SetSettingValue(sd, value, force_newgame); - } else { + bool success = true; + if (sd->IsStringSetting()) { + success = SetSettingValue(sd->AsStringSetting(), value, force_newgame); + } else if (sd->IsIntSetting()) { uint32 val; extern bool GetArgumentInteger(uint32 *value, const char *arg); success = GetArgumentInteger(&val, value); @@ -2655,7 +2619,7 @@ void IConsoleSetSetting(const char *name, const char *value, bool force_newgame) return; } - success = SetSettingValue(sd, val, force_newgame); + success = SetSettingValue(sd->AsIntSetting(), val, force_newgame); } if (!success) { @@ -2671,7 +2635,7 @@ void IConsoleSetSetting(const char *name, int value) { const SettingDesc *sd = GetSettingFromName(name); assert(sd != nullptr); - SetSettingValue(sd, value); + SetSettingValue(sd->AsIntSetting(), value); } /** @@ -2681,28 +2645,28 @@ void IConsoleSetSetting(const char *name, int value) */ void IConsoleGetSetting(const char *name, bool force_newgame) { - char value[20]; const SettingDesc *sd = GetSettingFromName(name); - const void *ptr; if (sd == nullptr || ((sd->flags & SGF_NO_NEWGAME) && (_game_mode == GM_MENU || force_newgame))) { IConsolePrintF(CC_WARNING, "'%s' is an unknown setting.", name); return; } - ptr = GetVariableAddress((_game_mode == GM_MENU || force_newgame) ? &_settings_newgame : &_settings_game, &sd->save); + const void *object = (_game_mode == GM_MENU || force_newgame) ? &_settings_newgame : &_settings_game; + + if (sd->IsStringSetting()) { + IConsolePrintF(CC_WARNING, "Current value for '%s' is: '%s'", name, sd->AsStringSetting()->Read(object).c_str()); + } else if (sd->IsIntSetting()) { + const IntSettingDesc *int_setting = sd->AsIntSetting(); - if (sd->cmd == SDT_STDSTRING) { - IConsolePrintF(CC_WARNING, "Current value for '%s' is: '%s'", name, reinterpret_cast(ptr)->c_str()); - } else { bool show_min_max = true; - int64 min_value = sd->min; - int64 max_value = sd->max; + int64 min_value = int_setting->min; + int64 max_value = int_setting->max; if (sd->flags & SGF_ENUM) { min_value = INT64_MAX; max_value = INT64_MIN; int count = 0; - for (const SettingDescEnumEntry *enumlist = sd->enumlist; enumlist != nullptr && enumlist->str != STR_NULL; enumlist++) { + for (const SettingDescEnumEntry *enumlist = int_setting->enumlist; enumlist != nullptr && enumlist->str != STR_NULL; enumlist++) { if (enumlist->val < min_value) min_value = enumlist->val; if (enumlist->val > max_value) max_value = enumlist->val; count++; @@ -2712,11 +2676,9 @@ void IConsoleGetSetting(const char *name, bool force_newgame) show_min_max = false; } } - if (sd->cmd == SDT_BOOLX) { - seprintf(value, lastof(value), (*(const bool*)ptr != 0) ? "on" : "off"); - } else { - seprintf(value, lastof(value), sd->min < 0 ? "%d" : "%u", (int32)ReadValue(ptr, sd->save.conv)); - } + + char value[20]; + sd->FormatValue(value, lastof(value), object); if (show_min_max) { IConsolePrintF(CC_WARNING, "Current value for '%s' is: '%s' (min: %s" OTTD_PRINTF64 ", max: " OTTD_PRINTF64 ")", @@ -2738,20 +2700,12 @@ void IConsoleListSettings(const char *prefilter) IConsolePrintF(CC_WARNING, "All settings with their current value:"); for (auto &sd : _settings) { - if (!SlIsObjectCurrentlyValid(sd.save.version_from, sd.save.version_to, sd.save.ext_feature_test)) continue; - if (prefilter != nullptr && strstr(sd.name, prefilter) == nullptr) continue; - if ((sd.flags & SGF_NO_NEWGAME) && _game_mode == GM_MENU) continue; + if (!SlIsObjectCurrentlyValid(sd->save.version_from, sd->save.version_to, sd->save.ext_feature_test)) continue; + if (prefilter != nullptr && strstr(sd->name, prefilter) == nullptr) continue; + if ((sd->flags & SGF_NO_NEWGAME) && _game_mode == GM_MENU) continue; char value[80]; - const void *ptr = GetVariableAddress(&GetGameSettings(), &sd.save); - - if (sd.cmd == SDT_BOOLX) { - seprintf(value, lastof(value), (*(const bool *)ptr != 0) ? "on" : "off"); - } else if (sd.cmd == SDT_STDSTRING) { - seprintf(value, lastof(value), "%s", reinterpret_cast(ptr)->c_str()); - } else { - seprintf(value, lastof(value), sd.min < 0 ? "%d" : "%u", (int32)ReadValue(ptr, sd.save.conv)); - } - IConsolePrintF(CC_DEFAULT, "%s = %s", sd.name, value); + sd->FormatValue(value, lastof(value), &GetGameSettings()); + IConsolePrintF(CC_DEFAULT, "%s = %s", sd->name, value); } IConsolePrintF(CC_WARNING, "Use 'setting' command to change a value"); @@ -2778,7 +2732,10 @@ static void LoadSettingsXref(const SettingDesc *osd, void *object) { if (!SlObjectMember(ptr, &sld)) return; int64 val = ReadValue(ptr, sld.conv); if (osd->xref.conv != nullptr) val = osd->xref.conv(val); - if (IsNumericType(sld.conv)) Write_ValidateSetting(ptr, setting_xref, val); + if (setting_xref->IsIntSetting()) { + const IntSettingDesc *int_setting = setting_xref->AsIntSetting(); + int_setting->Write_ValidateSetting(object, int_setting->Read(object)); + } } /** @@ -2792,16 +2749,19 @@ static void LoadSettings(const SettingTable &settings, void *object) extern SaveLoadVersion _sl_version; for (auto &osd : settings) { - if (osd.patx_name != nullptr) continue; - const SaveLoad *sld = &osd.save; - if (osd.xref.target != nullptr) { - if (sld->ext_feature_test.IsFeaturePresent(_sl_version, sld->version_from, sld->version_to)) LoadSettingsXref(&osd, object); + if (osd->patx_name != nullptr) continue; + const SaveLoad *sld = &osd->save; + if (osd->xref.target != nullptr) { + if (sld->ext_feature_test.IsFeaturePresent(_sl_version, sld->version_from, sld->version_to)) LoadSettingsXref(osd.get(), object); continue; } void *ptr = GetVariableAddress(object, sld); - if (!SlObjectMember(ptr, sld)) continue; - if (IsNumericType(sld->conv)) Write_ValidateSetting(ptr, &osd, ReadValue(ptr, sld->conv)); + if (!SlObjectMember(ptr, &osd->save)) continue; + if (osd->IsIntSetting()) { + const IntSettingDesc *int_setting = osd->AsIntSetting(); + int_setting->Write_ValidateSetting(object, int_setting->Read(object)); + } } } @@ -2817,16 +2777,16 @@ static void SaveSettings(const SettingTable &settings, void *object) * SlCalcLength() because we have a different format. So do this manually */ size_t length = 0; for (auto &sd : settings) { - if (sd.patx_name != nullptr) continue; - if (sd.xref.target != nullptr) continue; - length += SlCalcObjMemberLength(object, &sd.save); + if (sd->patx_name != nullptr) continue; + if (sd->xref.target != nullptr) continue; + length += SlCalcObjMemberLength(object, &sd->save); } SlSetLength(length); for (auto &sd : settings) { - if (sd.patx_name != nullptr) continue; - void *ptr = GetVariableAddress(object, &sd.save); - SlObjectMember(ptr, &sd.save); + if (sd->patx_name != nullptr) continue; + void *ptr = GetVariableAddress(object, &sd->save); + SlObjectMember(ptr, &sd->save); } } @@ -2864,8 +2824,8 @@ static void MakeSettingsPatxList(const SettingTable &settings) _sorted_patx_settings.clear(); for (auto &sd : settings) { - if (sd.patx_name == nullptr) continue; - _sorted_patx_settings.push_back(&sd); + if (sd->patx_name == nullptr) continue; + _sorted_patx_settings.push_back(sd.get()); } std::sort(_sorted_patx_settings.begin(), _sorted_patx_settings.end(), [](const SettingDesc *a, const SettingDesc *b) { @@ -2939,14 +2899,18 @@ static void LoadSettingsPatx(const SettingTable &settings, void *object) if (exact_match) { assert(iter != _sorted_patx_settings.end()); // found setting - const SaveLoad *sld = &((*iter)->save); + const SettingDesc *setting = (*iter); + const SaveLoad *sld = &(setting->save); size_t read = SlGetBytesRead(); void *ptr = GetVariableAddress(object, sld); SlObjectMember(ptr, sld); if (SlGetBytesRead() != read + current_setting.setting_length) { SlErrorCorruptFmt("PATX chunk: setting read length mismatch for setting: '%s'", current_setting.name); } - if (IsNumericType(sld->conv)) Write_ValidateSetting(ptr, *iter, ReadValue(ptr, sld->conv)); + if (setting->IsIntSetting()) { + const IntSettingDesc *int_setting = setting->AsIntSetting(); + int_setting->Write_ValidateSetting(object, int_setting->Read(object)); + } } else { DEBUG(sl, 1, "PATX chunk: Could not find setting: '%s', ignoring", current_setting.name); SlSkipBytes(current_setting.setting_length); @@ -2972,11 +2936,11 @@ static void SaveSettingsPatx(const SettingTable &settings, void *object) size_t length = 8; for (auto &sd : settings) { - if (sd.patx_name == nullptr) continue; - uint32 setting_length = (uint32)SlCalcObjMemberLength(object, &sd.save); + if (sd->patx_name == nullptr) continue; + uint32 setting_length = (uint32)SlCalcObjMemberLength(object, &sd->save); if (!setting_length) continue; - current_setting.name = sd.patx_name; + current_setting.name = sd->patx_name; // add length of setting header length += SlCalcObjLength(¤t_setting, _settings_ext_save_desc); @@ -2985,7 +2949,7 @@ static void SaveSettingsPatx(const SettingTable &settings, void *object) length += setting_length; // duplicate copy made for compiler backwards compatibility - SettingToAdd new_setting = { &sd, setting_length }; + SettingToAdd new_setting = { sd.get(), setting_length }; settings_to_add.push_back(new_setting); } SlSetLength(length); @@ -3069,8 +3033,8 @@ void LoadSettingsPlyx(bool skip) // not many company settings, so perform a linear scan for (auto &sd : _company_settings) { - if (sd.patx_name != nullptr && strcmp(sd.patx_name, current_setting.name) == 0) { - setting = &sd; + if (sd->patx_name != nullptr && strcmp(sd->patx_name, current_setting.name) == 0) { + setting = sd.get(); break; } } @@ -3084,7 +3048,10 @@ void LoadSettingsPlyx(bool skip) if (SlGetBytesRead() != read + current_setting.setting_length) { SlErrorCorruptFmt("PLYX chunk: setting read length mismatch for setting: '%s'", current_setting.name); } - if (IsNumericType(sld->conv)) Write_ValidateSetting(ptr, setting, ReadValue(ptr, sld->conv)); + if (setting->IsIntSetting()) { + const IntSettingDesc *int_setting = setting->AsIntSetting(); + int_setting->Write_ValidateSetting(&(c->settings), int_setting->Read(&(c->settings))); + } } else { DEBUG(sl, 1, "PLYX chunk: Could not find company setting: '%s', ignoring", current_setting.name); SlSkipBytes(current_setting.setting_length); @@ -3117,11 +3084,11 @@ void SaveSettingsPlyx() companies_count++; uint32 setting_count = 0; for (auto &sd : _company_settings) { - if (sd.patx_name == nullptr) continue; - uint32 setting_length = (uint32)SlCalcObjMemberLength(&(c->settings), &sd.save); + if (sd->patx_name == nullptr) continue; + uint32 setting_length = (uint32)SlCalcObjMemberLength(&(c->settings), &sd->save); if (!setting_length) continue; - current_setting.name = sd.patx_name; + current_setting.name = sd->patx_name; // add length of setting header length += SlCalcObjLength(¤t_setting, _settings_ext_save_desc); @@ -3148,16 +3115,16 @@ void SaveSettingsPlyx() index++; for (auto &sd : _company_settings) { - if (sd.patx_name == nullptr) continue; - uint32 setting_length = (uint32)SlCalcObjMemberLength(&(c->settings), &sd.save); + if (sd->patx_name == nullptr) continue; + uint32 setting_length = (uint32)SlCalcObjMemberLength(&(c->settings), &sd->save); if (!setting_length) continue; current_setting.flags = 0; - current_setting.name = sd.patx_name; + current_setting.name = sd->patx_name; current_setting.setting_length = setting_length; SlObject(¤t_setting, _settings_plyx_desc); - void *ptr = GetVariableAddress(&(c->settings), &sd.save); - SlObjectMember(ptr, &sd.save); + void *ptr = GetVariableAddress(&(c->settings), &sd->save); + SlObjectMember(ptr, &sd->save); } } } diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index ef0fa162f7..9b6fe9b615 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -49,6 +49,9 @@ #include "safeguards.h" #include "video/video_driver.hpp" +uint GetSettingIndexByFullName(const char *name); +const SettingDesc *GetSettingDescription(uint index); + extern void FlushDeparturesWindowTextCaches(); static const StringID _autosave_dropdown[] = { @@ -78,7 +81,7 @@ static const StringID _font_zoom_dropdown[] = { static Dimension _circle_size; ///< Dimension of the circle +/- icon. This is here as not all users are within the class of the settings window. -static const void *ResolveVariableAddress(const GameSettings *settings_ptr, const SettingDesc *sd); +static const void *ResolveObject(const GameSettings *settings_ptr, const IntSettingDesc *sd); /** * Get index of the current screen resolution. @@ -830,8 +833,8 @@ protected: /** Standard setting */ struct SettingEntry : BaseSettingEntry { - const char *name; ///< Name of the setting - const SettingDesc *setting; ///< Setting description of the setting + const char *name; ///< Name of the setting + const IntSettingDesc *setting; ///< Setting description of the setting SettingEntry(const char *name); @@ -851,7 +854,7 @@ struct SettingEntry : BaseSettingEntry { void SetValueDParams(uint first_param, int32 value, std::unique_ptr &tempdata) const; protected: - SettingEntry(const SettingDesc *setting); + SettingEntry(const IntSettingDesc *setting); virtual void DrawSetting(GameSettings *settings_ptr, int left, int right, int y, bool highlight) const; virtual void DrawSettingString(uint left, uint right, int y, bool highlight, int32 value) const; @@ -881,7 +884,7 @@ StringID SettingEntry::GetHelpText() const struct CargoDestPerCargoSettingEntry : SettingEntry { CargoID cargo; - CargoDestPerCargoSettingEntry(CargoID cargo, const SettingDesc *setting); + CargoDestPerCargoSettingEntry(CargoID cargo, const IntSettingDesc *setting); virtual void Init(byte level = 0); virtual bool UpdateFilterState(SettingFilter &filter, bool force_visible); @@ -1067,7 +1070,7 @@ SettingEntry::SettingEntry(const char *name) this->setting = nullptr; } -SettingEntry::SettingEntry(const SettingDesc *setting) +SettingEntry::SettingEntry(const IntSettingDesc *setting) { this->name = nullptr; this->setting = setting; @@ -1080,15 +1083,15 @@ SettingEntry::SettingEntry(const SettingDesc *setting) void SettingEntry::Init(byte level) { BaseSettingEntry::Init(level); - this->setting = GetSettingFromName(this->name); - assert_msg(this->setting != nullptr, "name: %s", this->name); + const SettingDesc *st = GetSettingFromName(this->name); + assert_msg(st != nullptr, "name: %s", this->name); + this->setting = st->AsIntSetting(); } /* Sets the given setting entry to its default value */ void SettingEntry::ResetAll() { - int32 default_value = ReadValue(&this->setting->def, this->setting->save.conv); - SetSettingValue(this->setting, default_value); + SetSettingValue(this->setting, this->setting->def); } /** @@ -1128,34 +1131,31 @@ bool SettingEntry::IsVisibleByRestrictionMode(RestrictionMode mode) const /* There shall not be any restriction, i.e. all settings shall be visible. */ if (mode == RM_ALL) return true; - GameSettings *settings_ptr = &GetGameSettings(); - const SettingDesc *sd = this->setting; + const IntSettingDesc *sd = this->setting; if (mode == RM_BASIC) return (this->setting->cat & SC_BASIC_LIST) != 0; if (mode == RM_ADVANCED) return (this->setting->cat & SC_ADVANCED_LIST) != 0; /* Read the current value. */ - const void *var = ResolveVariableAddress(settings_ptr, sd); - int64 current_value = ReadValue(var, sd->save.conv); - + const void *object = ResolveObject(&GetGameSettings(), sd); + int64 current_value = sd->Read(object); int64 filter_value; if (mode == RM_CHANGED_AGAINST_DEFAULT) { /* This entry shall only be visible, if the value deviates from its default value. */ /* Read the default value. */ - filter_value = ReadValue(&sd->def, sd->save.conv); + filter_value = sd->def; } else { assert(mode == RM_CHANGED_AGAINST_NEW); /* This entry shall only be visible, if the value deviates from * its value is used when starting a new game. */ /* Make sure we're not comparing the new game settings against itself. */ - assert(settings_ptr != &_settings_newgame); + assert(&GetGameSettings() != &_settings_newgame); /* Read the new game's value. */ - var = ResolveVariableAddress(&_settings_newgame, sd); - filter_value = ReadValue(var, sd->save.conv); + filter_value = sd->Read(ResolveObject(&_settings_newgame, sd)); } return current_value != filter_value; @@ -1177,7 +1177,7 @@ bool SettingEntry::UpdateFilterState(SettingFilter &filter, bool force_visible) bool visible = true; - const SettingDesc *sd = this->setting; + const IntSettingDesc *sd = this->setting; if (!force_visible && !filter.string.IsEmpty()) { /* Process the search text filter for this item. */ filter.string.ResetState(); @@ -1204,17 +1204,15 @@ bool SettingEntry::UpdateFilterState(SettingFilter &filter, bool force_visible) return visible; } -static const void *ResolveVariableAddress(const GameSettings *settings_ptr, const SettingDesc *sd) +static const void *ResolveObject(const GameSettings *settings_ptr, const IntSettingDesc *sd) { if ((sd->flags & SGF_PER_COMPANY) != 0) { if (Company::IsValidID(_local_company) && _game_mode != GM_MENU) { - return GetVariableAddress(&Company::Get(_local_company)->settings, &sd->save); - } else { - return GetVariableAddress(&_settings_client.company, &sd->save); + return &Company::Get(_local_company)->settings; } - } else { - return GetVariableAddress(settings_ptr, &sd->save); + return &_settings_client.company; } + return settings_ptr; } /** @@ -1224,7 +1222,7 @@ static const void *ResolveVariableAddress(const GameSettings *settings_ptr, cons */ void SettingEntry::SetValueDParams(uint first_param, int32 value, std::unique_ptr &tempdata) const { - if (this->setting->cmd == SDT_BOOLX) { + if (this->setting->IsBoolSetting()) { SetDParam(first_param++, value != 0 ? STR_CONFIG_SETTING_ON : STR_CONFIG_SETTING_OFF); } else if (this->setting->flags & SGF_DEC1SCALE) { tempdata.reset(new SettingEntry::SetValueDParamsTempData()); @@ -1268,8 +1266,7 @@ void SettingEntry::SetValueDParams(uint first_param, int32 value, std::unique_pt */ void SettingEntry::DrawSetting(GameSettings *settings_ptr, int left, int right, int y, bool highlight) const { - const SettingDesc *sd = this->setting; - const void *var = ResolveVariableAddress(settings_ptr, sd); + const IntSettingDesc *sd = this->setting; int state = this->flags & SEF_BUTTONS_MASK; bool rtl = _current_text_dir == TD_RTL; @@ -1282,8 +1279,8 @@ void SettingEntry::DrawSetting(GameSettings *settings_ptr, int left, int right, bool editable = sd->IsEditable(); SetDParam(0, highlight ? STR_ORANGE_STRING1_WHITE : STR_ORANGE_STRING1_LTBLUE); - int32 value = (int32)ReadValue(var, sd->save.conv); - if (sd->cmd == SDT_BOOLX) { + int32 value = sd->Read(ResolveObject(settings_ptr, sd)); + if (sd->IsBoolSetting()) { /* Draw checkbox for boolean-value either on/off */ DrawBoolButton(buttons_left, button_y, value != 0, editable); } else if ((sd->flags & (SGF_MULTISTRING | SGF_ENUM)) != 0) { @@ -1299,15 +1296,14 @@ void SettingEntry::DrawSetting(GameSettings *settings_ptr, int left, int right, void SettingEntry::DrawSettingString(uint left, uint right, int y, bool highlight, int32 value) const { - const SettingDesc *sd = this->setting; std::unique_ptr tempdata; this->SetValueDParams(1, value, tempdata); - DrawString(left, right, y, sd->str, highlight ? TC_WHITE : TC_LIGHT_BLUE); + DrawString(left, right, y, this->setting->str, highlight ? TC_WHITE : TC_LIGHT_BLUE); } /* == CargoDestPerCargoSettingEntry methods == */ -CargoDestPerCargoSettingEntry::CargoDestPerCargoSettingEntry(CargoID cargo, const SettingDesc *setting) +CargoDestPerCargoSettingEntry::CargoDestPerCargoSettingEntry(CargoID cargo, const IntSettingDesc *setting) : SettingEntry(setting), cargo(cargo) {} void CargoDestPerCargoSettingEntry::Init(byte level) @@ -1317,8 +1313,7 @@ void CargoDestPerCargoSettingEntry::Init(byte level) void CargoDestPerCargoSettingEntry::DrawSettingString(uint left, uint right, int y, bool highlight, int32 value) const { - const SettingDesc *sd = this->setting; - assert(sd->str == STR_CONFIG_SETTING_DISTRIBUTION_PER_CARGO); + assert(this->setting->str == STR_CONFIG_SETTING_DISTRIBUTION_PER_CARGO); SetDParam(0, CargoSpec::Get(this->cargo)->name); SetDParam(1, highlight ? STR_ORANGE_STRING1_WHITE : STR_ORANGE_STRING1_LTBLUE); std::unique_ptr tempdata; @@ -2127,9 +2122,10 @@ static SettingsContainer &GetSettingsTree() cdist->Add(new SettingEntry("linkgraph.distribution_default")); SettingsPage *cdist_override = cdist->Add(new SettingsPage(STR_CONFIG_SETTING_ENVIRONMENT_CARGODIST_PER_CARGO_OVERRIDE)); { - const SettingDesc *setting = GetSettingFromName("linkgraph.distribution_per_cargo[0]"); + uint base_index = GetSettingIndexByFullName("linkgraph.distribution_per_cargo[0]"); + assert(base_index != UINT32_MAX); for (CargoID c = 0; c < NUM_CARGO; c++) { - cdist_override->Add(new CargoDestPerCargoSettingEntry(c, setting + c)); + cdist_override->Add(new CargoDestPerCargoSettingEntry(c, GetSettingDescription(base_index + c)->AsIntSetting())); } } cdist->Add(new SettingEntry("linkgraph.accuracy")); @@ -2421,7 +2417,7 @@ struct GameSettingsWindow : Window { case WID_GS_HELP_TEXT: if (this->last_clicked != nullptr) { - const SettingDesc *sd = this->last_clicked->setting; + const IntSettingDesc *sd = this->last_clicked->setting; int y = r.top; switch (sd->GetType()) { @@ -2433,9 +2429,8 @@ struct GameSettingsWindow : Window { DrawString(r.left, r.right, y, STR_CONFIG_SETTING_TYPE); y += FONT_HEIGHT_NORMAL; - int32 default_value = ReadValue(&sd->def, sd->save.conv); std::unique_ptr tempdata; - this->last_clicked->SetValueDParams(0, default_value, tempdata); + this->last_clicked->SetValueDParams(0, sd->def, tempdata); DrawString(r.left, r.right, y, STR_CONFIG_SETTING_DEFAULT_VALUE); y += FONT_HEIGHT_NORMAL + WD_PAR_VSEP_NORMAL; @@ -2526,7 +2521,7 @@ struct GameSettingsWindow : Window { SettingEntry *pe = dynamic_cast(clicked_entry); assert(pe != nullptr); - const SettingDesc *sd = pe->setting; + const IntSettingDesc *sd = pe->setting; /* return if action is only active in network, or only settable by server */ if (!sd->IsEditable()) { @@ -2534,8 +2529,7 @@ struct GameSettingsWindow : Window { return; } - const void *var = ResolveVariableAddress(settings_ptr, sd); - int32 value = (int32)ReadValue(var, sd->save.conv); + int32 value = sd->Read(ResolveObject(settings_ptr, sd)); /* clicked on the icon on the left side. Either scroller, bool on/off or dropdown */ if (x < SETTING_BUTTON_WIDTH && (sd->flags & (SGF_MULTISTRING | SGF_ENUM))) { @@ -2594,52 +2588,47 @@ struct GameSettingsWindow : Window { this->SetDisplayedHelpText(pe); int32 oldvalue = value; - switch (sd->cmd) { - case SDT_BOOLX: value ^= 1; break; - case SDT_ONEOFMANY: - case SDT_NUMX: { - /* Add a dynamic step-size to the scroller. In a maximum of - * 50-steps you should be able to get from min to max, - * unless specified otherwise in the 'interval' variable - * of the current setting. */ - uint32 step = (sd->interval == 0) ? ((sd->max - sd->min) / 50) : sd->interval; - if (step == 0) step = 1; + if (sd->IsBoolSetting()) { + value ^= 1; + } else { + /* Add a dynamic step-size to the scroller. In a maximum of + * 50-steps you should be able to get from min to max, + * unless specified otherwise in the 'interval' variable + * of the current setting. */ + uint32 step = (sd->interval == 0) ? ((sd->max - sd->min) / 50) : sd->interval; + if (step == 0) step = 1; - /* don't allow too fast scrolling */ - if ((this->flags & WF_TIMEOUT) && this->timeout_timer > 1) { - _left_button_clicked = false; - return; - } - - /* Increase or decrease the value and clamp it to extremes */ - if (x >= SETTING_BUTTON_WIDTH / 2) { - value += step; - if (sd->min < 0) { - assert((int32)sd->max >= 0); - if (value > (int32)sd->max) value = (int32)sd->max; - } else { - if ((uint32)value > sd->max) value = (int32)sd->max; - } - if (value < sd->min) value = sd->min; // skip between "disabled" and minimum - } else { - value -= step; - if (value < sd->min) value = (sd->flags & SGF_0ISDISABLED) ? 0 : sd->min; - } - - /* Set up scroller timeout for numeric values */ - if (value != oldvalue) { - if (this->clicked_entry != nullptr) { // Release previous buttons if any - this->clicked_entry->SetButtons(0); - } - this->clicked_entry = pe; - this->clicked_entry->SetButtons((x >= SETTING_BUTTON_WIDTH / 2) != (_current_text_dir == TD_RTL) ? SEF_RIGHT_DEPRESSED : SEF_LEFT_DEPRESSED); - this->SetTimeout(); - _left_button_clicked = false; - } - break; + /* don't allow too fast scrolling */ + if ((this->flags & WF_TIMEOUT) && this->timeout_timer > 1) { + _left_button_clicked = false; + return; } - default: NOT_REACHED(); + /* Increase or decrease the value and clamp it to extremes */ + if (x >= SETTING_BUTTON_WIDTH / 2) { + value += step; + if (sd->min < 0) { + assert((int32)sd->max >= 0); + if (value > (int32)sd->max) value = (int32)sd->max; + } else { + if ((uint32)value > sd->max) value = (int32)sd->max; + } + if (value < sd->min) value = sd->min; // skip between "disabled" and minimum + } else { + value -= step; + if (value < sd->min) value = (sd->flags & SGF_0ISDISABLED) ? 0 : sd->min; + } + + /* Set up scroller timeout for numeric values */ + if (value != oldvalue) { + if (this->clicked_entry != nullptr) { // Release previous buttons if any + this->clicked_entry->SetButtons(0); + } + this->clicked_entry = pe; + this->clicked_entry->SetButtons((x >= SETTING_BUTTON_WIDTH / 2) != (_current_text_dir == TD_RTL) ? SEF_RIGHT_DEPRESSED : SEF_LEFT_DEPRESSED); + this->SetTimeout(); + _left_button_clicked = false; + } } if (value != oldvalue) { @@ -2648,7 +2637,7 @@ struct GameSettingsWindow : Window { } } else { /* Only open editbox if clicked for the second time, and only for types where it is sensible for. */ - if (this->last_clicked == pe && sd->cmd != SDT_BOOLX && !(sd->flags & (SGF_MULTISTRING | SGF_ENUM))) { + if (this->last_clicked == pe && !sd->IsBoolSetting() && !(sd->flags & (SGF_MULTISTRING | SGF_ENUM))) { int64 value64 = value; /* Show the correct currency-translated value */ if (sd->flags & SGF_CURRENCY) value64 *= _currency->rate; @@ -2682,7 +2671,7 @@ struct GameSettingsWindow : Window { if (str == nullptr) return; assert(this->valuewindow_entry != nullptr); - const SettingDesc *sd = this->valuewindow_entry->setting; + const IntSettingDesc *sd = this->valuewindow_entry->setting; int32 value; if (!StrEmpty(str)) { @@ -2698,7 +2687,7 @@ struct GameSettingsWindow : Window { value = (int32)ClampToI32(llvalue); } else { - value = (int32)(size_t)sd->def; + value = sd->def; } SetSettingValue(this->valuewindow_entry->setting, value); @@ -2734,7 +2723,7 @@ struct GameSettingsWindow : Window { if (widget < 0) { /* Deal with drop down boxes on the panel. */ assert(this->valuedropdown_entry != nullptr); - const SettingDesc *sd = this->valuedropdown_entry->setting; + const IntSettingDesc *sd = this->valuedropdown_entry->setting; assert(sd->flags & (SGF_MULTISTRING | SGF_ENUM)); SetSettingValue(sd, index); diff --git a/src/settings_internal.h b/src/settings_internal.h index 543616ed1e..3531ad65e4 100644 --- a/src/settings_internal.h +++ b/src/settings_internal.h @@ -12,21 +12,6 @@ #include "saveload/saveload.h" -/** - * Convention/Type of settings. This is then further specified if necessary - * with the SLE_ (SLE_VAR/SLE_FILE) enums in saveload.h - * @see VarTypes - * @see SettingDescBase - */ -enum SettingDescType : byte { - SDT_NUMX = 0, ///< any number-type - SDT_BOOLX = 1, ///< a boolean number - SDT_ONEOFMANY = 2, ///< bitmasked number where only ONE bit may be set - SDT_MANYOFMANY = 3, ///< bitmasked number where MULTIPLE bits may be set - SDT_INTLIST = 4, ///< list of integers separated by a comma ',' - SDT_STDSTRING = 6, ///< \c std::string -}; - enum SettingGuiFlag : uint16 { /* 2 bytes allocated for a maximum of 16 flags. */ SGF_NONE = 0, @@ -93,6 +78,7 @@ struct SettingOnGuiCtrlData { int val; }; +struct IniItem; typedef bool OnChange(int32 var); ///< callback prototype on data modification typedef size_t OnConvert(const char *value); ///< callback prototype for conversion error typedef bool OnGuiCtrl(SettingOnGuiCtrlData &data); ///< callback prototype for GUI operations @@ -104,27 +90,6 @@ struct SettingDescEnumEntry { StringID str; }; -/** Properties of config file settings. */ -struct SettingDescBase { - const char *name; ///< name of the setting. Used in configuration file and for console - const void *def; ///< default value given when none is present - SettingDescType cmd; ///< various flags for the variable - SettingGuiFlag flags; ///< handles how a setting would show up in the GUI (text/currency, etc.) - int32 min; ///< minimum values - uint32 max; ///< maximum values - int32 interval; ///< the interval to use between settings in the 'settings' window. If interval is '0' the interval is dynamically determined - const char *many; ///< ONE/MANY_OF_MANY: string of possible values for this type - StringID str; ///< (translated) string with descriptive text; gui and console - StringID str_help; ///< (Translated) string with help text; gui only. - StringID str_val; ///< (Translated) first string describing the value. - OnChange *proc; ///< callback procedure for when the value is changed - OnGuiCtrl *guiproc; ///< callback procedure for GUI operations - OnConvert *proc_cnvt; ///< callback procedure when loading value mechanism fails - SettingCategory cat; ///< assigned categories of the setting - bool startup; ///< setting has to be loaded directly at startup? - const SettingDescEnumEntry *enumlist; ///< For SGF_ENUM. The last entry must use STR_NULL -}; - struct SettingsXref { const char *target; OnXrefValueConvert *conv; @@ -133,20 +98,215 @@ struct SettingsXref { SettingsXref(const char *target_, OnXrefValueConvert *conv_) : target(target_), conv(conv_) {} }; -struct SettingDesc : SettingDescBase { +/** Properties of config file settings. */ +struct SettingDesc { + struct XrefContructorTag {}; + SettingDesc(SaveLoad save, const char *name, SettingGuiFlag flags, OnGuiCtrl *guiproc, bool startup, const char *patx_name) : + name(name), flags(flags), guiproc(guiproc), startup(startup), save(save), patx_name(patx_name) {} + SettingDesc(XrefContructorTag tag, SaveLoad save, SettingsXref xref) : + name(nullptr), flags(SGF_NONE), guiproc(nullptr), startup(false), save(save), patx_name(nullptr), xref(xref) {} + virtual ~SettingDesc() {} + + const char *name; ///< name of the setting. Used in configuration file and for console + SettingGuiFlag flags; ///< handles how a setting would show up in the GUI (text/currency, etc.) + OnGuiCtrl *guiproc; ///< callback procedure for GUI operations + bool startup; ///< setting has to be loaded directly at startup? SaveLoad save; ///< Internal structure (going to savegame, parts to config) + const char *patx_name; ///< Name to save/load setting from in PATX chunk, if nullptr save/load from PATS chunk as normal SettingsXref xref; ///< Details of SettingDesc to use instead of the contents of this one, useful for loading legacy savegames, if target field nullptr save/load as normal bool IsEditable(bool do_command = false) const; SettingType GetType() const; + + /** + * Check whether this setting is an integer type setting. + * @return True when the underlying type is an integer. + */ + virtual bool IsIntSetting() const { return false; } + + /** + * Check whether this setting is an string type setting. + * @return True when the underlying type is a string. + */ + virtual bool IsStringSetting() const { return false; } + + const struct IntSettingDesc *AsIntSetting() const; + const struct StringSettingDesc *AsStringSetting() const; + + /** + * Format the value of the setting associated with this object. + * @param buf The before of the buffer to format into. + * @param last The end of the buffer to format into. + * @param object The object the setting is in. + */ + virtual void FormatValue(char *buf, const char *last, const void *object) const = 0; + + /** + * Parse/read the value from the Ini item into the setting associated with this object. + * @param item The Ini item with the content of this setting. + * @param object The object the setting is in. + */ + virtual void ParseValue(const IniItem *item, void *object) const = 0; + + /** + * Check whether the value in the Ini item is the same as is saved in this setting in the object. + * It might be that determining whether the value is the same is way more expensive than just + * writing the value. In those cases this function may unconditionally return false even though + * the value might be the same as in the Ini item. + * @param item The Ini item with the content of this setting. + * @param object The object the setting is in. + * @return True if the value is definitely the same (might be false when the same). + */ + virtual bool IsSameValue(const IniItem *item, void *object) const = 0; }; -typedef std::initializer_list SettingTable; +/** Base integer type, including boolean, settings. Only these are shown in the settings UI. */ +struct IntSettingDesc : SettingDesc { + IntSettingDesc(SaveLoad save, const char *name, SettingGuiFlag flags, OnGuiCtrl *guiproc, bool startup, const char *patx_name, int32 def, + int32 min, uint32 max, int32 interval, StringID str, StringID str_help, StringID str_val, + SettingCategory cat, OnChange *proc, const SettingDescEnumEntry *enumlist) : + SettingDesc(save, name, flags, guiproc, startup, patx_name), def(def), min(min), max(max), interval(interval), + str(str), str_help(str_help), str_val(str_val), cat(cat), proc(proc), enumlist(enumlist) {} + virtual ~IntSettingDesc() {} + + int32 def; ///< default value given when none is present + int32 min; ///< minimum values + uint32 max; ///< maximum values + int32 interval; ///< the interval to use between settings in the 'settings' window. If interval is '0' the interval is dynamically determined + StringID str; ///< (translated) string with descriptive text; gui and console + StringID str_help; ///< (Translated) string with help text; gui only. + StringID str_val; ///< (Translated) first string describing the value. + SettingCategory cat; ///< assigned categories of the setting + OnChange *proc; ///< callback procedure for when the value is changed + + const SettingDescEnumEntry *enumlist; ///< For SGF_ENUM. The last entry must use STR_NULL + + /** + * Check whether this setting is a boolean type setting. + * @return True when the underlying type is an integer. + */ + virtual bool IsBoolSetting() const { return false; } + bool IsIntSetting() const override { return true; } + + void ChangeValue(const void *object, int32 newvalue) const; + void Write_ValidateSetting(const void *object, int32 value) const; + + virtual size_t ParseValue(const char *str) const; + void FormatValue(char *buf, const char *last, const void *object) const override; + void ParseValue(const IniItem *item, void *object) const override; + bool IsSameValue(const IniItem *item, void *object) const override; + int32 Read(const void *object) const; +}; + +/** Boolean setting. */ +struct BoolSettingDesc : IntSettingDesc { + BoolSettingDesc(SaveLoad save, const char *name, SettingGuiFlag flags, OnGuiCtrl *guiproc, bool startup, const char *patx_name, bool def, + StringID str, StringID str_help, StringID str_val, SettingCategory cat, OnChange *proc) : + IntSettingDesc(save, name, flags, guiproc, startup, patx_name, def, 0, 1, 0, str, str_help, str_val, cat, proc, nullptr) {} + virtual ~BoolSettingDesc() {} + + bool IsBoolSetting() const override { return true; } + size_t ParseValue(const char *str) const override; + void FormatValue(char *buf, const char *last, const void *object) const override; +}; + +/** One of many setting. */ +struct OneOfManySettingDesc : IntSettingDesc { + OneOfManySettingDesc(SaveLoad save, const char *name, SettingGuiFlag flags, OnGuiCtrl *guiproc, bool startup, const char *patx_name, + int32 def, int32 max, StringID str, StringID str_help, StringID str_val, SettingCategory cat, OnChange *proc, + std::initializer_list many, OnConvert *many_cnvt) : + IntSettingDesc(save, name, flags, guiproc, startup, patx_name, def, 0, max, 0, str, str_help, str_val, cat, proc, nullptr), many_cnvt(many_cnvt) + { + for (auto one : many) this->many.push_back(one); + } + + virtual ~OneOfManySettingDesc() {} + + std::vector many; ///< possible values for this type + OnConvert *many_cnvt; ///< callback procedure when loading value mechanism fails + + static size_t ParseSingleValue(const char *str, size_t len, const std::vector &many); + char *FormatSingleValue(char *buf, const char *last, uint id) const; + + size_t ParseValue(const char *str) const override; + void FormatValue(char *buf, const char *last, const void *object) const override; +}; + +/** Many of many setting. */ +struct ManyOfManySettingDesc : OneOfManySettingDesc { + ManyOfManySettingDesc(SaveLoad save, const char *name, SettingGuiFlag flags, OnGuiCtrl *guiproc, bool startup, const char *patx_name, + int32 def, StringID str, StringID str_help, StringID str_val, SettingCategory cat, OnChange *proc, + std::initializer_list many, OnConvert *many_cnvt) : + OneOfManySettingDesc(save, name, flags, guiproc, startup, patx_name, def, (1 << many.size()) - 1, str, str_help, + str_val, cat, proc, many, many_cnvt) {} + virtual ~ManyOfManySettingDesc() {} + + size_t ParseValue(const char *str) const override; + void FormatValue(char *buf, const char *last, const void *object) const override; +}; + +/** String settings. */ +struct StringSettingDesc : SettingDesc { + StringSettingDesc(SaveLoad save, const char *name, SettingGuiFlag flags, OnGuiCtrl *guiproc, bool startup, const char *patx_name, const char *def, + uint32 max_length, OnChange proc) : + SettingDesc(save, name, flags, guiproc, startup, patx_name), def(def), max_length(max_length), proc(proc) {} + virtual ~StringSettingDesc() {} + + const char *def; ///< default value given when none is present + uint32 max_length; ///< maximum length of the string, 0 means no maximum length + OnChange *proc; ///< callback procedure for when the value is changed + + bool IsStringSetting() const override { return true; } + void ChangeValue(const void *object, const char *newval) const; + void Write_ValidateSetting(const void *object, const char *str) const; + + void FormatValue(char *buf, const char *last, const void *object) const override; + void ParseValue(const IniItem *item, void *object) const override; + bool IsSameValue(const IniItem *item, void *object) const override; + const std::string &Read(const void *object) const; +}; + +/** List/array settings. */ +struct ListSettingDesc : SettingDesc { + ListSettingDesc(SaveLoad save, const char *name, SettingGuiFlag flags, OnGuiCtrl *guiproc, bool startup, const char *patx_name, const char *def) : + SettingDesc(save, name, flags, guiproc, startup, patx_name), def(def) {} + virtual ~ListSettingDesc() {} + + const char *def; ///< default value given when none is present + + void FormatValue(char *buf, const char *last, const void *object) const override; + void ParseValue(const IniItem *item, void *object) const override; + bool IsSameValue(const IniItem *item, void *object) const override; +}; + +/** Placeholder for settings that have been removed, but might still linger in the savegame. */ +struct NullSettingDesc : SettingDesc { + NullSettingDesc(SaveLoad save) : + SettingDesc(save, "", SGF_NONE, nullptr, false, nullptr) {} + virtual ~NullSettingDesc() {} + + void FormatValue(char *buf, const char *last, const void *object) const override { NOT_REACHED(); } + void ParseValue(const IniItem *item, void *object) const override { NOT_REACHED(); } + bool IsSameValue(const IniItem *item, void *object) const override { NOT_REACHED(); } +}; + +/** Setting cross-reference type. */ +struct XrefSettingDesc : SettingDesc { + XrefSettingDesc(SaveLoad save, SettingsXref xref) : + SettingDesc(SettingDesc::XrefContructorTag(), save, xref) {} + virtual ~XrefSettingDesc() {} + + void FormatValue(char *buf, const char *last, const void *object) const override { NOT_REACHED(); } + void ParseValue(const IniItem *item, void *object) const override { NOT_REACHED(); } + bool IsSameValue(const IniItem *item, void *object) const override { NOT_REACHED(); } +}; + +typedef std::initializer_list> SettingTable; const SettingDesc *GetSettingFromName(const char *name, bool ignore_version = false); -bool SetSettingValue(const SettingDesc *sd, int32 value, bool force_newgame = false); -bool SetSettingValue(const SettingDesc *sd, const char *value, bool force_newgame = false); +bool SetSettingValue(const IntSettingDesc *sd, int32 value, bool force_newgame = false); +bool SetSettingValue(const StringSettingDesc *sd, const char *value, bool force_newgame = false); uint GetSettingIndex(const SettingDesc *sd); #endif /* SETTINGS_INTERNAL_H */ diff --git a/src/table/company_settings.ini b/src/table/company_settings.ini index 60052d834d..d25e2f9aa5 100644 --- a/src/table/company_settings.ini +++ b/src/table/company_settings.ini @@ -16,8 +16,8 @@ static const SettingTable _company_settings{ [post-amble] }; [templates] -SDT_BOOL = SDT_BOOL($base, $var, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, $patxname), -SDT_VAR = SDT_VAR($base, $var, $type, $flags, $guiflags, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, $patxname), +SDT_BOOL = SDT_BOOL($base, $var, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $from, $to, $extver, $cat, $guiproc, $startup, $patxname), +SDT_VAR = SDT_VAR($base, $var, $type, $flags, $guiflags, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $from, $to, $extver, $cat, $guiproc, $startup, $patxname), SDT_NULL = SDT_NULL($length, $from, $to, $extver), [validation] diff --git a/src/table/currency_settings.ini b/src/table/currency_settings.ini index c446d808a3..e450fc1b48 100644 --- a/src/table/currency_settings.ini +++ b/src/table/currency_settings.ini @@ -9,8 +9,8 @@ static const SettingTable _currency_settings{ [post-amble] }; [templates] -SDT_VAR = SDT_VAR ($base, $var, $type, $flags, $guiflags, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, nullptr), -SDT_SSTR = SDT_SSTR($base, $var, $type, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, nullptr), +SDT_VAR = SDT_VAR ($base, $var, $type, $flags, $guiflags, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $from, $to, $extver, $cat, $guiproc, $startup, nullptr), +SDT_SSTR = SDT_SSTR($base, $var, $type, $flags, $guiflags, $def, $proc, $from, $to, $extver, $cat, $guiproc, $startup, nullptr), [validation] SDT_VAR = static_assert($max <= MAX_$type, "Maximum value for $base.$var exceeds storage size"); diff --git a/src/table/gameopt_settings.ini b/src/table/gameopt_settings.ini index d4bf8286a9..34ebf47edb 100644 --- a/src/table/gameopt_settings.ini +++ b/src/table/gameopt_settings.ini @@ -12,17 +12,17 @@ uint8 _old_units; ///< Old units from old s /* Most of these strings are used both for gameopt-backward compatibility * and the settings tables. The rest is here for consistency. */ -static const char *_locale_currencies = "GBP|USD|EUR|YEN|ATS|BEF|CHF|CZK|DEM|DKK|ESP|FIM|FRF|GRD|HUF|ISK|ITL|NLG|NOK|PLN|RON|RUR|SIT|SEK|YTL|SKK|BRL|EEK|custom"; -static const char *_locale_units = "imperial|metric|si|gameunits"; -static const char *_town_names = "english|french|german|american|latin|silly|swedish|dutch|finnish|polish|slovak|norwegian|hungarian|austrian|romanian|czech|swiss|danish|turkish|italian|catalan"; -static const char *_climates = "temperate|arctic|tropic|toyland"; -static const char *_autosave_interval = "off|monthly|quarterly|half year|yearly"; -static const char *_roadsides = "left|right"; -static const char *_savegame_date = "long|short|iso"; -static const char *_savegame_overwrite_confirm = "no|different|not same|yes"; -static const char *_osk_activation = "disabled|double|single|immediately"; -static const char *_settings_profiles = "easy|medium|hard"; -static const char *_news_display = "off|summarized|full"; +static std::initializer_list _locale_currencies{"GBP", "USD", "EUR", "YEN", "ATS", "BEF", "CHF", "CZK", "DEM", "DKK", "ESP", "FIM", "FRF", "GRD", "HUF", "ISK", "ITL", "NLG", "NOK", "PLN", "RON", "RUR", "SIT", "SEK", "YTL", "SKK", "BRL", "EEK", "custom"}; +static std::initializer_list _locale_units{"imperial", "metric", "si", "gameunits"}; +static std::initializer_list _town_names{"english", "french", "german", "american", "latin", "silly", "swedish", "dutch", "finnish", "polish", "slovak", "norwegian", "hungarian", "austrian", "romanian", "czech", "swiss", "danish", "turkish", "italian", "catalan"}; +static std::initializer_list _climates{"temperate", "arctic", "tropic", "toyland"}; +static std::initializer_list _autosave_interval{"off", "monthly", "quarterly", "half year", "yearly"}; +static std::initializer_list _roadsides{"left", "right"}; +static std::initializer_list _savegame_date{"long", "short", "iso"}; +static std::initializer_list _savegame_overwrite_confirm{"no", "different", "not same", "yes"}; +static std::initializer_list _osk_activation{"disabled", "double", "single", "immediately"}; +static std::initializer_list _settings_profiles{"easy", "medium", "hard"}; +static std::initializer_list _news_display{ "off", "summarized", "full"}; static const SettingTable _gameopt_settings{ /* In version 4 a new difficulty setting has been added to the difficulty settings, @@ -37,13 +37,13 @@ static const SettingTable _gameopt_settings{ [post-amble] }; [templates] -SDTG_GENERAL = SDTG_GENERAL($name, $sdt_cmd, $sle_cmd, $type, $flags, $guiflags, $var, $length, $def, $min, $max, $interval, $full, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, nullptr), -SDTG_VAR = SDTG_VAR($name, $type, $flags, $guiflags, $var, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, nullptr), -SDT_NULL = SDT_NULL($length, $from, $to, $extver), -SDTC_OMANY = SDTC_OMANY( $var, $type, $flags, $guiflags, $def, $max, $full, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, nullptr), -SDTG_OMANY = SDTG_OMANY($name, $type, $flags, $guiflags, $var, $def, $max, $full, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, nullptr), -SDT_OMANY = SDT_OMANY($base, $var, $type, $flags, $guiflags, $def, $max, $full, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $load, $cat, $startup, $extver, nullptr), -SDT_VAR = SDT_VAR($base, $var, $type, $flags, $guiflags, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, nullptr), +SDTG_LIST = SDTG_LIST($name, $type, $flags, $guiflags, $var, $def, $length, $from, $to, $extver, $cat, $guiproc, $startup, nullptr), +SDTG_VAR = SDTG_VAR($name, $type, $flags, $guiflags, $var, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $from, $to, $extver, $cat, $guiproc, $startup, nullptr), +SDT_NULL = SDT_NULL( $length, $from, $to, $extver), +SDTC_OMANY = SDTC_OMANY( $var, $type, $flags, $guiflags, $def, $max, $full, $str, $strhelp, $strval, $proc, $from, $to, $extver, $cat, $guiproc, $startup, nullptr), +SDTG_OMANY = SDTG_OMANY($name, $type, $flags, $guiflags, $var, $def, $max, $full, $str, $strhelp, $strval, $proc, $from, $to, $extver, $cat, $guiproc, $startup, nullptr), +SDT_OMANY = SDT_OMANY($base, $var, $type, $flags, $guiflags, $def, $max, $full, $str, $strhelp, $strval, $proc, $from, $to, $extver, $load, $cat, $guiproc, $startup, nullptr), +SDT_VAR = SDT_VAR($base, $var, $type, $flags, $guiflags, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $from, $to, $extver, $cat, $guiproc, $startup, nullptr), [validation] SDTG_VAR = static_assert($max <= MAX_$type, "Maximum value for $var exceeds storage size"); @@ -70,7 +70,7 @@ extver = SlXvFeatureTest() -[SDTG_GENERAL] +[SDTG_LIST] name = ""diff_custom"" sdt_cmd = SDT_INTLIST sle_cmd = SL_ARR @@ -78,13 +78,10 @@ type = SLE_FILE_I16 | SLE_VAR_U16 flags = SLF_NOT_IN_CONFIG var = _old_diff_custom length = 17 -def = 0 -min = 0 -max = 0 -full = nullptr +def = nullptr to = SLV_4 -[SDTG_GENERAL] +[SDTG_LIST] name = ""diff_custom"" sdt_cmd = SDT_INTLIST sle_cmd = SL_ARR @@ -92,9 +89,7 @@ type = SLE_UINT16 flags = SLF_NOT_IN_CONFIG var = _old_diff_custom length = 18 -def = 0 -min = 0 -max = 0 +def = nullptr full = nullptr from = SLV_4 diff --git a/src/table/misc_settings.ini b/src/table/misc_settings.ini index 4d74d05253..d4dc5229f1 100644 --- a/src/table/misc_settings.ini +++ b/src/table/misc_settings.ini @@ -7,7 +7,9 @@ [pre-amble] extern std::string _config_language_file; -static const char *_support8bppmodes = "no|system|hardware"; +static std::initializer_list _support8bppmodes{"no", "system" , "hardware"}; +static std::initializer_list _display_opt_modes{"SHOW_TOWN_NAMES", "SHOW_STATION_NAMES", "SHOW_SIGNS", "FULL_ANIMATION", "", "FULL_DETAIL", "WAYPOINTS", "SHOW_COMPETITOR_SIGNS"}; +static std::initializer_list _extra_display_opt_modes{"SHOW_MONEY_TEXT_EFFECTS"}; #ifdef WITH_COCOA extern bool _allow_hidpi_window; @@ -20,12 +22,12 @@ static const SettingTable _misc_settings{ [post-amble] }; [templates] -SDTG_LIST = SDTG_LIST($name, $type, $length, $flags, $guiflags, $var, $def, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, nullptr), -SDTG_MMANY = SDTG_MMANY($name, $type, $flags, $guiflags, $var, $def, $full, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, nullptr), -SDTG_OMANY = SDTG_OMANY($name, $type, $flags, $guiflags, $var, $def, $max, $full, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, nullptr), -SDTG_SSTR = SDTG_SSTR($name, $type, $flags, $guiflags, $var, $def, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, nullptr), -SDTG_BOOL = SDTG_BOOL($name, $flags, $guiflags, $var, $def, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, nullptr), -SDTG_VAR = SDTG_VAR($name, $type, $flags, $guiflags, $var, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, nullptr), +SDTG_LIST = SDTG_LIST($name, $type, $flags, $guiflags, $var, $def, $length, $from, $to, $extver, $cat, $guiproc, $startup, nullptr), +SDTG_MMANY = SDTG_MMANY($name, $type, $flags, $guiflags, $var, $def, $full, $str, $strhelp, $strval, $proc, $from, $to, $extver, $cat, $guiproc, $startup, nullptr), +SDTG_OMANY = SDTG_OMANY($name, $type, $flags, $guiflags, $var, $def, $max, $full, $str, $strhelp, $strval, $proc, $from, $to, $extver, $cat, $guiproc, $startup, nullptr), +SDTG_SSTR = SDTG_SSTR($name, $type, $flags, $guiflags, $var, $def, 0, $proc, $from, $to, $extver, $cat, $guiproc, $startup, nullptr), +SDTG_BOOL = SDTG_BOOL($name, $flags, $guiflags, $var, $def, $str, $strhelp, $strval, $proc, $from, $to, $extver, $cat, $guiproc, $startup, nullptr), +SDTG_VAR = SDTG_VAR($name, $type, $flags, $guiflags, $var, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $from, $to, $extver, $cat, $guiproc, $startup, nullptr), [validation] SDTG_VAR = static_assert($max <= MAX_$type, "Maximum value for $var exceeds storage size"); @@ -54,14 +56,14 @@ name = ""display_opt"" type = SLE_UINT8 var = _display_opt def = (1 << DO_SHOW_TOWN_NAMES | 1 << DO_SHOW_STATION_NAMES | 1 << DO_SHOW_SIGNS | 1 << DO_FULL_ANIMATION | 1 << DO_FULL_DETAIL | 1 << DO_SHOW_WAYPOINT_NAMES | 1 << DO_SHOW_COMPETITOR_SIGNS) -full = ""SHOW_TOWN_NAMES|SHOW_STATION_NAMES|SHOW_SIGNS|FULL_ANIMATION||FULL_DETAIL|WAYPOINTS|SHOW_COMPETITOR_SIGNS"" +full = _display_opt_modes [SDTG_MMANY] name = ""extra_display_opt"" type = SLE_UINT8 var = _extra_display_opt def = (1 << XDO_SHOW_MONEY_TEXT_EFFECTS) -full = ""SHOW_MONEY_TEXT_EFFECTS"" +full = _extra_display_opt_modes [SDTG_BOOL] name = ""fullscreen"" diff --git a/src/table/settings.h.preamble b/src/table/settings.h.preamble index bf066f0249..59fd13568d 100644 --- a/src/table/settings.h.preamble +++ b/src/table/settings.h.preamble @@ -54,101 +54,71 @@ static size_t ConvertLandscape(const char *value); * on the appropriate macro. */ -#define NSD_GENERAL(name, def, cmd, guiflags, min, max, interval, many, str, strhelp, strval, proc, guiproc, load, cat, startup, enumlist)\ - {name, (const void*)(size_t)(def), cmd, guiflags, min, max, interval, many, str, strhelp, strval, proc, guiproc, load, cat, startup, enumlist} +#define NSD(type, ...) std::unique_ptr(new type##SettingDesc(__VA_ARGS__)) /* Macros for various objects to go in the configuration file. * This section is for global variables */ -#define SDTG_GENERAL2(name, sdt_cmd, sle_cmd, type, flags, guiflags, var, length, def, min, max, interval, full, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname, enumlist)\ - {NSD_GENERAL(name, def, sdt_cmd, guiflags, min, max, interval, full, str, strhelp, strval, proc, guiproc, nullptr, cat, startup, enumlist), SLEG_GENERAL_X(sle_cmd, var, type | flags, length, from, to, extver), patxname, SettingsXref()} +#define SDTG_VAR(name, type, flags, guiflags, var, def, min, max, interval, str, strhelp, strval, proc, from, to, extver, cat, guiproc, startup, patxname)\ + NSD(Int, SLEG_GENERAL_X(SL_VAR, var, type | flags, 1, from, to, extver), name, guiflags, guiproc, startup, patxname, def, min, max, interval, str, strhelp, strval, cat, proc, nullptr) -#define SDTG_GENERAL(name, sdt_cmd, sle_cmd, type, flags, guiflags, var, length, def, min, max, interval, full, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname)\ - SDTG_GENERAL2(name, sdt_cmd, sle_cmd, type, flags, guiflags, var, length, def, min, max, interval, full, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname, nullptr) +#define SDTG_BOOL(name, flags, guiflags, var, def, str, strhelp, strval, proc, from, to, extver, cat, guiproc, startup, patxname)\ + NSD(Bool, SLEG_GENERAL_X(SL_VAR, var, SLE_BOOL | flags, 1, from, to, extver), name, guiflags, guiproc, startup, patxname, def, str, strhelp, strval, cat, proc) -#define SDTG_VAR(name, type, flags, guiflags, var, def, min, max, interval, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname)\ - SDTG_GENERAL2(name, SDT_NUMX, SL_VAR, type, flags, guiflags, var, 0, def, min, max, interval, nullptr, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname, nullptr) +#define SDTG_LIST(name, type, flags, guiflags, var, def, length, from, to, extver, cat, guiproc, startup, patxname)\ + NSD(List, SLEG_GENERAL_X(SL_ARR, var, type | flags, length, from, to, extver), name, guiflags, guiproc, startup, patxname, def) -#define SDTG_ENUM(name, type, flags, guiflags, var, def, str, strhelp, proc, guiproc, from, to, cat, startup, extver, patxname, enumlist)\ - SDTG_GENERAL2(name, SDT_NUMX, SL_VAR, type, flags, guiflags | SGF_ENUM, var, 0, def, 0, 0, 0, nullptr, str, strhelp, STR_NULL, proc, guiproc, nullptr, from, to, cat, startup, extver, patxname, enumlist) +#define SDTG_SSTR(name, type, flags, guiflags, var, def, max_length, proc, from, to, extver, cat, guiproc, startup, patxname)\ + NSD(String, SLEG_GENERAL_X(SL_STDSTR, var, type | flags, sizeof(var), from, to, extver), name, guiflags, guiproc, startup, patxname, def, max_length, proc) -#define SDTG_BOOL(name, flags, guiflags, var, def, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname)\ - SDTG_GENERAL(name, SDT_BOOLX, SL_VAR, SLE_BOOL, flags, guiflags, var, 0, def, 0, 1, 0, nullptr, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname) +#define SDTG_OMANY(name, type, flags, guiflags, var, def, max, full, str, strhelp, strval, proc, from, to, extver, cat, guiproc, startup, patxname)\ + NSD(OneOfMany, SLEG_GENERAL_X(SL_VAR, var, type | flags, 1, from, to, extver), name, guiflags, guiproc, startup, patxname, def, max, str, strhelp, strval, cat, proc, full, nullptr) -#define SDTG_LIST(name, type, length, flags, guiflags, var, def, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname)\ - SDTG_GENERAL(name, SDT_INTLIST, SL_ARR, type, flags, guiflags, var, length, def, 0, 0, 0, nullptr, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname) - -#define SDTG_SSTR(name, type, flags, guiflags, var, def, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname)\ - SDTG_GENERAL(name, SDT_STDSTRING, SL_STDSTR, type, flags, guiflags, var, sizeof(var), def, 0, 0, 0, nullptr, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname) - -#define SDTG_STR(name, type, flags, guiflags, var, def, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname)\ - SDTG_GENERAL(name, SDT_STRING, SL_STR, type, flags, guiflags, var, sizeof(var), def, 0, 0, 0, nullptr, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname) - -#define SDTG_OMANY(name, type, flags, guiflags, var, def, max, full, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname)\ - SDTG_GENERAL(name, SDT_ONEOFMANY, SL_VAR, type, flags, guiflags, var, 0, def, 0, max, 0, full, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname) - -#define SDTG_MMANY(name, type, flags, guiflags, var, def, full, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname)\ - SDTG_GENERAL(name, SDT_MANYOFMANY, SL_VAR, type, flags, guiflags, var, 0, def, 0, 0, 0, full, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname) +#define SDTG_MMANY(name, type, flags, guiflags, var, def, full, str, strhelp, strval, proc, from, to, extver, cat, guiproc, startup, patxname)\ + NSD(ManyOfMany, SLEG_GENERAL_X(SL_VAR, var, type | flags, 1, from, to, extver), name, guiflags, guiproc, startup, patxname, def, str, strhelp, strval, cat, proc, full, nullptr) #define SDTG_NULL(length, from, to, extver)\ - {{"", nullptr, SDT_NUMX, SGF_NONE, 0, 0, 0, nullptr, STR_NULL, STR_NULL, STR_NULL, nullptr, nullptr, nullptr, SC_NONE, false, nullptr}, SLEG_NULL_X(length, from, to, extver), nullptr, SettingsXref()} + NSD(Null, SLE_CONDNULL_X(length, from, to, extver)) /* Macros for various objects to go in the configuration file. * This section is for structures where their various members are saved */ -#define SDT_GENERAL2(name, sdt_cmd, sle_cmd, type, flags, guiflags, base, var, length, def, min, max, interval, full, str, strhelp, strval, proc, guiproc, load, from, to, cat, startup, extver, patxname, enumlist)\ - {NSD_GENERAL(name, def, sdt_cmd, guiflags, min, max, interval, full, str, strhelp, strval, proc, guiproc, load, cat, startup, enumlist), SLE_GENERAL_X(sle_cmd, base, var, type | flags, length, from, to, extver), patxname, SettingsXref()} +#define SDT_VAR(base, var, type, flags, guiflags, def, min, max, interval, str, strhelp, strval, proc, from, to, extver, cat, guiproc, startup, patxname)\ + NSD(Int, SLE_GENERAL_X(SL_VAR, base, var, type | flags, 1, from, to, extver), #var, guiflags, guiproc, startup, patxname, def, min, max, interval, str, strhelp, strval, cat, proc, nullptr) -#define SDT_GENERAL(name, sdt_cmd, sle_cmd, type, flags, guiflags, base, var, length, def, min, max, interval, full, str, strhelp, strval, proc, guiproc, load, from, to, cat, startup, extver, patxname)\ - SDT_GENERAL2(name, sdt_cmd, sle_cmd, type, flags, guiflags, base, var, length, def, min, max, interval, full, str, strhelp, strval, proc, guiproc, load, from, to, cat, startup, extver, patxname, nullptr) +#define SDT_ENUM(base, var, type, flags, guiflags, def, str, strhelp, proc, from, to, extver, cat, guiproc, startup, patxname, enumlist)\ + NSD(Int, SLE_GENERAL_X(SL_VAR, base, var, type | flags, 1, from, to, extver), #var, guiflags | SGF_ENUM, guiproc, startup, patxname, def, 0, 0, 0, str, strhelp, STR_NULL, cat, proc, enumlist) -#define SDT_VAR(base, var, type, flags, guiflags, def, min, max, interval, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname)\ - SDT_GENERAL2(#var, SDT_NUMX, SL_VAR, type, flags, guiflags, base, var, 1, def, min, max, interval, nullptr, str, strhelp, strval, proc, guiproc, nullptr, from, to, cat, startup, extver, patxname, nullptr) +#define SDT_BOOL(base, var, flags, guiflags, def, str, strhelp, strval, proc, from, to, extver, cat, guiproc, startup, patxname)\ + NSD(Bool, SLE_GENERAL_X(SL_VAR, base, var, SLE_BOOL | flags, 1, from, to, extver), #var, guiflags, guiproc, startup, patxname, def, str, strhelp, strval, cat, proc) -#define SDT_ENUM(base, var, type, flags, guiflags, def, str, strhelp, proc, guiproc, from, to, cat, startup, extver, patxname, enumlist)\ - SDT_GENERAL2(#var, SDT_NUMX, SL_VAR, type, flags, guiflags | SGF_ENUM, base, var, 1, def, 0, 0, 0, nullptr, str, strhelp, STR_NULL, proc, guiproc, nullptr, from, to, cat, startup, extver, patxname, enumlist) +#define SDT_LIST(base, var, type, flags, guiflags, def, from, to, extver, cat, guiproc, startup, patxname)\ + NSD(List, SLE_GENERAL_X(SL_ARR, base, var, type | flags, lengthof(((base*)8)->var), from, to, extver), #var, guiflags, guiproc, startup, patxname, def) -#define SDT_BOOL(base, var, flags, guiflags, def, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname)\ - SDT_GENERAL(#var, SDT_BOOLX, SL_VAR, SLE_BOOL, flags, guiflags, base, var, 1, def, 0, 1, 0, nullptr, str, strhelp, strval, proc, guiproc, nullptr, from, to, cat, startup, extver, patxname) +#define SDT_SSTR(base, var, type, flags, guiflags, def, proc, from, to, extver, cat, guiproc, startup, patxname)\ + NSD(String, SLE_GENERAL_X(SL_STDSTR, base, var, type | flags, sizeof(((base*)8)->var), from, to, extver), #var, guiflags, guiproc, startup, patxname, def, 0, proc) -#define SDT_LIST(base, var, type, flags, guiflags, def, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname)\ - SDT_GENERAL(#var, SDT_INTLIST, SL_ARR, type, flags, guiflags, base, var, lengthof(((base*)8)->var), def, 0, 0, 0, nullptr, str, strhelp, strval, proc, guiproc, nullptr, from, to, cat, startup, extver, patxname) +#define SDT_OMANY(base, var, type, flags, guiflags, def, max, full, str, strhelp, strval, proc, from, to, extver, load, cat, guiproc, startup, patxname)\ + NSD(OneOfMany, SLE_GENERAL_X(SL_VAR, base, var, type | flags, 1, from, to, extver), #var, guiflags, guiproc, startup, patxname, def, max, str, strhelp, strval, cat, proc, full, load) -#define SDT_STR(base, var, type, flags, guiflags, def, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname)\ - SDT_GENERAL(#var, SDT_STRING, SL_STR, type, flags, guiflags, base, var, sizeof(((base*)8)->var), def, 0, 0, 0, nullptr, str, strhelp, strval, proc, guiproc, nullptr, from, to, cat, startup, extver, patxname) - -#define SDT_SSTR(base, var, type, flags, guiflags, def, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname)\ - SDT_GENERAL(#var, SDT_STDSTRING, SL_STDSTR, type, flags, guiflags, base, var, sizeof(((base*)8)->var), def, 0, 0, 0, nullptr, str, strhelp, strval, proc, guiproc, nullptr, from, to, cat, startup, extver, patxname) - -#define SDT_OMANY(base, var, type, flags, guiflags, def, max, full, str, strhelp, strval, proc, guiproc, from, to, load, cat, startup, extver, patxname)\ - SDT_GENERAL(#var, SDT_ONEOFMANY, SL_VAR, type, flags, guiflags, base, var, 1, def, 0, max, 0, full, str, strhelp, strval, proc, guiproc, load, from, to, cat, startup, extver, patxname) - -#define SDT_MMANY(base, var, type, flags, guiflags, def, full, str, proc, guiproc, strhelp, strval, from, to, cat, startup, extver, patxname)\ - SDT_GENERAL(#var, SDT_MANYOFMANY, SL_VAR, type, flags, guiflags, base, var, 1, def, 0, 0, 0, full, str, strhelp, strval, proc, guiproc, nullptr, from, to, cat, startup, extver, patxname) +#define SDT_MMANY(base, var, type, flags, guiflags, def, full, str, proc, strhelp, strval, from, to, extver, cat, guiproc, startup, patxname)\ + NSD(ManyOfMany, SLE_GENERAL_X(SL_VAR, base, var, type | flags, 1, from, to, extver), #var, guiflags, guiproc, startup, patxname, def, str, strhelp, strval, cat, proc, full, nullptr) #define SDT_NULL(length, from, to, extver)\ - {{"", nullptr, SDT_NUMX, SGF_NONE, 0, 0, 0, nullptr, STR_NULL, STR_NULL, STR_NULL, nullptr, nullptr, nullptr, SC_NONE, false, nullptr}, SLE_CONDNULL_X(length, from, to, extver), nullptr, SettingsXref()} + NSD(Null, SLE_CONDNULL_X(length, from, to, extver)) +#define SDTC_VAR(var, type, flags, guiflags, def, min, max, interval, str, strhelp, strval, proc, from, to, extver, cat, guiproc, startup, patxname)\ + SDTG_VAR(#var, type, flags, guiflags, _settings_client.var, def, min, max, interval, str, strhelp, strval, proc, from, to, extver, cat, guiproc, startup, patxname) -#define SDTC_VAR(var, type, flags, guiflags, def, min, max, interval, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname)\ - SDTG_GENERAL2(#var, SDT_NUMX, SL_VAR, type, flags, guiflags, _settings_client.var, 1, def, min, max, interval, nullptr, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname, nullptr) +#define SDTC_BOOL(var, flags, guiflags, def, str, strhelp, strval, proc, from, to, extver, cat, guiproc, startup, patxname)\ + SDTG_BOOL(#var, flags, guiflags, _settings_client.var, def, str, strhelp, strval, proc, from, to, extver, cat, guiproc, startup, patxname) -#define SDTC_ENUM(var, type, flags, guiflags, def, str, strhelp, proc, guiproc, from, to, cat, startup, extver, patxname, enumlist)\ - SDTG_GENERAL2(#var, SDT_NUMX, SL_VAR, type, flags, guiflags | SGF_ENUM, _settings_client.var, 1, def, 0, 0, 0, nullptr, str, strhelp, STR_NULL, proc, guiproc, nullptr, from, to, cat, startup, extver, patxname,, enumlist) +#define SDTC_LIST(var, type, flags, guiflags, def, from, to, extver, cat, guiproc, startup, patxname)\ + SDTG_LIST(#var, type, flags, guiflags, _settings_client.var, def, lengthof(_settings_client.var), from, to, extver, cat, guiproc, startup, patxname) -#define SDTC_BOOL(var, flags, guiflags, def, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname)\ - SDTG_GENERAL(#var, SDT_BOOLX, SL_VAR, SLE_BOOL, flags, guiflags, _settings_client.var, 1, def, 0, 1, 0, nullptr, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname) +#define SDTC_SSTR(var, type, flags, guiflags, def, max_length, proc, from, to, extver, cat, guiproc, startup, patxname)\ + SDTG_SSTR(#var, type, flags, guiflags, _settings_client.var, def, max_length, proc, from, to, extver, cat, guiproc, startup, patxname)\ -#define SDTC_LIST(var, type, flags, guiflags, def, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname)\ - SDTG_GENERAL(#var, SDT_INTLIST, SL_ARR, type, flags, guiflags, _settings_client.var, lengthof(_settings_client.var), def, 0, 0, 0, nullptr, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname) - -#define SDTC_STR(var, type, flags, guiflags, def, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname)\ - SDTG_GENERAL(#var, SDT_STRING, SL_STR, type, flags, guiflags, _settings_client.var, sizeof(_settings_client.var), def, 0, 0, 0, nullptr, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname) - -#define SDTC_SSTR(var, type, flags, guiflags, def, max_length, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname)\ - SDTG_GENERAL(#var, SDT_STDSTRING, SL_STDSTR, type, flags, guiflags, _settings_client.var, sizeof(_settings_client.var), def, 0, max_length, 0, nullptr, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname) - -#define SDTC_OMANY(var, type, flags, guiflags, def, max, full, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname)\ - SDTG_GENERAL(#var, SDT_ONEOFMANY, SL_VAR, type, flags, guiflags, _settings_client.var, 1, def, 0, max, 0, full, str, strhelp, strval, proc, guiproc, from, to, cat, startup, extver, patxname) +#define SDTC_OMANY(var, type, flags, guiflags, def, max, full, str, strhelp, strval, proc, from, to, extver, cat, guiproc, startup, patxname)\ + SDTG_OMANY(#var, type, flags, guiflags, _settings_client.var, def, max, full, str, strhelp, strval, proc, from, to, extver, cat, guiproc, startup, patxname) #define SDT_XREF(from, to, extver, xref, xrefcvt)\ - {{"", nullptr, SDT_NUMX, SGF_NONE, 0, 0, 0, nullptr, STR_NULL, STR_NULL, STR_NULL, nullptr, nullptr, nullptr, SC_NONE, false, nullptr}, SLE_CONDNULL_X(0, from, to, extver), nullptr, SettingsXref(xref, xrefcvt)} - + NSD(Xref, SLE_CONDNULL_X(0, from, to, extver), SettingsXref(xref, xrefcvt)) diff --git a/src/table/settings.ini b/src/table/settings.ini index 5b2060530a..f02a5d9925 100644 --- a/src/table/settings.ini +++ b/src/table/settings.ini @@ -143,24 +143,24 @@ const SettingTable _settings{ [post-amble] }; [templates] -SDTG_BOOL = SDTG_BOOL($name, $flags, $guiflags, $var, $def, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, $patxname), -SDTG_VAR = SDTG_VAR($name, $type, $flags, $guiflags, $var, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, $patxname), -SDTG_ENUM = SDTG_ENUM($name, $type, $flags, $guiflags, $var, $def, $str, $strhelp, $proc, $guiproc, $from, $to, $cat, $startup, $extver, $patxname, $enumlist), -SDTG_OMANY = SDTG_OMANY($name, $type, $flags, $guiflags, $var, $def, $max, $full, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, $patxname), -SDTC_BOOL = SDTC_BOOL( $var, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, $patxname), -SDTC_LIST = SDTC_LIST( $var, $type, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, $patxname), -SDTC_OMANY = SDTC_OMANY( $var, $type, $flags, $guiflags, $def, $max, $full, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, $patxname), -SDTC_SSTR = SDTC_SSTR( $var, $type, $flags, $guiflags, $def, $length, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, $patxname), -SDTC_VAR = SDTC_VAR( $var, $type, $flags, $guiflags, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, $patxname), -SDT_BOOL = SDT_BOOL($base, $var, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, $patxname), -SDT_OMANY = SDT_OMANY($base, $var, $type, $flags, $guiflags, $def, $max, $full, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $load, $cat, $startup, $extver, $patxname), -SDT_SSTR = SDT_SSTR($base, $var, $type, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, $patxname), -SDT_VAR = SDT_VAR($base, $var, $type, $flags, $guiflags, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, $patxname), -SDT_ENUM = SDT_ENUM($base, $var, $type, $flags, $guiflags, $def, $str, $strhelp, $proc, $guiproc, $from, $to, $cat, $startup, $extver, $patxname, $enumlist), +SDTG_BOOL = SDTG_BOOL($name, $flags, $guiflags, $var, $def, $str, $strhelp, $strval, $proc, $from, $to, $extver, $cat, $guiproc, $startup, $patxname), +SDTG_VAR = SDTG_VAR($name, $type, $flags, $guiflags, $var, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $from, $to, $extver, $cat, $guiproc, $startup, $patxname), +SDTG_ENUM = SDTG_ENUM($name, $type, $flags, $guiflags, $var, $def, $str, $strhelp, $proc, $from, $to, $extver, $cat, $guiproc, $startup, $patxname, $enumlist), +SDTG_OMANY = SDTG_OMANY($name, $type, $flags, $guiflags, $var, $def, $max, $full, $str, $strhelp, $strval, $proc, $from, $to, $extver, $cat, $guiproc, $startup, $patxname), +SDTC_BOOL = SDTC_BOOL( $var, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $from, $to, $extver, $cat, $guiproc, $startup, $patxname), +SDTC_LIST = SDTC_LIST( $var, $type, $flags, $guiflags, $def, $from, $to, $extver, $cat, $guiproc, $startup, $patxname), +SDTC_OMANY = SDTC_OMANY( $var, $type, $flags, $guiflags, $def, $max, $full, $str, $strhelp, $strval, $proc, $from, $to, $extver, $cat, $guiproc, $startup, $patxname), +SDTC_SSTR = SDTC_SSTR( $var, $type, $flags, $guiflags, $def, $length, $proc, $from, $to, $extver, $cat, $guiproc, $startup, $patxname), +SDTC_VAR = SDTC_VAR( $var, $type, $flags, $guiflags, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $from, $to, $extver, $cat, $guiproc, $startup, $patxname), +SDT_BOOL = SDT_BOOL($base, $var, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $from, $to, $extver, $cat, $guiproc, $startup, $patxname), +SDT_OMANY = SDT_OMANY($base, $var, $type, $flags, $guiflags, $def, $max, $full, $str, $strhelp, $strval, $proc, $from, $to, $extver, $load, $cat, $guiproc, $startup, $patxname), +SDT_SSTR = SDT_SSTR($base, $var, $type, $flags, $guiflags, $def, $proc, $from, $to, $extver, $cat, $guiproc, $startup, $patxname), +SDT_VAR = SDT_VAR($base, $var, $type, $flags, $guiflags, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $from, $to, $extver, $cat, $guiproc, $startup, $patxname), +SDT_ENUM = SDT_ENUM($base, $var, $type, $flags, $guiflags, $def, $str, $strhelp, $proc, $from, $to, $extver, $cat, $guiproc, $startup, $patxname, $enumlist), SDT_NULL = SDT_NULL($length, $from, $to, $extver), SDT_XREF = SDT_XREF( $from, $to, $extver, $xref, $xrefcvt), -SDT_LINKGRAPH_PER_CARGO = SDT_ENUM(GameSettings, linkgraph.distribution_per_cargo[$linkgraph_cargo], SLE_UINT8, $flags | SLF_NOT_IN_CONFIG, $guiflags | SGF_NO_NEWGAME, DT_PER_CARGO_DEFAULT, STR_CONFIG_SETTING_DISTRIBUTION_PER_CARGO, STR_CONFIG_SETTING_DISTRIBUTION_PER_CARGO_HELPTEXT, $proc, LinkGraphDistributionSettingGUI, $from, $to, SC_EXPERT, false, SlXvFeatureTest(XSLFTO_AND, XSLFI_LINKGRAPH_MODES), nullptr, _linkgraph_mode_per_cargo), +SDT_LINKGRAPH_PER_CARGO = SDT_ENUM(GameSettings, linkgraph.distribution_per_cargo[$linkgraph_cargo], SLE_UINT8, $flags | SLF_NOT_IN_CONFIG, $guiflags | SGF_NO_NEWGAME, DT_PER_CARGO_DEFAULT, STR_CONFIG_SETTING_DISTRIBUTION_PER_CARGO, STR_CONFIG_SETTING_DISTRIBUTION_PER_CARGO_HELPTEXT, $proc, $from, $to, SlXvFeatureTest(XSLFTO_AND, XSLFI_LINKGRAPH_MODES), SC_EXPERT, LinkGraphDistributionSettingGUI, false, nullptr, _linkgraph_mode_per_cargo), [validation] SDTG_VAR = static_assert($max <= MAX_$type, "Maximum value for $var exceeds storage size"); diff --git a/src/table/win32_settings.ini b/src/table/win32_settings.ini index 6afae025ba..19cbba904b 100644 --- a/src/table/win32_settings.ini +++ b/src/table/win32_settings.ini @@ -14,8 +14,8 @@ static const SettingTable _win32_settings{ }; #endif /* _WIN32 */ [templates] -SDTG_BOOL = SDTG_BOOL($name, $flags, $guiflags, $var, $def, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, nullptr), -SDTG_VAR = SDTG_VAR($name, $type, $flags, $guiflags, $var, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, nullptr), +SDTG_BOOL = SDTG_BOOL($name, $flags, $guiflags, $var, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $guiproc, $startup, $extver, nullptr), +SDTG_VAR = SDTG_VAR($name, $type, $flags, $guiflags, $var, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $from, $to, $cat, $guiproc, $startup, $extver, nullptr), [validation] SDTG_VAR = static_assert($max <= MAX_$type, "Maximum value for $var exceeds storage size"); diff --git a/src/table/window_settings.ini b/src/table/window_settings.ini index 17d5130828..741f0448a7 100644 --- a/src/table/window_settings.ini +++ b/src/table/window_settings.ini @@ -10,8 +10,8 @@ static const SettingTable _window_settings{ [post-amble] }; [templates] -SDT_BOOL = SDT_BOOL($base, $var, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, nullptr), -SDT_VAR = SDT_VAR($base, $var, $type, $flags, $guiflags, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $guiproc, $from, $to, $cat, $startup, $extver, nullptr), +SDT_BOOL = SDT_BOOL($base, $var, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $from, $to, $extver, $cat, $guiproc, $startup, nullptr), +SDT_VAR = SDT_VAR($base, $var, $type, $flags, $guiflags, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $from, $to, $extver, $cat, $guiproc, $startup, nullptr), [validation] SDT_VAR = static_assert($max <= MAX_$type, "Maximum value for $base.$var exceeds storage size");