diff options
-rw-r--r-- | src/settings.cpp | 176 | ||||
-rw-r--r-- | src/settings_internal.h | 28 |
2 files changed, 104 insertions, 100 deletions
diff --git a/src/settings.cpp b/src/settings.cpp index cedfdf5af..a99b196b4 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -374,75 +374,70 @@ static void MakeManyOfMany(char *buf, const char *last, const char *many, uint32 } /** - * 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 + * 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 const void *StringToVal(const SettingDesc *desc, const char *orig_str) +size_t IntSettingDesc::ParseValue(const char *str) const { - const char *str = orig_str == nullptr ? "" : orig_str; - - switch (desc->cmd) { + switch (this->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); + msg.SetDParamStr(1, this->name); _settings_error_list.push_back(msg); - return desc->def; + break; } if (*end != '\0') { ErrorMessageData msg(STR_CONFIG_ERROR, STR_CONFIG_ERROR_TRAILING_CHARACTERS); - msg.SetDParamStr(0, desc->name); + msg.SetDParamStr(0, this->name); _settings_error_list.push_back(msg); } - return (void*)val; + return val; } case SDT_ONEOFMANY: { - size_t r = LookupOneOfMany(desc->many, str); + size_t r = LookupOneOfMany(this->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 + if (r == (size_t)-1 && this->proc_cnvt != nullptr) r = this->proc_cnvt(str); + if (r != (size_t)-1) return 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); + msg.SetDParamStr(1, this->name); _settings_error_list.push_back(msg); - return desc->def; + break; } case SDT_MANYOFMANY: { - size_t r = LookupManyOfMany(desc->many, str); - if (r != (size_t)-1) return (void*)r; + 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, desc->name); + msg.SetDParamStr(1, this->name); _settings_error_list.push_back(msg); - return desc->def; + break; } 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; + 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, desc->name); + msg.SetDParamStr(1, this->name); _settings_error_list.push_back(msg); - return desc->def; + break; } - case SDT_STDSTRING: return orig_str; - case SDT_INTLIST: return str; - default: break; + default: NOT_REACHED(); } - return nullptr; + return (size_t)this->def; } /** @@ -590,36 +585,33 @@ static void IniLoadSettings(IniFile *ini, const SettingTable &settings_table, co if (sc != std::string::npos) item = ini->GetGroup(s.substr(0, sc))->GetItem(s.substr(sc + 1), false); } - const void *p = (item == nullptr) ? sdb->def : StringToVal(sdb, item->value.has_value() ? item->value->c_str() : nullptr); - void *ptr = GetVariableAddress(object, sld); + sdb->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: - sd->AsIntSetting()->Write_ValidateSetting(object, (int32)(size_t)p); - break; +void IntSettingDesc::ParseValue(const IniItem *item, void *object) const +{ + size_t val = (item == nullptr) ? (size_t)this->def : this->ParseValue(item->value.has_value() ? item->value->c_str() : ""); + this->Write_ValidateSetting(object, (int32)val); +} - case SDT_STDSTRING: - sd->AsStringSetting()->Write_ValidateSetting(object, (const char *)p); - break; +void StringSettingDesc::ParseValue(const IniItem *item, void *object) const +{ + const char *str = (item == nullptr) ? (const char *)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) ? (const char *)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((const char*)this->def, ptr, this->save.length, GetVarMemType(this->save.conv)); } } @@ -640,7 +632,6 @@ 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.get(); @@ -664,51 +655,13 @@ static void IniSaveSettings(IniFile *ini, const SettingTable &settings_table, co item = group->GetItem(s, true); - 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()); - ptr = GetVariableAddress(object, sld); - - /* 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; + if (!item->value.has_value() || !sdb->IsSameValue(item, object)) { + /* Value has changed, get the new value and put it into a buffer */ + sdb->FormatValue(buf, lastof(buf), object); - 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 */ - sdb->FormatValue(buf, lastof(buf), object); - - /* The value is different, that means we have to write it to the ini */ - item->value.emplace(buf); } } @@ -724,10 +677,17 @@ void IntSettingDesc::FormatValue(char *buf, const char *last, const void *object } } +bool IntSettingDesc::IsSameValue(const IniItem *item, void *object) const +{ + int64 item_value = this->ParseValue(item->value->c_str()); + int64 object_value = ReadValue(GetVariableAddress(object, &this->save), this->save.conv); + 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)) { + switch (GetVarMemType(this->save.conv)) { case SLE_VAR_STR: strecpy(buf, str.c_str(), last); break; case SLE_VAR_STRQ: @@ -742,6 +702,22 @@ void StringSettingDesc::FormatValue(char *buf, const char *last, const void *obj } } +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 diff --git a/src/settings_internal.h b/src/settings_internal.h index 33910e066..0914f2803 100644 --- a/src/settings_internal.h +++ b/src/settings_internal.h @@ -79,6 +79,7 @@ enum SettingType { ST_ALL, ///< Used in setting filter to match all types. }; +struct IniItem; typedef bool OnChange(int32 var); ///< callback prototype on data modification typedef size_t OnConvert(const char *value); ///< callback prototype for conversion error @@ -122,6 +123,24 @@ struct SettingDesc { * @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; }; /** Integer type, including boolean, settings. Only these are shown in the settings UI. */ @@ -136,7 +155,10 @@ struct IntSettingDesc : SettingDesc { void ChangeValue(const void *object, int32 newvalue) const; void Write_ValidateSetting(const void *object, int32 value) const; + 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; }; /** String settings. */ @@ -150,6 +172,8 @@ struct StringSettingDesc : SettingDesc { 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; }; @@ -160,6 +184,8 @@ struct ListSettingDesc : SettingDesc { virtual ~ListSettingDesc() {} 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. */ @@ -169,6 +195,8 @@ struct NullSettingDesc : SettingDesc { 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(); } }; typedef std::initializer_list<std::unique_ptr<const SettingDesc>> SettingTable; |