changeset 15595:2e82550c876b draft

(svn r20255) -Add: allow NewGRFs to specify static info about their user-changeable parameters
author yexo <yexo@openttd.org>
date Sat, 31 Jul 2010 09:51:04 +0000
parents ce0d21487c27
children 87719b9a4a5b
files src/newgrf.cpp src/newgrf_config.cpp src/newgrf_config.h
diffstat 3 files changed, 237 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/src/newgrf.cpp
+++ b/src/newgrf.cpp
@@ -5952,6 +5952,78 @@
 	return true;
 }
 
+
+static GRFParameterInfo *_cur_parameter; ///< The parameter which info is currently changed by the newgrf.
+
+/** Callback function for 'INFO'->'PARAM'->param_num->'NAME' to set the name of a parameter. */
+static bool ChangeGRFParamName(byte langid, const char *str)
+{
+	AddGRFTextToList(&_cur_parameter->name, langid, _cur_grfconfig->ident.grfid, str);
+	return true;
+}
+
+/** Callback function for 'INFO'->'PARAM'->param_num->'DESC' to set the description of a parameter. */
+static bool ChangeGRFParamDescription(byte langid, const char *str)
+{
+	AddGRFTextToList(&_cur_parameter->desc, langid, _cur_grfconfig->ident.grfid, str);
+	return true;
+}
+
+/** Callback function for 'INFO'->'PARAM'->param_num->'TYPE' to set the typeof a parameter. */
+static bool ChangeGRFParamType(size_t len, ByteReader *buf)
+{
+	if (len != 1) {
+		grfmsg(2, "StaticGRFInfo: expected 1 byte for 'INFO'->'PARA'->'TYPE' but got " PRINTF_SIZE ", ignoring this field", len);
+		buf->Skip(len);
+	} else {
+		GRFParameterType type = (GRFParameterType)buf->ReadByte();
+		if (type < PTYPE_END) {
+			_cur_parameter->type = type;
+		} else {
+			grfmsg(3, "StaticGRFInfo: unknown parameter type %d, ignoring this field", type);
+		}
+	}
+	return true;
+}
+
+/** Callback function for 'INFO'->'PARAM'->param_num->'LIMI' to set the min/max value of a parameter. */
+static bool ChangeGRFParamLimits(size_t len, ByteReader *buf)
+{
+	if (_cur_parameter->type != PTYPE_UINT_ENUM) {
+		grfmsg(2, "StaticGRFInfo: 'INFO'->'PARA'->'LIMI' is only valid for parameters with type uint/enum, ignoring this field");
+		buf->Skip(len);
+	} else if (len != 8) {
+		grfmsg(2, "StaticGRFInfo: expected 8 bytes for 'INFO'->'PARA'->'LIMI' but got " PRINTF_SIZE ", ignoring this field", len);
+		buf->Skip(len);
+	} else {
+		_cur_parameter->min_value = buf->ReadDWord();
+		_cur_parameter->max_value = buf->ReadDWord();
+	}
+	return true;
+}
+
+/** Callback function for 'INFO'->'PARAM'->param_num->'MASK' to set the parameter and bits to use. */
+static bool ChangeGRFParamMask(size_t len, ByteReader *buf)
+{
+	if (len < 1 || len > 3) {
+		grfmsg(2, "StaticGRFInfo: expected 1 to 3 bytes for 'INFO'->'PARA'->'MASK' but got " PRINTF_SIZE ", ignoring this field", len);
+		buf->Skip(len);
+	} else {
+		byte param_nr = buf->ReadByte();
+		if (param_nr >= lengthof(_cur_grfconfig->param)) {
+			grfmsg(2, "StaticGRFInfo: invalid parameter number in 'INFO'->'PARA'->'MASK', param %d, ignoring this field", param_nr);
+			buf->Skip(len - 1);
+		} else {
+			_cur_parameter->param_nr = param_nr;
+			if (len >= 2) _cur_parameter->first_bit = min(buf->ReadByte(), 31);
+			if (len >= 3) _cur_parameter->num_bit = min(buf->ReadByte(), 32 - _cur_parameter->first_bit);
+		}
+	}
+
+	return true;
+}
+
+
 typedef bool (*DataHandler)(size_t, ByteReader *);  ///< Type of callback function for binary nodes
 typedef bool (*TextHandler)(byte, const char *str); ///< Type of callback function for text nodes
 typedef bool (*BranchHandler)(ByteReader *);        ///< Type of callback function for branch nodes
@@ -6035,11 +6107,95 @@
 	} handler;
 };
 
+static bool SkipUnknownInfo(ByteReader *buf, byte type);
+static bool HandleNode(byte type, uint32 id, ByteReader *buf, AllowedSubtags *tags);
+
+/**
+ * Callback function for 'INFO'->'PARA'->param_num->'VALU' to set the names
+ * of some parameter values (type uint/enum) or the names of some bits
+ * (type bitmask). In both cases the format is the same:
+ * Each subnode should be a text node with the value/bit number as id.
+ */
+static bool ChangeGRFParamValueNames(ByteReader *buf)
+{
+	byte type = buf->ReadByte();
+	while (type != 0) {
+		uint32 id = buf->ReadDWord();
+		if (type != 'T' || id > _cur_parameter->max_value) {
+			grfmsg(2, "StaticGRFInfo: all child nodes of 'INFO'->'PARA'->param_num->'VALU' should have type 't' and the value/bit number as id");
+			if (!SkipUnknownInfo(buf, type)) return false;
+		}
+
+		byte langid = buf->ReadByte();
+		const char *name_string = buf->ReadString();
+
+		SmallPair<uint32, GRFText *> *val_name = _cur_parameter->value_names.Find(id);
+		if (val_name != _cur_parameter->value_names.End()) {
+			AddGRFTextToList(&val_name->second, langid, _cur_grfconfig->ident.grfid, name_string);
+		} else {
+			GRFText *list = NULL;
+			AddGRFTextToList(&list, langid, _cur_grfconfig->ident.grfid, name_string);
+			_cur_parameter->value_names.Insert(id, list);
+		}
+
+		type = buf->ReadByte();
+	}
+	return true;
+}
+
+AllowedSubtags _tags_parameters[] = {
+	AllowedSubtags('NAME', ChangeGRFParamName),
+	AllowedSubtags('DESC', ChangeGRFParamDescription),
+	AllowedSubtags('TYPE', ChangeGRFParamType),
+	AllowedSubtags('LIMI', ChangeGRFParamLimits),
+	AllowedSubtags('MASK', ChangeGRFParamMask),
+	AllowedSubtags('VALU', ChangeGRFParamValueNames),
+	AllowedSubtags()
+};
+
+/**
+ * Callback function for 'INFO'->'PARA' to set extra information about the
+ * parameters. Each subnode of 'INFO'->'PARA' should be a branch node with
+ * the parameter number as id. The first parameter has id 0. The maximum
+ * parameter that can be changed is set by 'INFO'->'NPAR' which defaults to 80.
+ */
+static bool HandleParameterInfo(ByteReader *buf)
+{
+	byte type = buf->ReadByte();
+	while (type != 0) {
+		uint32 id = buf->ReadDWord();
+		if (type != 'C' || id >= _cur_grfconfig->num_valid_params) {
+			grfmsg(2, "StaticGRFInfo: all child nodes of 'INFO'->'PARA' should have type 'C' and their parameter number as id");
+			if (!SkipUnknownInfo(buf, type)) return false;
+		}
+
+		if (id >= _cur_grfconfig->param_info.Length()) {
+			uint num_to_add = id - _cur_grfconfig->param_info.Length() + 1;
+			GRFParameterInfo **newdata = _cur_grfconfig->param_info.Append(num_to_add);
+			MemSetT<GRFParameterInfo *>(newdata, 0, num_to_add);
+		}
+		if (_cur_grfconfig->param_info[id] == NULL) {
+			_cur_grfconfig->param_info[id] = new GRFParameterInfo(id);
+		}
+		_cur_parameter = _cur_grfconfig->param_info[id];
+		/* Read all parameter-data and process each node. */
+		byte sub_type = buf->ReadByte();
+		while (sub_type != 0) {
+			uint32 sub_id = buf->ReadDWord();
+			if (!HandleNode(sub_type, sub_id, buf, _tags_parameters)) return false;
+			sub_type = buf->ReadByte();
+		}
+		type = buf->ReadByte();
+	}
+	return true;
+}
+
 AllowedSubtags _tags_info[] = {
 	AllowedSubtags('NAME', ChangeGRFName),
 	AllowedSubtags('DESC', ChangeGRFDescription),
 	AllowedSubtags('NPAR', ChangeGRFNumUsedParams),
 	AllowedSubtags('PALS', ChangeGRFPalette),
+	AllowedSubtags('PARA', HandleParameterInfo),
 	AllowedSubtags()
 };
 
--- a/src/newgrf_config.cpp
+++ b/src/newgrf_config.cpp
@@ -51,6 +51,13 @@
 	this->name = DuplicateGRFText(config.name);
 	this->info = DuplicateGRFText(config.info);
 	if (config.error    != NULL) this->error    = new GRFError(*config.error);
+	for (uint i = 0; i < config.param_info.Length(); i++) {
+		if (config.param_info[i] == NULL) {
+			*this->param_info.Append() = NULL;
+		} else {
+			*this->param_info.Append() = new GRFParameterInfo(*config.param_info[i]);
+		}
+	}
 }
 
 /** Cleanup a GRFConfig object. */
@@ -63,6 +70,8 @@
 		delete this->error;
 	}
 	CleanUpGRFText(this->name);
+
+	for (uint i = 0; i < this->param_info.Length(); i++) delete this->param_info[i];
 }
 
 /**
@@ -140,6 +149,53 @@
 }
 
 /**
+ * Create a new empty GRFParameterInfo object.
+ * @param nr The newgrf parameter that is changed.
+ */
+GRFParameterInfo::GRFParameterInfo(uint nr) :
+	name(NULL),
+	desc(NULL),
+	type(PTYPE_UINT_ENUM),
+	min_value(0),
+	max_value(UINT32_MAX),
+	param_nr(nr),
+	first_bit(0),
+	num_bit(32)
+{}
+
+/**
+ * Create a new GRFParameterInfo object that is a deep copy of an existing
+ *   parameter info object.
+ * @param info The GRFParameterInfo object to make a copy of.
+ */
+GRFParameterInfo::GRFParameterInfo(GRFParameterInfo &info) :
+	name(DuplicateGRFText(info.name)),
+	desc(DuplicateGRFText(info.desc)),
+	type(info.type),
+	min_value(info.min_value),
+	max_value(info.max_value),
+	param_nr(info.param_nr),
+	first_bit(info.first_bit),
+	num_bit(info.num_bit)
+{
+	for (uint i = 0; i < info.value_names.Length(); i++) {
+		SmallPair<uint32, GRFText *> *data = info.value_names.Get(i);
+		this->value_names.Insert(data->first, DuplicateGRFText(data->second));
+	}
+}
+
+/** Cleanup all parameter info. */
+GRFParameterInfo::~GRFParameterInfo()
+{
+	CleanUpGRFText(this->name);
+	CleanUpGRFText(this->desc);
+	for (uint i = 0; i < this->value_names.Length(); i++) {
+		SmallPair<uint32, GRFText *> *data = this->value_names.Get(i);
+		CleanUpGRFText(data->second);
+	}
+}
+
+/**
  * Update the palettes of the graphics from the config file.
  * This is needed because the config file gets read and parsed
  * before the palette is chosen (one can configure the base
--- a/src/newgrf_config.h
+++ b/src/newgrf_config.h
@@ -14,6 +14,7 @@
 
 #include "strings_type.h"
 #include "core/alloc_type.hpp"
+#include "core/smallmap_type.hpp"
 
 /** GRF config bit flags */
 enum GCF_Flags {
@@ -99,6 +100,29 @@
 	uint32 param_value[2]; ///< Values of GRF parameters to show for message and custom_message
 };
 
+/** The possible types of a newgrf parameter. */
+enum GRFParameterType {
+	PTYPE_UINT_ENUM, ///< The parameter allows a range of numbers, each of which can have a special name
+	PTYPE_BOOL,      ///< The parameter is either 0 or 1
+	PTYPE_END,       ///< Invalid parameter type
+};
+
+/** Information about one grf parameter. */
+struct GRFParameterInfo {
+	GRFParameterInfo(uint nr);
+	GRFParameterInfo(GRFParameterInfo &info);
+	~GRFParameterInfo();
+	struct GRFText *name;  ///< The name of this parameter
+	struct GRFText *desc;  ///< The description of this parameter
+	GRFParameterType type; ///< The type of this parameter
+	uint32 min_value;      ///< The minimal value this parameter can have
+	uint32 max_value;      ///< The maximal value of this parameter
+	byte param_nr;         ///< GRF parameter to store content in
+	byte first_bit;        ///< First bit to use in the GRF parameter
+	byte num_bit;          ///< Number of bits to use for this parameter
+	SmallMap<uint32, struct GRFText *, 8> value_names; ///< Names for each value.
+};
+
 /** Information about GRF, used in the game and (part of it) in savegames */
 struct GRFConfig : ZeroedMemoryAllocator {
 	GRFConfig(const char *filename = NULL);
@@ -119,6 +143,7 @@
 	uint8 num_params;          ///< Number of used parameters
 	uint8 num_valid_params;    ///< NOSAVE: Number of valid parameters (action 0x14)
 	uint8 palette;             ///< GRFPalette, bitset
+	SmallVector<GRFParameterInfo *, 4> param_info; ///< NOSAVE: extra information about the parameters
 
 	struct GRFConfig *next;    ///< NOSAVE: Next item in the linked list