From a3c003546bc98701781cb3dbafc742a30a47d3ef Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sun, 13 Feb 2022 22:57:30 +0000 Subject: [PATCH] Implement extended NewGRF variable parameter support --- docs/newgrf-additions.html | 9 ++++++++- src/newgrf.cpp | 17 ++++++++++++++--- src/newgrf.h | 1 + src/newgrf_extension.cpp | 2 +- src/newgrf_spritegroup.cpp | 2 +- 5 files changed, 25 insertions(+), 6 deletions(-) diff --git a/docs/newgrf-additions.html b/docs/newgrf-additions.html index d6d4ae864d..e550e59376 100644 --- a/docs/newgrf-additions.html +++ b/docs/newgrf-additions.html @@ -466,7 +466,7 @@

Action 14 - Variable Mapping for Variational Action 2

See Action 14 Specification and Variational Action 2 Specification for background information.

-

The variable mapping mechanism has the feature name: variable_mapping, this document describes version 1.

+

The variable mapping mechanism has the feature name: variable_mapping, this document describes version 1 (and where indicated, version 2).

Unlike property mappings, it is not necessary to perform a feature test or check a mapping success variable before using a mapped variable.

Remapped variables are accessed by reading from variable 0x11 using a varadjust shift-num and and-mask which exactly matches that specified in the variable mapping.
In the absence of any successful variable mapping, variable 0x11 has all bits set to 0 and attempting to read from it with any shift-num and and-mask value @@ -474,6 +474,9 @@

Reading a mapped variable on a version of OpenTTD which does not support this variable mapping mechanism or which does not support the requested variable, returns a value of 0.

If more than one variable mapping is made for the same combination of feature ID, shift-num and and-mask, it is implementation-defined which mapping is used.
Each variable mapping SHOULD use a unique combination of feature ID, shift-num and and-mask.

+

From version 2 of the variable_mapping feature name, variable remapping can also be used with variable 0x7B.
+ In this case the parameter of variable 0x7B should be set to 0x11, and the shift and mask fields set the same way in the direct 0x11 variable case.
+ The "VPRM" parameter field is ignored, and the parameter used is the accumulator of the previous adjust part in the usual way for variable 0x7B.

Variable Mapping: C "A2VM"

Each A2VM chunk (type C) describes an individual variable mapping.
Sub-chunks within each A2VM chunk may appear in any order, however each sub-chunk SHOULD only appear ONCE within an individual A2VM chunk.

@@ -500,6 +503,10 @@ If this is not specified, a value of 0 is assumed.

Success Indicator Global Variable 0x8D Bit: C "A2VM" -> B "SETT"

This behaves identically to the C "A0PM" -> B "SETT" case, above

+

Replacement parameter: C "A2VM" -> B "VPRM"

+

Within an A2VM chunk, the VPRM binary (type B) field contains the Variational Action 2 parameter value (as in 60+X variables) to use on the mapped variable. This is 4 bytes.
+ If this is not specified, a value of 0 is assumed.
+ Support for this field is indicated by the feature name variable_mapping, version 2.

Example NFO:

 // Map station variable "sample_station_variable" with a shift-num of 4 and an and-mask of 0xFF, to reads of variable 0x11 with a shift-num of 1 and an and-mask of 0x2, and set bit 4 of global variable 0x8D if successful
diff --git a/src/newgrf.cpp b/src/newgrf.cpp
index 1c0cfe1d22..05b22ecd27 100644
--- a/src/newgrf.cpp
+++ b/src/newgrf.cpp
@@ -5414,6 +5414,16 @@ static void NewSpriteGroup(ByteReader *buf)
 							adjust.variable = remap.id;
 							adjust.shift_num = remap.output_shift;
 							adjust.and_mask = remap.output_mask;
+							adjust.parameter = remap.output_param;
+							break;
+						}
+					}
+				} else if (adjust.variable == 0x7B && adjust.parameter == 0x11) {
+					for (const GRFVariableMapEntry &remap : _cur.grffile->grf_variable_remaps) {
+						if (remap.feature == feature && remap.input_shift == adjust.shift_num && remap.input_mask == adjust.and_mask) {
+							adjust.parameter = remap.id;
+							adjust.shift_num = remap.output_shift;
+							adjust.and_mask = remap.output_mask;
 							break;
 						}
 					}
@@ -8799,6 +8809,7 @@ struct GRFPropertyMapAction {
 	uint8 output_shift;
 	uint input_mask;
 	uint output_mask;
+	uint output_param;
 
 	void Reset(const char *tag, const char *desc)
 	{
@@ -8814,6 +8825,7 @@ struct GRFPropertyMapAction {
 		this->output_shift = 0;
 		this->input_mask = 0;
 		this->output_mask = 0;
+		this->output_param = 0;
 	}
 
 	void ExecuteFeatureIDRemapping()
@@ -8930,7 +8942,7 @@ struct GRFPropertyMapAction {
 		extern const GRFVariableMapDefinition _grf_action2_remappable_variables[];
 		for (const GRFVariableMapDefinition *info = _grf_action2_remappable_variables; info->name != nullptr; info++) {
 			if (info->feature == this->feature && strcmp(info->name, str) == 0) {
-				_cur.grffile->grf_variable_remaps.push_back({ (uint16)info->id, (uint8)this->feature, this->input_shift, this->output_shift, this->input_mask, this->output_mask });
+				_cur.grffile->grf_variable_remaps.push_back({ (uint16)info->id, (uint8)this->feature, this->input_shift, this->output_shift, this->input_mask, this->output_mask, this->output_param });
 				success = true;
 				break;
 			}
@@ -9162,8 +9174,7 @@ static bool ChangePropertyRemapSetOutputParam(size_t len, ByteReader *buf)
 		grfmsg(2, "Action 14 %s mapping: expected 4 bytes for '%s'->'VPRM' but got " PRINTF_SIZE ", ignoring this field", action.descriptor, action.tag_name, len);
 		buf->Skip(len);
 	} else {
-		buf->ReadDWord();
-		/* This is not implemented yet, so just do nothing, but still validate that the format is correct */
+		action.output_param = buf->ReadDWord();
 	}
 	return true;
 }
diff --git a/src/newgrf.h b/src/newgrf.h
index 7c77d80f48..fb1a70b8f5 100644
--- a/src/newgrf.h
+++ b/src/newgrf.h
@@ -211,6 +211,7 @@ struct GRFVariableMapEntry {
 	uint8 output_shift = 0;
 	uint32 input_mask = 0;
 	uint32 output_mask = 0;
+	uint32 output_param = 0;
 };
 
 /** The type of action 5 type. */
diff --git a/src/newgrf_extension.cpp b/src/newgrf_extension.cpp
index 79116ff74c..9112ad4078 100644
--- a/src/newgrf_extension.cpp
+++ b/src/newgrf_extension.cpp
@@ -18,7 +18,7 @@
 extern const GRFFeatureInfo _grf_feature_list[] = {
 	GRFFeatureInfo("feature_test", 1),
 	GRFFeatureInfo("property_mapping", 1),
-	GRFFeatureInfo("variable_mapping", 1),
+	GRFFeatureInfo("variable_mapping", 2),
 	GRFFeatureInfo("feature_id_mapping", 1),
 	GRFFeatureInfo("action5_type_id_mapping", 1),
 	GRFFeatureInfo("action0_station_prop1B", 1),
diff --git a/src/newgrf_spritegroup.cpp b/src/newgrf_spritegroup.cpp
index e40f093577..84f6f3b556 100644
--- a/src/newgrf_spritegroup.cpp
+++ b/src/newgrf_spritegroup.cpp
@@ -595,7 +595,7 @@ void SpriteGroupDumper::DumpSpriteGroup(const SpriteGroup *sg, int padding, uint
 						}
 					}
 				}
-				if (adjust.variable >= 0x60 && adjust.variable <= 0x7F) p += seprintf(p, lastof(this->buffer), " (parameter: %X)", adjust.parameter);
+				if ((adjust.variable >= 0x60 && adjust.variable <= 0x7F) || adjust.parameter != 0) p += seprintf(p, lastof(this->buffer), " (parameter: %X)", adjust.parameter);
 				p += seprintf(p, lastof(this->buffer), ", shift: %X, and: %X", adjust.shift_num, adjust.and_mask);
 				switch (adjust.type) {
 					case DSGA_TYPE_DIV: p += seprintf(p, lastof(this->buffer), ", add: %X, div: %X", adjust.add_val, adjust.divmod_val); break;