Merge branch 'newgrf-property-mapping' into jgrpp

# Conflicts:
#	docs/newgrf-additions.html
#	src/newgrf.cpp
pull/73/head
Jonathan G Rennison 6 years ago
commit 06c458cb19

@ -25,7 +25,7 @@
<p>See <a href="https://newgrf-specs.tt-wiki.net/wiki/Action14">Action 14 Specification</a> for background information.</p>
<h4>Feature Test: C "FTST"</h4>
<p>Each FTST chunk (type C) describes an individual feature test.<br />
Sub-chunks with each FTST chunk may appear in any order, however each sub-chunk SHOULD only appear ONCE within an individual FTST chunk.</p>
Sub-chunks within each FTST chunk may appear in any order, however each sub-chunk SHOULD only appear ONCE within an individual FTST chunk.</p>
<p>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.</p>
<h4>Feature Name: C "FTST" -> T "NAME"</h4>
@ -58,7 +58,6 @@
"B" "MINV" \w2 \w4
"B" "SETP" \w1 04
00
00
"C" "FTST"
"T" "NAME" 00 "sample_feature_2" 00
"B" "MINV" \w2 \w5
@ -70,6 +69,75 @@
// 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
</pre>
<br />
<h3 id="feature-test">Action 14 - Property Mapping for Action 0</h3>
<p>See <a href="https://newgrf-specs.tt-wiki.net/wiki/Action14">Action 14 Specification</a> and <a href="https://newgrf-specs.tt-wiki.net/wiki/Action0">Action 0 Specification</a> for background information.</p>
<p>The property mapping mechanism has the feature name: <font face="monospace">property_mapping</font>, this document describes version 1.</p>
<p>Users of this mechanism SHOULD at minimum use test for the presence of the feature above or test variable 8D, below.</p>
<h4>Property Mapping: C "A0PM"</h4>
<p>Each A0PM chunk (type C) describes an individual property mapping.<br />
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.</p>
<p>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:
<ul>
<li>The global variable 0x8D is checked to determine whether the property mapping operation was successful.</li>
<li>The feature name <font face="monospace">property_mapping</font> is checked for.</li>
</ul>
Unknown Action 14 blocks are ignored, and do not need to be skipped.</p>
<h4>Property Name: C "A0PM" -> T "NAME"</h4>
<p>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.</p>
<h4>Action 0 Feature ID: C "A0PM" -> B "FEAT"</h4>
<p>Within an A0PM chunk, the FEAT binary (type B) field contains the <a href="https://newgrf-specs.tt-wiki.net/wiki/Action0#Feature">Action 0 feature ID</a>. This is 1 byte.</p>
<h4>Property ID: C "A0PM" -> B "PROP"</h4>
<p>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.<br />
It is possible to override existing properties, however this use is not recommended.</p>
<h4>Success Indicator Global Variable 0x8D Bit: C "A0PM" -> B "SETT"</h4>
<p>Within an A0PM chunk, the SETT binary (type B) field contains the bit number to set/clear in
<a href="https://newgrf-specs.tt-wiki.net/wiki/GlobalVariables">global variable</a> 0x8D (TTD version) to store whether the mapping operation was successful. This is 1 byte.<br />
If the operation is successful, the bit is set (to 1), otherwise the bit is cleared (to 0).<br />
The bit number MUST be in the range: 4 &le; bit number &le; 31. These bits can be assumed to be 0 on implementations which do not support this property mapping mechanism.<br />
Global variable 0x8D can then be tested by using a standard <a href="https://newgrf-specs.tt-wiki.net/wiki/Action7">Action 7 or 9</a>, or a standard <a href="https://newgrf-specs.tt-wiki.net/wiki/VariationalAction2">Variational Action 2</a>.<br />
If this field is omitted, no bit is set or cleared.
</p>
<h4>Fallback Mode: C "A0PM" -> B "FLBK"</h4>
<p>Within an A0PM chunk, the FLBK binary (type B) field contains the fallback mode. This is 1 byte.<br />
The fallback mode may take the following values:
<table>
<tr><th>Value</th><th>Behaviour</th></tr>
<tr><td>0</td><td>Attempts to map an unknown property name are ignored. Use of the mapped property in an Action 0 is ignored. This is the default.</td></tr>
<tr><td>1</td><td>Attempts to map an unknown property name are ignored. Use of the mapped property in an Action 0 is an error.</td></tr>
<tr><td>2</td><td>Attempting to map an unknown property name is an error.</td></tr>
</table>
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.<br />
This chunk MAY be specified more than once, in which case the last specified valid value is used.<br />
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.
</p>
<h4>Format of remapped properties</h4>
All properties which are mapped by the mechanism have the format:
<table>
<tr><th>Size</th><th>Name</th><th>Meaning</th></tr>
<tr><td>B*</td><td>num</td><td>Size of the data in bytes</td></tr>
<tr><td>V</td><td>data</td><td>Property data</td></tr>
</table>
Note that num is an extended byte, see <a href="https://newgrf-specs.tt-wiki.net/wiki/GRFActionsDetailed">GRFActionsDetailed</a>.<br />
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.
<h4>Example NFO:</h4>
<pre>
// 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
</pre>
<br />
<h3><a href="https://newgrf-specs.tt-wiki.net/wiki/Action0/Stations">Action 0 - Stations</a></h3>
<h4><a href="https://newgrf-specs.tt-wiki.net/wiki/Action0/Stations#Minimum_bridge_height_.281B.29">Minimum bridge height (1B)</a></h4>
<p>This is indicated by the feature name: <font face="monospace">action0_station_prop1B</font>, version 1</p>

@ -1,3 +1,15 @@
openttd (1.8.0-0) unstable; urgency=low
* New upstream release 1.8.0
-- OpenTTD <info@openttd.org> 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 <info@openttd.org> 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 <m.kooijman@student.utwente.nl> Fri, 24 Dec 2004 02:58:47 +0100

@ -3064,6 +3064,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!

@ -3457,7 +3457,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}
@ -3470,6 +3470,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})

@ -998,6 +998,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.
@ -1034,7 +1052,7 @@ static ChangeInfoResult CommonVehicleChangeInfo(EngineInfo *ei, int prop, ByteRe
break;
default:
return CIR_UNKNOWN;
return HandleAction0PropertyDefault(buf, prop);
}
return CIR_SUCCESS;
@ -2094,7 +2112,7 @@ static ChangeInfoResult StationChangeInfo(uint stid, int numinfo, int prop, Byte
break;
default:
ret = CIR_UNKNOWN;
ret = HandleAction0PropertyDefault(buf, prop);
break;
}
}
@ -2132,7 +2150,7 @@ static ChangeInfoResult CanalChangeInfo(uint id, int numinfo, int prop, ByteRead
break;
default:
ret = CIR_UNKNOWN;
ret = HandleAction0PropertyDefault(buf, prop);
break;
}
}
@ -2249,7 +2267,7 @@ static ChangeInfoResult BridgeChangeInfo(uint brid, int numinfo, int prop, ByteR
break;
default:
ret = CIR_UNKNOWN;
ret = HandleAction0PropertyDefault(buf, prop);
break;
}
}
@ -2312,7 +2330,7 @@ static ChangeInfoResult IgnoreTownHouseProperty(int prop, ByteReader *buf)
}
default:
ret = CIR_UNKNOWN;
ret = HandleAction0PropertyDefault(buf, prop);
break;
}
return ret;
@ -2544,7 +2562,7 @@ static ChangeInfoResult TownHouseChangeInfo(uint hid, int numinfo, int prop, Byt
break;
default:
ret = CIR_UNKNOWN;
ret = HandleAction0PropertyDefault(buf, prop);
break;
}
}
@ -2806,7 +2824,7 @@ static ChangeInfoResult GlobalVarChangeInfo(uint gvid, int numinfo, int prop, By
}
default:
ret = CIR_UNKNOWN;
ret = HandleAction0PropertyDefault(buf, prop);
break;
}
}
@ -2868,7 +2886,7 @@ static ChangeInfoResult GlobalVarReserveInfo(uint gvid, int numinfo, int prop, B
break;
default:
ret = CIR_UNKNOWN;
ret = HandleAction0PropertyDefault(buf, prop);
break;
}
}
@ -3007,7 +3025,7 @@ static ChangeInfoResult CargoChangeInfo(uint cid, int numinfo, int prop, ByteRea
break;
default:
ret = CIR_UNKNOWN;
ret = HandleAction0PropertyDefault(buf, prop);
break;
}
}
@ -3065,7 +3083,7 @@ static ChangeInfoResult SoundEffectChangeInfo(uint sid, int numinfo, int prop, B
}
default:
ret = CIR_UNKNOWN;
ret = HandleAction0PropertyDefault(buf, prop);
break;
}
}
@ -3105,7 +3123,7 @@ static ChangeInfoResult IgnoreIndustryTileProperty(int prop, ByteReader *buf)
break;
default:
ret = CIR_UNKNOWN;
ret = HandleAction0PropertyDefault(buf, prop);
break;
}
return ret;
@ -3243,7 +3261,7 @@ static ChangeInfoResult IndustrytilesChangeInfo(uint indtid, int numinfo, int pr
}
default:
ret = CIR_UNKNOWN;
ret = HandleAction0PropertyDefault(buf, prop);
break;
}
}
@ -3336,7 +3354,7 @@ static ChangeInfoResult IgnoreIndustryProperty(int prop, ByteReader *buf)
}
default:
ret = CIR_UNKNOWN;
ret = HandleAction0PropertyDefault(buf, prop);
break;
}
return ret;
@ -3761,7 +3779,7 @@ static ChangeInfoResult IndustriesChangeInfo(uint indid, int numinfo, int prop,
}
default:
ret = CIR_UNKNOWN;
ret = HandleAction0PropertyDefault(buf, prop);
break;
}
}
@ -3964,7 +3982,7 @@ static ChangeInfoResult AirportChangeInfo(uint airport, int numinfo, int prop, B
break;
default:
ret = CIR_UNKNOWN;
ret = HandleAction0PropertyDefault(buf, prop);
break;
}
}
@ -4009,7 +4027,7 @@ static ChangeInfoResult IgnoreObjectProperty(uint prop, ByteReader *buf)
break;
default:
ret = CIR_UNKNOWN;
ret = HandleAction0PropertyDefault(buf, prop);
break;
}
@ -4139,7 +4157,7 @@ static ChangeInfoResult ObjectChangeInfo(uint id, int numinfo, int prop, ByteRea
break;
default:
ret = CIR_UNKNOWN;
ret = HandleAction0PropertyDefault(buf, prop);
break;
}
}
@ -4277,7 +4295,7 @@ static ChangeInfoResult RailTypeChangeInfo(uint id, int numinfo, int prop, ByteR
break;
default:
ret = CIR_UNKNOWN;
ret = HandleAction0PropertyDefault(buf, prop);
break;
}
}
@ -4357,7 +4375,7 @@ static ChangeInfoResult RailTypeReserveInfo(uint id, int numinfo, int prop, Byte
break;
default:
ret = CIR_UNKNOWN;
ret = HandleAction0PropertyDefault(buf, prop);
break;
}
}
@ -4447,7 +4465,7 @@ static ChangeInfoResult AirportTilesChangeInfo(uint airtid, int numinfo, int pro
break;
default:
ret = CIR_UNKNOWN;
ret = HandleAction0PropertyDefault(buf, prop);
break;
}
}
@ -4455,7 +4473,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();
@ -4484,6 +4502,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)
{
@ -4518,6 +4572,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();
@ -4536,7 +4591,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;
@ -4552,12 +4607,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;
@ -4592,7 +4647,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) {
@ -5963,7 +6018,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
@ -7891,6 +7946,7 @@ struct GRFFeatureInfo {
/** Action14 feature list */
static const GRFFeatureInfo _grf_feature_list[] = {
GRFFeatureInfo("feature_test", 1),
GRFFeatureInfo("property_mapping", 1),
GRFFeatureInfo("action0_station_prop1B", 1),
GRFFeatureInfo("varaction2_station_var42", 1),
GRFFeatureInfo("more_bridge_types", 1),
@ -7967,7 +8023,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 with the result of the feature test. */
static bool ChangeGRFFeatureSetPlatformVarBit(size_t len, ByteReader *buf)
{
if (len != 1) {
@ -8004,10 +8060,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()
};
@ -8015,6 +8238,7 @@ AllowedSubtags _tags_root_static[] = {
AllowedSubtags _tags_root_feature_tests[] = {
AllowedSubtags('INFO', SkipInfoChunk),
AllowedSubtags('FTST', HandleFeatureTestInfo),
AllowedSubtags('A0PM', HandleAction0PropertyMap),
AllowedSubtags()
};

@ -19,6 +19,8 @@
#include "core/bitmath_func.hpp"
#include "core/alloc_type.hpp"
#include "core/smallvec_type.hpp"
#include "3rdparty/cpp-btree/btree_map.h"
#include <bitset>
/**
* List of different canal 'features'.
@ -101,6 +103,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;
btree::btree_map<uint8, GRFFilePropertyRemapEntry> 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;
@ -118,6 +179,9 @@ struct GRFFile : ZeroedMemoryAllocator {
struct AirportSpec **airportspec;
struct AirportTileSpec **airtspec;
GRFFilePropertyRemapSet action0_property_remaps[GSF_END];
AutoFreeSmallVector<const char *, 8> action0_unknown_property_names;
uint32 param[0x80];
uint param_end; ///< one more than the highest set parameter
@ -139,6 +203,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);

@ -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. */

@ -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
*

Loading…
Cancel
Save