Changeset - r25536:93e072c5a8dc
[Not reviewed]
master
0 2 0
rubidium42 - 3 years ago 2021-05-22 18:57:41
rubidium@openttd.org
Codechange: make parsing of IniItems overridable functions of SettingDesc
2 files changed with 105 insertions and 101 deletions:
0 comments (0 inline, 0 general)
src/settings.cpp
Show inline comments
 
@@ -371,81 +371,76 @@ static void MakeManyOfMany(char *buf, co
 
	}
 

	
 
	*buf = '\0';
 
}
 

	
 
/**
 
 * 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;
 
}
 

	
 
/**
 
 * 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.
 
@@ -587,42 +582,39 @@ static void IniLoadSettings(IniFile *ini
 
			/* For settings.xx.zz.yy load the settings from [zz] yy = ? in case the previous
 
			 * did not exist (e.g. loading old config files with a [yapf] section */
 
			sc = s.find('.');
 
			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;
 

	
 
			case SDT_STDSTRING:
 
				sd->AsStringSetting()->Write_ValidateSetting(object, (const char *)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_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 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);
 
}
 

	
 
					/* 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();
 
		}
 
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*)this->def, ptr, this->save.length, GetVarMemType(this->save.conv));
 
	}
 
}
 

	
 
/**
 
 * Save the values of settings to the inifile.
 
 * @param ini pointer to IniFile structure
 
@@ -637,13 +629,12 @@ static void IniLoadSettings(IniFile *ini
 
 */
 
static void IniSaveSettings(IniFile *ini, const SettingTable &settings_table, const char *grpname, void *object, bool)
 
{
 
	IniGroup *group_def = nullptr, *group;
 
	IniItem *item;
 
	char buf[512];
 
	void *ptr;
 

	
 
	for (auto &sd : settings_table) {
 
		const SettingDesc *sdb = sd.get();
 
		const SaveLoad *sld = &sd->save;
 

	
 
		/* If the setting is not saved to the configuration
 
@@ -661,57 +652,19 @@ static void IniSaveSettings(IniFile *ini
 
			if (group_def == nullptr) group_def = ini->GetGroup(grpname);
 
			group = group_def;
 
		}
 

	
 
		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;
 
		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);
 

	
 
						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 */
 
		sdb->FormatValue(buf, lastof(buf), object);
 

	
 
		/* 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)ReadValue(GetVariableAddress(object, &this->save), this->save.conv);
 
@@ -721,16 +674,23 @@ void IntSettingDesc::FormatValue(char *b
 
		case SDT_ONEOFMANY:  MakeOneOfMany(buf, last, this->many, i); break;
 
		case SDT_MANYOFMANY: MakeManyOfMany(buf, last, this->many, i); break;
 
		default: NOT_REACHED();
 
	}
 
}
 

	
 
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:
 
			if (str.empty()) {
 
				buf[0] = '\0';
 
			} else {
 
@@ -739,12 +699,28 @@ void StringSettingDesc::FormatValue(char
 
			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
 
 * saved and a callback function should be defined that will take over the
 
 * list-handling and store the data itself somewhere.
 
 * @param ini IniFile handle to the ini file with the source data
src/settings_internal.h
Show inline comments
 
@@ -76,12 +76,13 @@ enum SettingType {
 
	ST_COMPANY,   ///< Company setting.
 
	ST_CLIENT,    ///< Client setting.
 

	
 
	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
 

	
 
/** Properties of config file settings. */
 
struct SettingDesc {
 
	SettingDesc(SaveLoad save, const char *name, const void *def, SettingDescType cmd, SettingGuiFlag flags,
 
@@ -119,12 +120,30 @@ struct SettingDesc {
 
	 * 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;
 
};
 

	
 
/** Integer type, including boolean, settings. Only these are shown in the settings UI. */
 
struct IntSettingDesc : SettingDesc {
 
	IntSettingDesc(SaveLoad save, const char *name, SettingGuiFlag flags, SettingDescType cmd, bool startup, int32 def,
 
			int32 min, uint32 max, int32 interval, StringID str, StringID str_help, StringID str_val,
 
@@ -133,13 +152,16 @@ struct IntSettingDesc : SettingDesc {
 
			proc, many_cnvt, cat, startup) {}
 
	virtual ~IntSettingDesc() {}
 

	
 
	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. */
 
struct StringSettingDesc : SettingDesc {
 
	StringSettingDesc(SaveLoad save, const char *name, SettingGuiFlag flags, SettingDescType cmd, bool startup, const char *def,
 
			uint32 max_length, OnChange proc) :
 
@@ -147,31 +169,37 @@ struct StringSettingDesc : SettingDesc {
 
	virtual ~StringSettingDesc() {}
 

	
 
	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, SettingDescType cmd, bool startup, const char *def) :
 
		SettingDesc(save, name, def, cmd, flags, 0, 0, 0, nullptr, 0, 0, 0, proc, nullptr, SC_NONE, startup) {}
 
	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. */
 
struct NullSettingDesc : SettingDesc {
 
	NullSettingDesc(SaveLoad save) :
 
		SettingDesc(save, "", nullptr, SDT_NULL, SGF_NONE, 0, 0, 0, nullptr, 0, 0, 0, nullptr, nullptr, SC_NONE, false) {}
 
	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;
 

	
 
const SettingDesc *GetSettingFromName(const char *name);
 
bool SetSettingValue(const SettingDesc *sd, int32 value, bool force_newgame = false);
0 comments (0 inline, 0 general)