From 4ba73dac9599a92f5c228a156e0f2d1e22f275ea Mon Sep 17 00:00:00 2001 From: Greg-21 <34160838+Greg-21@users.noreply.github.com> Date: Tue, 6 Nov 2018 00:38:42 +0100 Subject: [PATCH 1/5] Doc: Changed several files to unificate them... (#6964) ...with those from release 1.8 branch. --- os/debian/changelog | 13 ++++++++++++- src/script/api/ai_changelog.hpp | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/os/debian/changelog b/os/debian/changelog index ce2582128c..bb57ca39e8 100644 --- a/os/debian/changelog +++ b/os/debian/changelog @@ -1,3 +1,15 @@ +openttd (1.8.0-0) unstable; urgency=low + + * New upstream release 1.8.0 + + -- OpenTTD Sun, 01 Apr 2018 14:00:00 +0200 + +openttd (1.8.0~RC1-0) unstable; urgency=low + + * New upstream release 1.8.0-RC1 + + -- OpenTTD Wed, 21 Mar 2018 21:00:00 +0100 + openttd (1.7.2-0) unstable; urgency=low * New upstream release 1.7.2 @@ -1002,4 +1014,3 @@ openttd (0.3.5-1) unstable; urgency=low * Initial Release. -- Matthijs Kooijman Fri, 24 Dec 2004 02:58:47 +0100 - diff --git a/src/script/api/ai_changelog.hpp b/src/script/api/ai_changelog.hpp index ecb015ed4d..03e9831a11 100644 --- a/src/script/api/ai_changelog.hpp +++ b/src/script/api/ai_changelog.hpp @@ -23,7 +23,7 @@ * * \b 1.8.0 * - * 1.8.0 is not yet released. The following changes are not set in stone yet. + * No changes * * \b 1.7.0 - 1.7.2 * From 16cc482d57932228036248d4c6eef530ac54571d Mon Sep 17 00:00:00 2001 From: translators Date: Thu, 8 Nov 2018 19:45:43 +0100 Subject: [PATCH 2/5] Update: Translations from eints croatian: 2 changes by VoyagerOne --- src/lang/croatian.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lang/croatian.txt b/src/lang/croatian.txt index 3bb0edf419..a1c9dd86d0 100644 --- a/src/lang/croatian.txt +++ b/src/lang/croatian.txt @@ -3066,6 +3066,8 @@ STR_NEWGRF_ERROR_READ_BOUNDS :Pročitaj nakon STR_NEWGRF_ERROR_GRM_FAILED :Zatraženi GRF resursi nisu dostupni (sprite {3:NUM}) STR_NEWGRF_ERROR_FORCEFULLY_DISABLED :{1:STRING} je isključen od strane {STRING} STR_NEWGRF_ERROR_INVALID_SPRITE_LAYOUT :Pogrešan/nepoznat format raspored sprite-a (sprite {3:NUM}) +STR_NEWGRF_ERROR_LIST_PROPERTY_TOO_LONG :Previše elemenata na listi postavki varijabli (sprite {3:NUM}, postavka {4:HEX}) +STR_NEWGRF_ERROR_INDPROD_CALLBACK :Pogrešna callback funkcija za industrijsku proizvodnju (sprite {3:NUM}, "{1:STRING}") # NewGRF related 'general' warnings STR_NEWGRF_POPUP_CAUTION_CAPTION :{WHITE}Oprez! From c10f6f9abbcdc0dca9d4346888582e0dfde8ef14 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sat, 10 Nov 2018 10:10:44 +0000 Subject: [PATCH 3/5] Fix incorrect method documentation of ChangeGRFFeatureSetPlatformVarBit --- src/newgrf.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/newgrf.cpp b/src/newgrf.cpp index 29877b8dbe..b24ed48940 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -7797,7 +7797,7 @@ static bool ChangeGRFFeatureMaxVersion(size_t len, ByteReader *buf) return true; } -/** Callback function for 'FTST'->'SETP' to set the maximum version of the feature being tested. */ +/** Callback function for 'FTST'->'SETP' to set the bit number of global variable 9D (platform version) to set/unset the result of the feature test. */ static bool ChangeGRFFeatureSetPlatformVarBit(size_t len, ByteReader *buf) { if (len != 1) { From 2dda1c43512e6d71096041fdd8f892d56ca20883 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sat, 10 Nov 2018 12:49:44 +0000 Subject: [PATCH 4/5] Documentation: Add docs on NewGRF spec additions --- docs/newgrf-additions.html | 73 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 docs/newgrf-additions.html diff --git a/docs/newgrf-additions.html b/docs/newgrf-additions.html new file mode 100644 index 0000000000..648f9bfcac --- /dev/null +++ b/docs/newgrf-additions.html @@ -0,0 +1,73 @@ + + + + + Additions to NewGRF Specifications + + + +

Additions to NewGRF Specifications in thi branch

+

This document describes non-standard additions to the Official OpenTTD NewGRF Specifications which are present in this branch. +

This additions MAY also be present in other branches/repositories/etc. They MAY be removed or moved in future, if necessary.

+

NewGRFs which use any of these features SHOULD use the feature testing mechanism described below to check whether individual added features are supported.

+ +

Action 14 - Feature Tests

+

See Action 14 Specification for background information.

+

Feature Test: C "FTST"

+

Each FTST chunk (type C) describes an individual feature test.
+ Sub-chunks within each FTST chunk may appear in any order, however each sub-chunk SHOULD only appear ONCE within an individual FTST chunk.

+

Feature tests can be safely used on implementations which do not implement the described feature test mechanism because unknown Action 14 blocks are ignored, + and the observable result (in global variable 0x9D) is equivalent to the case where all feature tests have failed, indicating that the feature is not present.

+

Feature Name: C "FTST" -> T "NAME"

+

Within an FTST chunk, the NAME text (type T) field contains the name of the feature to test for. The value of the language ID byte is ignored.
+ If the named feature is not present, or if this field is omitted, the version is 0.
+ If the named feature is present, the version number will be at least 1.

+

The feature testing mechanism itself has the feature name: feature_test, this document describes version 1.

+

Feature Minimum Version: C "FTST" -> B "MINV"

+

Within an FTST chunk, the MINV binary (type B) field contains the minimum (inclusive) (≥) version to accept. This is a Word (2 bytes).
+ The default value is 1.

+

Feature Maximum Version: C "FTST" -> B "MAXV"

+

Within an FTST chunk, the MINV binary (type B) field contains the maximum (inclusive) (≤) version to accept. This is a Word (2 bytes).
+ The default value is 0xFFFF (no maximum).

+

Feature Set Global Variable 0x9D Bit: C "FTST" -> B "SETP"

+

Within an FTST chunk, the SETP binary (type B) field contains the bit number to set/clear in + global variable 0x9D (TTD Platform) to store the result of the test. This is 1 byte.
+ If the test is successful, the bit is set (to 1), otherwise the bit is cleared (to 0).
+ The bit number MUST be in the range: 4 ≤ bit number ≤ 31. These bits can be assumed to be 0 on implementations which do not support this feature test mechanism.
+ Global variable 0x9D can then be tested by using a standard Action 7 or 9, or a standard Variational Action 2.
+ If this field is omitted, no bit is set or cleared, and the test is not observable. +

+
+

Example:

+
+// Set bit 4 of global variable 0x9D if sample_feature_1 is present with a version of at least 4
+// Set bit 5 of global variable 0x9D if sample_feature_2 is present with a version of at least 5 and at most 6
+-1 * -1 14
+	"C" "FTST"
+		"T" "NAME" 00 "sample_feature_1" 00
+		"B" "MINV" \w2 \w4
+		"B" "SETP" \w1 04
+		00
+	"C" "FTST"
+		"T" "NAME" 00 "sample_feature_2" 00
+		"B" "MINV" \w2 \w5
+		"B" "MAXV" \w2 \w6
+		"B" "SETP" \w1 05
+		00
+	00
+....
+// Skip 1 sprite if bit 4 of global variable 0x9D is not set (indicating that sample_feature_1 with a version of at least 4 is NOT present)
+-1 * -1  07 9D 01 \70 04 01
+	
+ + From 9a6a66a054b98e1d3024921c169187cdc18cdb8f Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sat, 10 Nov 2018 12:44:19 +0000 Subject: [PATCH 5/5] Add general NewGRF Action 0 property mapping scheme to Action 14 --- docs/newgrf-additions.html | 72 +++++++++- src/lang/english.txt | 3 +- src/newgrf.cpp | 278 +++++++++++++++++++++++++++++++++---- src/newgrf.h | 65 +++++++++ src/newgrf_config.h | 2 +- 5 files changed, 390 insertions(+), 30 deletions(-) diff --git a/docs/newgrf-additions.html b/docs/newgrf-additions.html index 648f9bfcac..549586626c 100644 --- a/docs/newgrf-additions.html +++ b/docs/newgrf-additions.html @@ -13,6 +13,8 @@ td.caption { white-space: nowrap; text-align: left; } td li { white-space: nowrap; text-align: left; } th { white-space: nowrap; text-align: center; } + td, th { border: 1px solid #CCCCCC; padding: 0px 5px; } + table { border-collapse: collapse; empty-cells: show; } @@ -48,7 +50,7 @@ If this field is omitted, no bit is set or cleared, and the test is not observable.


-

Example:

+

Example NFO:

 // Set bit 4 of global variable 0x9D if sample_feature_1 is present with a version of at least 4
 // Set bit 5 of global variable 0x9D if sample_feature_2 is present with a version of at least 5 and at most 6
@@ -69,5 +71,73 @@
 // Skip 1 sprite if bit 4 of global variable 0x9D is not set (indicating that sample_feature_1 with a version of at least 4 is NOT present)
 -1 * -1  07 9D 01 \70 04 01
 	
+
+

Action 14 - Property Mapping for Action 0

+

See Action 14 Specification and Action 0 Specification for background information.

+

The property mapping mechanism has the feature name: property_mapping, this document describes version 1.

+

Users of this mechanism SHOULD at minimum use test for the presence of the feature above or test variable 8D, below.

+

Property Mapping: C "A0PM"

+

Each A0PM chunk (type C) describes an individual property mapping.
+ Sub-chunks within each A0PM chunk may appear in any order, however except where otherwise noted each sub-chunk SHOULD only appear ONCE within an individual A0PM chunk.

+

Property mapping can be safely used on implementations which do not implement the property mapping mechanism if Action 0 sprites which use mapped property IDs are skipped if one or more of: +

    +
  • The global variable 0x8D is checked to determine whether the property mapping operation was successful.
  • +
  • The feature name property_mapping is checked for.
  • +
+ Unknown Action 14 blocks are ignored, and do not need to be skipped.

+

Property Name: C "A0PM" -> T "NAME"

+

Within an A0PM chunk, the NAME text (type T) field contains the name of the property to map. The value of the language ID byte is ignored.

+

Action 0 Feature ID: C "A0PM" -> B "FEAT"

+

Within an A0PM chunk, the FEAT binary (type B) field contains the Action 0 feature ID. This is 1 byte.

+

Property ID: C "A0PM" -> B "PROP"

+

Within an A0PM chunk, the PROP binary (type B) field contains the property ID to allocate to the named property, this value can used in Action 0 sprites. This is 1 byte.
+ It is possible to override existing properties, however this use is not recommended.

+

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

+

Within an A0PM chunk, the SETT binary (type B) field contains the bit number to set/clear in + global variable 0x8D (TTD version) to store whether the mapping operation was successful. This is 1 byte.
+ If the operation is successful, the bit is set (to 1), otherwise the bit is cleared (to 0).
+ The bit number MUST be in the range: 4 ≤ bit number ≤ 31. These bits can be assumed to be 0 on implementations which do not support this property mapping mechanism.
+ Global variable 0x8D can then be tested by using a standard Action 7 or 9, or a standard Variational Action 2.
+ If this field is omitted, no bit is set or cleared. +

+

Fallback Mode: C "A0PM" -> B "FLBK"

+

Within an A0PM chunk, the FLBK binary (type B) field contains the fallback mode. This is 1 byte.
+ The fallback mode may take the following values: + + + + + +
ValueBehaviour
0Attempts to map an unknown property name are ignored. Use of the mapped property in an Action 0 is ignored. This is the default.
1Attempts to map an unknown property name are ignored. Use of the mapped property in an Action 0 is an error.
2Attempting to map an unknown property name is an error.
+ Attempts to set a fallback mode other than those listed above are silently ignored. More fallback modes MAY be added in future versions of this mechanism.
+ This chunk MAY be specified more than once, in which case the last specified valid value is used.
+ Note that even when using fallback mode 0, above, if the property mapping feature is not present, then use of the mapped property ID in an Action 0 is an error. +

+

Format of remapped properties

+ All properties which are mapped by the mechanism have the format: + + + + +
SizeNameMeaning
B*numSize of the data in bytes
VdataProperty data
+ Note that num is an extended byte, see GRFActionsDetailed.
+ If the size of the data does provided is not valid for the given property, the attempt to set the property MAY be ignored or partially applied. +

Example NFO:

+
+// Map station property "sample_station_property" to property id 0xF8, and set bit 4 of global variable 0x8D if successful
+-1 * -1 14
+	"C" "A0PM"
+		"T" "NAME" 00 "sample_station_property" 00
+		"B" "FEAT" \w1 04
+		"B" "PROP" \w1 F8
+		"B" "SETT" \w1 4
+		00
+	00
+....
+// Skip 1 sprite if bit 4 of global variable 0x8D is not set (indicating that station property sample_station_property is NOT present)
+-1 * -1  07 8D 01 \70 04 01
+// Set sample_station_property for station ID 10 to 2 byte value: AB CD
+-1 * -1  00 04 01 01 10 F8 02 AB CD
+	
diff --git a/src/lang/english.txt b/src/lang/english.txt index cb04a5cbd9..71b5abba68 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2950,7 +2950,7 @@ STR_NEWGRF_ERROR_MSG_INFO :{SILVER}{RAW_ST STR_NEWGRF_ERROR_MSG_WARNING :{RED}Warning: {SILVER}{RAW_STRING} STR_NEWGRF_ERROR_MSG_ERROR :{RED}Error: {SILVER}{RAW_STRING} STR_NEWGRF_ERROR_MSG_FATAL :{RED}Fatal: {SILVER}{RAW_STRING} -STR_NEWGRF_ERROR_FATAL_POPUP :{WHITE}A fatal NewGRF error has occurred: {}{STRING5} +STR_NEWGRF_ERROR_FATAL_POPUP :{WHITE}A fatal NewGRF error has occurred: {}{STRING7} STR_NEWGRF_ERROR_VERSION_NUMBER :{1:RAW_STRING} will not work with the TTDPatch version reported by OpenTTD STR_NEWGRF_ERROR_DOS_OR_WINDOWS :{1:RAW_STRING} is for the {RAW_STRING} version of TTD STR_NEWGRF_ERROR_UNSET_SWITCH :{1:RAW_STRING} is designed to be used with {RAW_STRING} @@ -2963,6 +2963,7 @@ STR_NEWGRF_ERROR_TOO_MANY_NEWGRFS_LOADED :Too many NewGRF STR_NEWGRF_ERROR_STATIC_GRF_CAUSES_DESYNC :Loading {1:RAW_STRING} as static NewGRF with {RAW_STRING} could cause desyncs STR_NEWGRF_ERROR_UNEXPECTED_SPRITE :Unexpected sprite (sprite {3:NUM}) STR_NEWGRF_ERROR_UNKNOWN_PROPERTY :Unknown Action 0 property {4:HEX} (sprite {3:NUM}) +STR_NEWGRF_ERROR_UNIMPLEMETED_MAPPED_PROPERTY :Unimplemented remapped Action 0 property feature: {4:HEX}, name: {2:RAW_STRING}, mapped to: {5:HEX} (sprite {3:NUM}) STR_NEWGRF_ERROR_INVALID_ID :Attempt to use invalid ID (sprite {3:NUM}) STR_NEWGRF_ERROR_CORRUPT_SPRITE :{YELLOW}{RAW_STRING} contains a corrupt sprite. All corrupt sprites will be shown as a red question mark (?) STR_NEWGRF_ERROR_MULTIPLE_ACTION_8 :Contains multiple Action 8 entries (sprite {3:NUM}) diff --git a/src/newgrf.cpp b/src/newgrf.cpp index 6558858ecb..ed91b2e464 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -993,6 +993,24 @@ enum ChangeInfoResult { typedef ChangeInfoResult (*VCI_Handler)(uint engine, int numinfo, int prop, ByteReader *buf); +static ChangeInfoResult HandleAction0PropertyDefault(ByteReader *buf, int prop) +{ + switch (prop) { + case A0RPI_UNKNOWN_IGNORE: + buf->Skip(buf->ReadExtendedByte()); + return CIR_SUCCESS; + + case A0RPI_UNKNOWN_ERROR: + return CIR_DISABLED; + + case A0RPI_SKIPPED_IGNORE: + return CIR_SUCCESS; + + default: + return CIR_UNKNOWN; + } +} + /** * Define properties common to all vehicles * @param ei Engine info. @@ -1029,7 +1047,7 @@ static ChangeInfoResult CommonVehicleChangeInfo(EngineInfo *ei, int prop, ByteRe break; default: - return CIR_UNKNOWN; + return HandleAction0PropertyDefault(buf, prop); } return CIR_SUCCESS; @@ -2082,7 +2100,7 @@ static ChangeInfoResult StationChangeInfo(uint stid, int numinfo, int prop, Byte break; default: - ret = CIR_UNKNOWN; + ret = HandleAction0PropertyDefault(buf, prop); break; } } @@ -2120,7 +2138,7 @@ static ChangeInfoResult CanalChangeInfo(uint id, int numinfo, int prop, ByteRead break; default: - ret = CIR_UNKNOWN; + ret = HandleAction0PropertyDefault(buf, prop); break; } } @@ -2232,7 +2250,7 @@ static ChangeInfoResult BridgeChangeInfo(uint brid, int numinfo, int prop, ByteR break; default: - ret = CIR_UNKNOWN; + ret = HandleAction0PropertyDefault(buf, prop); break; } } @@ -2295,7 +2313,7 @@ static ChangeInfoResult IgnoreTownHouseProperty(int prop, ByteReader *buf) } default: - ret = CIR_UNKNOWN; + ret = HandleAction0PropertyDefault(buf, prop); break; } return ret; @@ -2527,7 +2545,7 @@ static ChangeInfoResult TownHouseChangeInfo(uint hid, int numinfo, int prop, Byt break; default: - ret = CIR_UNKNOWN; + ret = HandleAction0PropertyDefault(buf, prop); break; } } @@ -2789,7 +2807,7 @@ static ChangeInfoResult GlobalVarChangeInfo(uint gvid, int numinfo, int prop, By } default: - ret = CIR_UNKNOWN; + ret = HandleAction0PropertyDefault(buf, prop); break; } } @@ -2851,7 +2869,7 @@ static ChangeInfoResult GlobalVarReserveInfo(uint gvid, int numinfo, int prop, B break; default: - ret = CIR_UNKNOWN; + ret = HandleAction0PropertyDefault(buf, prop); break; } } @@ -2990,7 +3008,7 @@ static ChangeInfoResult CargoChangeInfo(uint cid, int numinfo, int prop, ByteRea break; default: - ret = CIR_UNKNOWN; + ret = HandleAction0PropertyDefault(buf, prop); break; } } @@ -3048,7 +3066,7 @@ static ChangeInfoResult SoundEffectChangeInfo(uint sid, int numinfo, int prop, B } default: - ret = CIR_UNKNOWN; + ret = HandleAction0PropertyDefault(buf, prop); break; } } @@ -3088,7 +3106,7 @@ static ChangeInfoResult IgnoreIndustryTileProperty(int prop, ByteReader *buf) break; default: - ret = CIR_UNKNOWN; + ret = HandleAction0PropertyDefault(buf, prop); break; } return ret; @@ -3226,7 +3244,7 @@ static ChangeInfoResult IndustrytilesChangeInfo(uint indtid, int numinfo, int pr } default: - ret = CIR_UNKNOWN; + ret = HandleAction0PropertyDefault(buf, prop); break; } } @@ -3319,7 +3337,7 @@ static ChangeInfoResult IgnoreIndustryProperty(int prop, ByteReader *buf) } default: - ret = CIR_UNKNOWN; + ret = HandleAction0PropertyDefault(buf, prop); break; } return ret; @@ -3744,7 +3762,7 @@ static ChangeInfoResult IndustriesChangeInfo(uint indid, int numinfo, int prop, } default: - ret = CIR_UNKNOWN; + ret = HandleAction0PropertyDefault(buf, prop); break; } } @@ -3944,7 +3962,7 @@ static ChangeInfoResult AirportChangeInfo(uint airport, int numinfo, int prop, B break; default: - ret = CIR_UNKNOWN; + ret = HandleAction0PropertyDefault(buf, prop); break; } } @@ -3989,7 +4007,7 @@ static ChangeInfoResult IgnoreObjectProperty(uint prop, ByteReader *buf) break; default: - ret = CIR_UNKNOWN; + ret = HandleAction0PropertyDefault(buf, prop); break; } @@ -4119,7 +4137,7 @@ static ChangeInfoResult ObjectChangeInfo(uint id, int numinfo, int prop, ByteRea break; default: - ret = CIR_UNKNOWN; + ret = HandleAction0PropertyDefault(buf, prop); break; } } @@ -4257,7 +4275,7 @@ static ChangeInfoResult RailTypeChangeInfo(uint id, int numinfo, int prop, ByteR break; default: - ret = CIR_UNKNOWN; + ret = HandleAction0PropertyDefault(buf, prop); break; } } @@ -4337,7 +4355,7 @@ static ChangeInfoResult RailTypeReserveInfo(uint id, int numinfo, int prop, Byte break; default: - ret = CIR_UNKNOWN; + ret = HandleAction0PropertyDefault(buf, prop); break; } } @@ -4427,7 +4445,7 @@ static ChangeInfoResult AirportTilesChangeInfo(uint airtid, int numinfo, int pro break; default: - ret = CIR_UNKNOWN; + ret = HandleAction0PropertyDefault(buf, prop); break; } } @@ -4435,7 +4453,7 @@ static ChangeInfoResult AirportTilesChangeInfo(uint airtid, int numinfo, int pro return ret; } -static bool HandleChangeInfoResult(const char *caller, ChangeInfoResult cir, uint8 feature, uint8 property) +static bool HandleChangeInfoResult(const char *caller, ChangeInfoResult cir, uint8 feature, int property) { switch (cir) { default: NOT_REACHED(); @@ -4464,6 +4482,42 @@ static bool HandleChangeInfoResult(const char *caller, ChangeInfoResult cir, uin } } +static int ReadAction0PropertyID(ByteReader *buf, uint8 feature) +{ + uint8 raw_prop = buf->ReadByte(); + const GRFFilePropertyRemapSet &remap = _cur.grffile->action0_property_remaps[feature]; + if (remap.remapped_ids[raw_prop]) { + auto iter = remap.mapping.find(raw_prop); + assert(iter != remap.mapping.end()); + const GRFFilePropertyRemapEntry &def = iter->second; + int prop = def.id; + if (prop == A0RPI_UNKNOWN_ERROR) { + grfmsg(0, "Error: Unimplemented mapped property: %s, feature: %X, mapped to: %X", def.name, def.feature, raw_prop); + GRFError *error = DisableGrf(STR_NEWGRF_ERROR_UNIMPLEMETED_MAPPED_PROPERTY); + error->data = stredup(def.name); + error->param_value[1] = def.feature; + error->param_value[2] = raw_prop; + } else if (prop == A0RPI_UNKNOWN_IGNORE) { + grfmsg(2, "Ignoring unimplemented mapped property: %s, feature: %X, mapped to: %X", def.name, def.feature, raw_prop); + } else { + if (HasBit(def.flags, GFPRE_CHECK_SIZE)) { + uint length = buf->ReadExtendedByte(); + if (length != def.expected_size) { + grfmsg(2, "Ignoring use of mapped property: %s, feature: %X, mapped to: %X, with incorrect data size: %u instead of %u", + def.name, def.feature, raw_prop, length, def.expected_size); + buf->Skip(length); + prop = A0RPI_SKIPPED_IGNORE; + } + } else { + prop |= A0RPI_CHECK_PROPERTY_LENGTH; + } + } + return prop; + } else { + return raw_prop; + } +} + /* Action 0x00 */ static void FeatureChangeInfo(ByteReader *buf) { @@ -4498,6 +4552,7 @@ static void FeatureChangeInfo(ByteReader *buf) /* GSF_RAILTYPES */ RailTypeChangeInfo, /* GSF_AIRPORTTILES */ AirportTilesChangeInfo, }; + static_assert(lengthof(handler) == lengthof(_cur.grffile->action0_property_remaps)); uint8 feature = buf->ReadByte(); uint8 numprops = buf->ReadByte(); @@ -4516,7 +4571,7 @@ static void FeatureChangeInfo(ByteReader *buf) SetBit(_cur.grffile->grf_features, feature); while (numprops-- && buf->HasData()) { - uint8 prop = buf->ReadByte(); + int prop = ReadAction0PropertyID(buf, feature); ChangeInfoResult cir = handler[feature](engine, numinfo, prop, buf); if (HandleChangeInfoResult("FeatureChangeInfo", cir, feature, prop)) return; @@ -4532,12 +4587,12 @@ static void SafeChangeInfo(ByteReader *buf) buf->ReadExtendedByte(); // id if (feature == GSF_BRIDGES && numprops == 1) { - uint8 prop = buf->ReadByte(); + int prop = ReadAction0PropertyID(buf, feature); /* Bridge property 0x0D is redefinition of sprite layout tables, which * is considered safe. */ if (prop == 0x0D) return; } else if (feature == GSF_GLOBALVAR && numprops == 1) { - uint8 prop = buf->ReadByte(); + int prop = ReadAction0PropertyID(buf, feature); /* Engine ID Mappings are safe, if the source is static */ if (prop == 0x11) { bool is_safe = true; @@ -4572,7 +4627,7 @@ static void ReserveChangeInfo(ByteReader *buf) uint8 index = buf->ReadExtendedByte(); while (numprops-- && buf->HasData()) { - uint8 prop = buf->ReadByte(); + int prop = ReadAction0PropertyID(buf, feature); ChangeInfoResult cir = CIR_SUCCESS; switch (feature) { @@ -5937,7 +5992,7 @@ bool GetGlobalVariable(byte param, uint32 *value, const GRFFile *grffile) } case 0x0D: // TTD Version, 00=DOS, 01=Windows - *value = _cur.grfconfig->palette & GRFP_USE_MASK; + *value = (_cur.grfconfig->palette & GRFP_USE_MASK) | grffile->var8D_overlay; return true; case 0x0E: // Y-offset for train sprites @@ -7865,6 +7920,7 @@ struct GRFFeatureInfo { /** Action14 feature list */ static const GRFFeatureInfo _grf_feature_list[] = { GRFFeatureInfo("feature_test", 1), + GRFFeatureInfo("property_mapping", 1), GRFFeatureInfo(), }; @@ -7937,7 +7993,7 @@ static bool ChangeGRFFeatureMaxVersion(size_t len, ByteReader *buf) return true; } -/** Callback function for 'FTST'->'SETP' to set the bit number of global variable 9D (platform version) to set/unset the result of the feature test. */ +/** Callback function for 'FTST'->'SETP' to set the bit number of global variable 9D (platform version) to set/unset with the result of the feature test. */ static bool ChangeGRFFeatureSetPlatformVarBit(size_t len, ByteReader *buf) { if (len != 1) { @@ -7974,10 +8030,177 @@ static bool HandleFeatureTestInfo(ByteReader *buf) return true; } +/** Action14 Action0 remappable property list */ +static const GRFPropertyMapDefinition _grf_action0_remappable_properties[] = { + GRFPropertyMapDefinition(), +}; + +/** Action14 Action0 property map action instance */ +struct GRFPropertyMapAction { + int feature; + int prop_id; + std::string name; + Action0RemapFallbackMode fallback_mode; + uint8 ttd_ver_var_bit; + + void Reset() + { + this->feature = -1; + this->prop_id = -1; + this->name.clear(); + this->fallback_mode = A0REM_IGNORE; + this->ttd_ver_var_bit = 0; + } + + void Execute() + { + if (this->feature < 0) { + grfmsg(2, "Action 14 property remapping: no feature defined, doing nothing"); + return; + } + if (this->prop_id < 0) { + grfmsg(2, "Action 14 property remapping: no property ID defined, doing nothing"); + return; + } + if (this->name.empty()) { + grfmsg(2, "Action 14 property remapping: no name defined, doing nothing"); + return; + } + bool success = false; + const char *str = this->name.c_str(); + for (const GRFPropertyMapDefinition *info = _grf_action0_remappable_properties; info->name != NULL; info++) { + if (info->feature == this->feature && strcmp(info->name, str) == 0) { + GRFFilePropertyRemapEntry &entry = _cur.grffile->action0_property_remaps[this->feature].Entry(this->prop_id); + entry.name = info->name; + entry.id = info->id; + entry.feature = this->feature; + entry.flags = 0; + if (info->expected_size >= 0) { + SetBit(entry.flags, GFPRE_CHECK_SIZE); + entry.expected_size = info->expected_size; + } + success = true; + break; + } + } + if (this->ttd_ver_var_bit > 0) { + SB(_cur.grffile->var8D_overlay, this->ttd_ver_var_bit, 1, success ? 1 : 0); + } + if (!success) { + if (this->fallback_mode == A0REM_ERROR_ON_DEFINITION) { + grfmsg(0, "Error: Unimplemented mapped property: %s, feature: %X, mapped to: %X", str, this->feature, this->prop_id); + GRFError *error = DisableGrf(STR_NEWGRF_ERROR_UNIMPLEMETED_MAPPED_PROPERTY); + error->data = stredup(str); + error->param_value[1] = this->feature; + error->param_value[2] = this->prop_id; + } else { + const char *str_store = stredup(str); + grfmsg(2, "Unimplemented mapped property: %s, feature: %X, mapped to: %X, %s on use", + str, this->feature, this->prop_id, (this->fallback_mode == A0REM_IGNORE) ? "ignoring" : "error"); + *(_cur.grffile->action0_unknown_property_names.Append()) = str_store; + GRFFilePropertyRemapEntry &entry = _cur.grffile->action0_property_remaps[this->feature].Entry(this->prop_id); + entry.name = str_store; + entry.id = (this->fallback_mode == A0REM_IGNORE) ? A0RPI_UNKNOWN_IGNORE : A0RPI_UNKNOWN_ERROR; + entry.feature = this->feature; + entry.flags = 0; + } + } + } +}; + +static GRFPropertyMapAction _current_grf_property_map_action; + +/** Callback function for 'A0PM'->'NAME' to set the name of the property to be mapped. */ +static bool ChangePropertyRemapName(byte langid, const char *str) +{ + _current_grf_property_map_action.name = str; + return true; +} + +/** Callback function for 'A0PM'->'FEAT' to set which feature this property mapping applies to. */ +static bool ChangePropertyRemapFeature(size_t len, ByteReader *buf) +{ + if (len != 1) { + grfmsg(2, "Action 14 property mapping: expected 1 byte for 'A0PM'->'FEAT' but got " PRINTF_SIZE ", ignoring this field", len); + buf->Skip(len); + } else { + uint8 feature = buf->ReadByte(); + if (feature >= GSF_END) { + grfmsg(2, "Action 14 property mapping: invalid feature ID: %u, in 'A0PM'->'FEAT', ignoring this field", feature); + } else { + _current_grf_property_map_action.feature = feature; + } + } + return true; +} + +/** Callback function for 'A0PM'->'PROP' to set the property ID to which this property is being mapped. */ +static bool ChangePropertyRemapPropertyId(size_t len, ByteReader *buf) +{ + if (len != 1) { + grfmsg(2, "Action 14 property mapping: expected 1 byte for 'A0PM'->'PROP' but got " PRINTF_SIZE ", ignoring this field", len); + buf->Skip(len); + } else { + _current_grf_property_map_action.prop_id = buf->ReadByte(); + } + return true; +} + +/** Callback function for 'A0PM'->'FLBK' to set the maximum version of the feature being tested. */ +static bool ChangePropertyRemapSetFallbackMode(size_t len, ByteReader *buf) +{ + if (len != 1) { + grfmsg(2, "Action 14 property mapping: expected 1 byte for 'A0PM'->'FLBK' but got " PRINTF_SIZE ", ignoring this field", len); + buf->Skip(len); + } else { + Action0RemapFallbackMode mode = (Action0RemapFallbackMode) buf->ReadByte(); + if (mode < A0REM_END) _current_grf_property_map_action.fallback_mode = mode; + } + return true; +} +/** Callback function for 'A0PM'->'SETT' to set the bit number of global variable 8D (TTD version) to set/unset with whether the remapping was successful. */ +static bool ChangePropertyRemapSetTTDVerVarBit(size_t len, ByteReader *buf) +{ + if (len != 1) { + grfmsg(2, "Action 14 property mapping: expected 1 byte for 'A0PM'->'SETT' but got " PRINTF_SIZE ", ignoring this field", len); + buf->Skip(len); + } else { + uint8 bit_number = buf->ReadByte(); + if (bit_number >= 4 && bit_number <= 31) { + _current_grf_property_map_action.ttd_ver_var_bit = bit_number; + } else { + grfmsg(2, "Action 14 property mapping: expected a bit number >= 4 and <= 32 for 'A0PM'->'SETT' but got %u, ignoring this field", bit_number); + } + } + return true; +} + +/** Action14 tags for the A0PM node */ +AllowedSubtags _tags_a0pm[] = { + AllowedSubtags('NAME', ChangePropertyRemapName), + AllowedSubtags('FEAT', ChangePropertyRemapFeature), + AllowedSubtags('PROP', ChangePropertyRemapPropertyId), + AllowedSubtags('FLBK', ChangePropertyRemapSetFallbackMode), + AllowedSubtags('SETT', ChangePropertyRemapSetTTDVerVarBit), + AllowedSubtags() +}; + +/** + * Callback function for 'A0PM' (action 0 property mapping) + */ +static bool HandleAction0PropertyMap(ByteReader *buf) +{ + _current_grf_property_map_action.Reset(); + HandleNodes(buf, _tags_a0pm); + _current_grf_property_map_action.Execute(); + return true; +} + /** Action14 root tags */ AllowedSubtags _tags_root_static[] = { AllowedSubtags('INFO', _tags_info), AllowedSubtags('FTST', SkipInfoChunk), + AllowedSubtags('A0PM', SkipInfoChunk), AllowedSubtags() }; @@ -7985,6 +8208,7 @@ AllowedSubtags _tags_root_static[] = { AllowedSubtags _tags_root_feature_tests[] = { AllowedSubtags('INFO', SkipInfoChunk), AllowedSubtags('FTST', HandleFeatureTestInfo), + AllowedSubtags('A0PM', HandleAction0PropertyMap), AllowedSubtags() }; diff --git a/src/newgrf.h b/src/newgrf.h index 0aec32d1bf..5255ea915f 100644 --- a/src/newgrf.h +++ b/src/newgrf.h @@ -18,6 +18,8 @@ #include "core/bitmath_func.hpp" #include "core/alloc_type.hpp" #include "core/smallvec_type.hpp" +#include +#include /** * List of different canal 'features'. @@ -100,6 +102,65 @@ struct GRFLabel { struct GRFLabel *next; }; +enum Action0RemapPropertyIds { + A0RPI_CHECK_PROPERTY_LENGTH = 0x10000, + A0RPI_UNKNOWN_IGNORE = 0x200, + A0RPI_UNKNOWN_ERROR, + A0RPI_SKIPPED_IGNORE, +}; + +enum Action0RemapFallbackMode { + A0REM_IGNORE, + A0REM_ERROR_ON_USE, + A0REM_ERROR_ON_DEFINITION, + A0REM_END, +}; + +struct GRFPropertyMapDefinition { + const char *name; // NULL indicates the end of the list + int id; + uint8 feature; + int expected_size; + + /** Create empty object used to identify the end of a list. */ + GRFPropertyMapDefinition() : + name(NULL), + id(0), + feature(0), + expected_size(0) + {} + + GRFPropertyMapDefinition(uint8 feature, int id, const char *name, int expected_size = -1) : + name(name), + id(id), + feature(feature), + expected_size(expected_size) + {} +}; + +enum GFPRE_Flags { + GFPRE_CHECK_SIZE, +}; + +struct GRFFilePropertyRemapEntry { + const char *name = nullptr; + int id = 0; + uint8 feature = 0; + uint8 flags = 0; + uint16 expected_size = 0; +}; + +struct GRFFilePropertyRemapSet { + std::bitset<256> remapped_ids; + std::map mapping; + + GRFFilePropertyRemapEntry &Entry(uint8 property) + { + this->remapped_ids.set(property); + return this->mapping[property]; + } +}; + /** Dynamic data of a loaded NewGRF */ struct GRFFile : ZeroedMemoryAllocator { char *filename; @@ -117,6 +178,9 @@ struct GRFFile : ZeroedMemoryAllocator { struct AirportSpec **airportspec; struct AirportTileSpec **airtspec; + GRFFilePropertyRemapSet action0_property_remaps[GSF_END]; + AutoFreeSmallVector action0_unknown_property_names; + uint32 param[0x80]; uint param_end; ///< one more than the highest set parameter @@ -138,6 +202,7 @@ struct GRFFile : ZeroedMemoryAllocator { uint32 grf_features; ///< Bitset of GrfSpecFeature the grf uses PriceMultipliers price_base_multipliers; ///< Price base multipliers as set by the grf. + uint32 var8D_overlay; ///< Overlay for global variable 8D (action 0x14) uint32 var9D_overlay; ///< Overlay for global variable 9D (action 0x14) GRFFile(const struct GRFConfig *config); diff --git a/src/newgrf_config.h b/src/newgrf_config.h index dc3b884dd3..77d595456f 100644 --- a/src/newgrf_config.h +++ b/src/newgrf_config.h @@ -109,7 +109,7 @@ struct GRFError : ZeroedMemoryAllocator { char *data; ///< Additional data for message and custom_message StringID message; ///< Default message StringID severity; ///< Info / Warning / Error / Fatal - uint32 param_value[2]; ///< Values of GRF parameters to show for message and custom_message + uint64 param_value[4]; ///< Values of GRF parameters to show for message and custom_message }; /** The possible types of a newgrf parameter. */