diff --git a/src/settings.cpp b/src/settings.cpp index e8c3eb78aa..13855d27d9 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -23,6 +23,7 @@ #include "stdafx.h" #include +#include "core/string_consumer.hpp" #include "settings_table.h" #include "debug.h" #include "currency.h" @@ -178,24 +179,21 @@ enum IniFileVersion : uint32_t { const uint16_t INIFILE_VERSION = (IniFileVersion)(IFV_MAX_VERSION - 1); ///< Current ini-file version of OpenTTD. /** - * Find the index value of a ONEofMANY type in a string separated by | + * Find the index value of a ONEofMANY type in a string * @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 - * @return the integer index of the full-list, or SIZE_MAX if not found + * @return the integer index of the full-list, or std::nullopt if not found */ -size_t OneOfManySettingDesc::ParseSingleValue(const char *str, size_t len, const std::vector &many) +std::optional OneOfManySettingDesc::ParseSingleValue(std::string_view str, const std::vector &many) { + StringConsumer consumer{str}; + auto digit = consumer.TryReadIntegerBase(10); /* check if it's an integer */ - if (isdigit(*str)) return std::strtoul(str, nullptr, 0); + if (digit.has_value()) return digit; - size_t idx = 0; - for (const auto &one : many) { - if (one.size() == len && strncmp(one.c_str(), str, len) == 0) return idx; - idx++; - } - - return SIZE_MAX; + auto it = std::ranges::find(many, str); + if (it == many.end()) return std::nullopt; + return static_cast(it - many.begin()); } /** @@ -204,10 +202,10 @@ size_t OneOfManySettingDesc::ParseSingleValue(const char *str, size_t len, const * @param str the current value of the setting for which a value needs found. * @return Either true/false, or nullopt if no boolean value found. */ -std::optional BoolSettingDesc::ParseSingleValue(const char *str) +std::optional BoolSettingDesc::ParseSingleValue(std::string_view str) { - 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; + if (str == "true" || str == "on" || str == "1") return true; + if (str == "false" || str == "off" || str == "0") return false; return std::nullopt; } @@ -216,29 +214,26 @@ std::optional BoolSettingDesc::ParseSingleValue(const char *str) * Find the set-integer value MANYofMANY type in a string * @param many full domain of values the MANYofMANY setting can have * @param str the current string value of the setting, each individual - * of separated by a whitespace,tab or | character - * @return the 'fully' set integer, or SIZE_MAX if a set is not found + * of separated by a whitespace, tab or | character + * @return the 'fully' set integer, or std::nullopt if a set is not found */ -static size_t LookupManyOfMany(const std::vector &many, const char *str) +static std::optional LookupManyOfMany(const std::vector &many, std::string_view str) { - const char *s; - size_t r; - size_t res = 0; + static const std::string_view separators{" \t|"}; - for (;;) { + uint32_t res = 0; + StringConsumer consumer{str}; + while (consumer.AnyBytesLeft()) { /* skip "whitespace" */ - while (*str == ' ' || *str == '\t' || *str == '|') str++; - if (*str == 0) break; + consumer.SkipUntilCharNotIn(separators); - s = str; - while (*s != 0 && *s != ' ' && *s != '\t' && *s != '|') s++; + std::string_view value = consumer.ReadUntilCharIn(separators); + if (value.empty()) break; - r = OneOfManySettingDesc::ParseSingleValue(str, s - str, many); - if (r == SIZE_MAX) return r; + auto r = OneOfManySettingDesc::ParseSingleValue(value, many); + if (!r.has_value()) return r; - SetBit(res, (uint8_t)r); // value found, set it - if (*s == 0) break; - str = s + 1; + SetBit(res, *r); // value found, set it } return res; } @@ -378,31 +373,32 @@ std::string ManyOfManySettingDesc::FormatValue(const void *object) const * @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. */ -size_t IntSettingDesc::ParseValue(const char *str) const +int32_t IntSettingDesc::ParseValue(std::string_view str) const { - char *end; - size_t val = std::strtoul(str, &end, 0); - if (end == str) { + StringConsumer consumer{str}; + /* The actual settings value might be int32 or uint32. Read as int64 and just cast away the high bits. */ + auto value = consumer.TryReadIntegerBase(10); + if (!value.has_value()) { _settings_error_list.emplace_back( GetEncodedString(STR_CONFIG_ERROR), GetEncodedString(STR_CONFIG_ERROR_INVALID_VALUE, str, this->GetName())); return this->GetDefaultValue(); } - if (*end != '\0') { + if (consumer.AnyBytesLeft()) { _settings_error_list.emplace_back( GetEncodedString(STR_CONFIG_ERROR), GetEncodedString(STR_CONFIG_ERROR_TRAILING_CHARACTERS, this->GetName())); } - return val; + return static_cast(*value); } -size_t OneOfManySettingDesc::ParseValue(const char *str) const +int32_t OneOfManySettingDesc::ParseValue(std::string_view str) const { - size_t r = OneOfManySettingDesc::ParseSingleValue(str, strlen(str), this->many); + auto r = OneOfManySettingDesc::ParseSingleValue(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_MAX && this->many_cnvt != nullptr) r = this->many_cnvt(str); - if (r != SIZE_MAX) return r; // and here goes converted value + if (!r.has_value() && this->many_cnvt != nullptr) r = this->many_cnvt(str); + if (r.has_value()) return *r; // and here goes converted value _settings_error_list.emplace_back( GetEncodedString(STR_CONFIG_ERROR), @@ -410,10 +406,10 @@ size_t OneOfManySettingDesc::ParseValue(const char *str) const return this->GetDefaultValue(); } -size_t ManyOfManySettingDesc::ParseValue(const char *str) const +int32_t ManyOfManySettingDesc::ParseValue(std::string_view str) const { - size_t r = LookupManyOfMany(this->many, str); - if (r != SIZE_MAX) return r; + auto r = LookupManyOfMany(this->many, str); + if (r.has_value()) return *r; _settings_error_list.emplace_back( GetEncodedString(STR_CONFIG_ERROR), @@ -421,7 +417,7 @@ size_t ManyOfManySettingDesc::ParseValue(const char *str) const return this->GetDefaultValue(); } -size_t BoolSettingDesc::ParseValue(const char *str) const +int32_t BoolSettingDesc::ParseValue(std::string_view str) const { auto r = BoolSettingDesc::ParseSingleValue(str); if (r.has_value()) return *r; @@ -673,8 +669,8 @@ static void IniLoadSettings(IniFile &ini, const SettingTable &settings_table, co void IntSettingDesc::ParseValue(const IniItem *item, void *object) const { - size_t val = (item == nullptr) ? this->GetDefaultValue() : this->ParseValue(item->value.has_value() ? item->value->c_str() : ""); - this->MakeValueValidAndWrite(object, (int32_t)val); + int32_t val = (item != nullptr && item->value.has_value()) ? this->ParseValue(*item->value) : this->GetDefaultValue(); + this->MakeValueValidAndWrite(object, val); } void StringSettingDesc::ParseValue(const IniItem *item, void *object) const @@ -760,7 +756,7 @@ std::string BoolSettingDesc::FormatValue(const void *object) const bool IntSettingDesc::IsSameValue(const IniItem *item, void *object) const { - int32_t item_value = (int32_t)this->ParseValue(item->value->c_str()); + int32_t item_value = static_cast(this->ParseValue(*item->value)); int32_t object_value = this->Read(object); return item_value == object_value; } @@ -1413,13 +1409,13 @@ void LoadFromConfig(bool startup) const IniItem *old_item; if (generic_version < IFV_GAME_TYPE && IsConversionNeeded(generic_ini, "network", "server_advertise", "server_game_type", &old_item)) { - auto old_value = BoolSettingDesc::ParseSingleValue(old_item->value->c_str()); + auto old_value = BoolSettingDesc::ParseSingleValue(*old_item->value); _settings_client.network.server_game_type = old_value.value_or(false) ? SERVER_GAME_TYPE_PUBLIC : SERVER_GAME_TYPE_LOCAL; } if (generic_version < IFV_AUTOSAVE_RENAME && IsConversionNeeded(generic_ini, "gui", "autosave", "autosave_interval", &old_item)) { static std::vector _old_autosave_interval{"off", "monthly", "quarterly", "half year", "yearly"}; - auto old_value = OneOfManySettingDesc::ParseSingleValue(old_item->value->c_str(), old_item->value->size(), _old_autosave_interval); + auto old_value = OneOfManySettingDesc::ParseSingleValue(*old_item->value, _old_autosave_interval).value_or(-1); switch (old_value) { case 0: _settings_client.gui.autosave_interval = 0; break; @@ -1433,7 +1429,7 @@ void LoadFromConfig(bool startup) /* Persist the right click close option from older versions. */ if (generic_version < IFV_RIGHT_CLICK_CLOSE && IsConversionNeeded(generic_ini, "gui", "right_mouse_wnd_close", "right_click_wnd_close", &old_item)) { - auto old_value = BoolSettingDesc::ParseSingleValue(old_item->value->c_str()); + auto old_value = BoolSettingDesc::ParseSingleValue(*old_item->value); _settings_client.gui.right_click_wnd_close = old_value.value_or(false) ? RCC_YES : RCC_NO; } diff --git a/src/settings_internal.h b/src/settings_internal.h index 8fcf2cd71b..4c91f95b97 100644 --- a/src/settings_internal.h +++ b/src/settings_internal.h @@ -235,7 +235,7 @@ struct IntSettingDesc : SettingDesc { void ChangeValue(const void *object, int32_t newvalue) const; void MakeValueValidAndWrite(const void *object, int32_t value) const; - virtual size_t ParseValue(const char *str) const; + virtual int32_t ParseValue(std::string_view str) const; std::string FormatValue(const void *object) const override; void ParseValue(const IniItem *item, void *object) const override; bool IsSameValue(const IniItem *item, void *object) const override; @@ -258,16 +258,16 @@ struct BoolSettingDesc : IntSettingDesc { IntSettingDesc(save, flags, startup, def ? 1 : 0, 0, 1, 0, str, str_help, str_val, cat, pre_check, post_callback, get_title_cb, get_help_cb, get_value_params_cb, get_def_cb, nullptr) {} - static std::optional ParseSingleValue(const char *str); + static std::optional ParseSingleValue(std::string_view str); bool IsBoolSetting() const override { return true; } - size_t ParseValue(const char *str) const override; + int32_t ParseValue(std::string_view str) const override; std::string FormatValue(const void *object) const override; }; /** One of many setting. */ struct OneOfManySettingDesc : IntSettingDesc { - typedef size_t OnConvert(const char *value); ///< callback prototype for conversion error + typedef std::optional OnConvert(std::string_view value); ///< callback prototype for conversion error template Tdef, ConvertibleThroughBaseOrTo Tmax> OneOfManySettingDesc(const SaveLoad &save, SettingFlags flags, bool startup, Tdef def, @@ -284,10 +284,10 @@ struct OneOfManySettingDesc : IntSettingDesc { 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); + static std::optional ParseSingleValue(std::string_view str, const std::vector &many); std::string FormatSingleValue(uint id) const; - size_t ParseValue(const char *str) const override; + int32_t ParseValue(std::string_view str) const override; std::string FormatValue(const void *object) const override; }; @@ -302,7 +302,7 @@ struct ManyOfManySettingDesc : OneOfManySettingDesc { OneOfManySettingDesc(save, flags, startup, def, (1 << many.size()) - 1, str, str_help, str_val, cat, pre_check, post_callback, get_title_cb, get_help_cb, get_value_params_cb, get_def_cb, many, many_cnvt) {} - size_t ParseValue(const char *str) const override; + int32_t ParseValue(std::string_view str) const override; std::string FormatValue(const void *object) const override; }; diff --git a/src/settings_table.cpp b/src/settings_table.cpp index 6412253fad..72d866861a 100644 --- a/src/settings_table.cpp +++ b/src/settings_table.cpp @@ -445,11 +445,11 @@ static bool CheckRoadSide(int32_t &) * @param value that was read from config file * @return the "hopefully" converted value */ -static size_t ConvertLandscape(const char *value) +static std::optional ConvertLandscape(std::string_view value) { /* try with the old values */ static std::vector _old_landscape_values{"normal", "hilly", "desert", "candy"}; - return OneOfManySettingDesc::ParseSingleValue(value, strlen(value), _old_landscape_values); + return OneOfManySettingDesc::ParseSingleValue(value, _old_landscape_values); } static bool CheckFreeformEdges(int32_t &new_value) diff --git a/src/table/settings.h.preamble b/src/table/settings.h.preamble index f299106867..599760f562 100644 --- a/src/table/settings.h.preamble +++ b/src/table/settings.h.preamble @@ -10,7 +10,7 @@ #include "table/strings.h" /* Callback function used in _settings[] as well as _company_settings[] */ -static size_t ConvertLandscape(const char *value); +static std::optional ConvertLandscape(std::string_view value); static StringID SettingTitleWallclock(const IntSettingDesc &sd); static StringID SettingHelpWallclock(const IntSettingDesc &sd);