Add an action 5 type ID remapping mechanism
This commit is contained in:
@@ -18,7 +18,7 @@
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h2>Additions to NewGRF Specifications in thi branch</h2>
|
<h2>Additions to NewGRF Specifications in this branch</h2>
|
||||||
<p>This document describes non-standard additions to the <a href="https://newgrf-specs.tt-wiki.net/wiki/Main_Page">Official OpenTTD NewGRF Specifications</a> which are present in this branch.
|
<p>This document describes non-standard additions to the <a href="https://newgrf-specs.tt-wiki.net/wiki/Main_Page">Official OpenTTD NewGRF Specifications</a> which are present in this branch.
|
||||||
<p>This additions MAY also be present in other branches/repositories/etc. They MAY be removed or moved in future, if necessary.</p>
|
<p>This additions MAY also be present in other branches/repositories/etc. They MAY be removed or moved in future, if necessary.</p>
|
||||||
<p>NewGRFs which use any of these features SHOULD use the <a href="#feature-test">feature testing</a> mechanism described below to check whether individual added features are supported.</p>
|
<p>NewGRFs which use any of these features SHOULD use the <a href="#feature-test">feature testing</a> mechanism described below to check whether individual added features are supported.</p>
|
||||||
@@ -72,7 +72,7 @@
|
|||||||
-1 * -1 07 9D 01 \70 04 01
|
-1 * -1 07 9D 01 \70 04 01
|
||||||
</pre>
|
</pre>
|
||||||
<br />
|
<br />
|
||||||
<h3 id="feature-test">Action 14 - Property Mapping for Action 0</h3>
|
<h3 id="property-mapping">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>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>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 test for the presence of the feature above or test variable 8D, below.</p>
|
<p>Users of this mechanism SHOULD at minimum test for the presence of the feature above or test variable 8D, below.</p>
|
||||||
@@ -92,7 +92,7 @@
|
|||||||
<h4>Property ID: C "A0PM" -> B "PROP"</h4>
|
<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 />
|
<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>
|
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>
|
<h4 id="SETT">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
|
<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 />
|
<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 />
|
If the operation is successful, the bit is set (to 1), otherwise the bit is cleared (to 0).<br />
|
||||||
@@ -100,7 +100,7 @@
|
|||||||
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 />
|
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.
|
If this field is omitted, no bit is set or cleared.
|
||||||
</p>
|
</p>
|
||||||
<h4>Fallback Mode: C "A0PM" -> B "FLBK"</h4>
|
<h4 id="FLBK">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 />
|
<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:
|
The fallback mode may take the following values:
|
||||||
<table>
|
<table>
|
||||||
@@ -140,5 +140,46 @@
|
|||||||
// Set sample_station_property for station ID 10 to 2 byte value: AB CD
|
// 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
|
-1 * -1 00 04 01 01 10 F8 02 AB CD
|
||||||
</pre>
|
</pre>
|
||||||
|
<br />
|
||||||
|
<h3 id="feature-test">Action 14 - Type ID Mapping for Action 5</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/Action5">Action 5 Specification</a> for background information.</p>
|
||||||
|
<p>The action 5 type ID mapping mechanism has the feature name: <font face="monospace">action5_type_id_mapping</font>, this document describes version 1.</p>
|
||||||
|
<p>Users of this mechanism SHOULD at minimum test for the presence of the feature above or test variable 8D, below.</p>
|
||||||
|
<h4>Action 5 type ID Mapping: C "A5TM"</h4>
|
||||||
|
<p>Each A5TM chunk (type C) describes an individual action 5 type ID mapping.<br />
|
||||||
|
Sub-chunks within each A5TM chunk may appear in any order, however except where otherwise noted each sub-chunk SHOULD only appear ONCE within an individual A5TM chunk.</p>
|
||||||
|
<p>Action 5 type ID mapping can be safely used on implementations which do not implement the type ID mapping mechanism if Action 5 sprites which use mapped type IDs are skipped if one or more of:
|
||||||
|
<ul>
|
||||||
|
<li>The global variable 0x8D is checked to determine whether the type ID mapping operation was successful.</li>
|
||||||
|
<li>The feature name <font face="monospace">action5_type_id_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 "A5TM" -> T "NAME"</h4>
|
||||||
|
<p>Within an A5TM chunk, the NAME text (type T) field contains the name of the type to map. The value of the language ID byte is ignored.</p>
|
||||||
|
<h4>Property ID: C "A5TM" -> B "TYPE"</h4>
|
||||||
|
<p>Within an A5TM chunk, the TYPE binary (type B) field contains the type ID to allocate to the named type, this value can used in Action 5 sprites. This is 1 byte. The value MUST be < 128 (i.e bit 7 must be clear).<br />
|
||||||
|
It is possible to override existing type IDs, however this use is not recommended.</p>
|
||||||
|
<h4>Success Indicator Global Variable 0x8D Bit: C "A5TM" -> B "SETT"</h4>
|
||||||
|
<p>This behaves identically to the <a href="#SETT">C "A0PM" -> B "SETT"</a> case, above</p>
|
||||||
|
<h4>Fallback Mode: C "A5TM" -> B "FLBK"</h4>
|
||||||
|
<p>This behaves identically to the <a href="#FLBK">C "A0PM" -> B "FLBK"</a> case, above</p>
|
||||||
|
<h4>Example NFO:</h4>
|
||||||
|
<pre>
|
||||||
|
// Map action5 type "sample_action5_type" to type id 0x70, and set bit 5 of global variable 0x8D if successful
|
||||||
|
-1 * -1 14
|
||||||
|
"C" "A5TM"
|
||||||
|
"T" "NAME" 00 "sample_action5_type" 00
|
||||||
|
"B" "TYPE" \w1 70
|
||||||
|
"B" "SETT" \w1 5
|
||||||
|
00
|
||||||
|
00
|
||||||
|
....
|
||||||
|
// Skip 3 sprites if bit 5 of global variable 0x8D is not set (indicating that station property sample_action5_type is NOT present)
|
||||||
|
-1 * -1 07 8D 01 \70 05 03
|
||||||
|
// Set 2 sprites at offset 7 into sample_action5_type
|
||||||
|
-1 * -1 05 F0 02 07
|
||||||
|
-1 sprites/sample.png 546 8 09 23 33 -26 0
|
||||||
|
-1 sprites/sample.png 594 8 09 23 33 -5 0
|
||||||
|
</pre>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@@ -2964,6 +2964,7 @@ STR_NEWGRF_ERROR_STATIC_GRF_CAUSES_DESYNC :Loading {1:RAW_
|
|||||||
STR_NEWGRF_ERROR_UNEXPECTED_SPRITE :Unexpected sprite (sprite {3:NUM})
|
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_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_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_UNIMPLEMETED_MAPPED_ACTION5_TYPE :Unimplemented remapped Action 5 type: name: {2:RAW_STRING}, mapped to: {4:HEX} (sprite {3:NUM})
|
||||||
STR_NEWGRF_ERROR_INVALID_ID :Attempt to use invalid ID (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_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})
|
STR_NEWGRF_ERROR_MULTIPLE_ACTION_8 :Contains multiple Action 8 entries (sprite {3:NUM})
|
||||||
|
185
src/newgrf.cpp
185
src/newgrf.cpp
@@ -5811,22 +5811,6 @@ static uint16 SanitizeSpriteOffset(uint16& num, uint16 offset, int max_sprites,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** The type of action 5 type. */
|
|
||||||
enum Action5BlockType {
|
|
||||||
A5BLOCK_FIXED, ///< Only allow replacing a whole block of sprites. (TTDP compatible)
|
|
||||||
A5BLOCK_ALLOW_OFFSET, ///< Allow replacing any subset by specifiing an offset.
|
|
||||||
A5BLOCK_INVALID, ///< unknown/not-implemented type
|
|
||||||
};
|
|
||||||
/** Information about a single action 5 type. */
|
|
||||||
struct Action5Type {
|
|
||||||
Action5BlockType block_type; ///< How is this Action5 type processed?
|
|
||||||
SpriteID sprite_base; ///< Load the sprites starting from this sprite.
|
|
||||||
uint16 min_sprites; ///< If the Action5 contains less sprites, the whole block will be ignored.
|
|
||||||
uint16 max_sprites; ///< If the Action5 contains more sprites, only the first max_sprites sprites will be used.
|
|
||||||
const char *name; ///< Name for error messages.
|
|
||||||
};
|
|
||||||
|
|
||||||
/** The information about action 5 types. */
|
/** The information about action 5 types. */
|
||||||
static const Action5Type _action5_types[] = {
|
static const Action5Type _action5_types[] = {
|
||||||
/* Note: min_sprites should not be changed. Therefore these constants are directly here and not in sprites.h */
|
/* Note: min_sprites should not be changed. Therefore these constants are directly here and not in sprites.h */
|
||||||
@@ -5872,32 +5856,54 @@ static void GraphicsNew(ByteReader *buf)
|
|||||||
uint16 offset = HasBit(type, 7) ? buf->ReadExtendedByte() : 0;
|
uint16 offset = HasBit(type, 7) ? buf->ReadExtendedByte() : 0;
|
||||||
ClrBit(type, 7); // Clear the high bit as that only indicates whether there is an offset.
|
ClrBit(type, 7); // Clear the high bit as that only indicates whether there is an offset.
|
||||||
|
|
||||||
if ((type == 0x0D) && (num == 10) && HasBit(_cur.grfconfig->flags, GCF_SYSTEM)) {
|
const Action5Type *action5_type;
|
||||||
/* Special not-TTDP-compatible case used in openttd.grf
|
const Action5TypeRemapSet &remap = _cur.grffile->action5_type_remaps;
|
||||||
* Missing shore sprites and initialisation of SPR_SHORE_BASE */
|
if (remap.remapped_ids[type]) {
|
||||||
grfmsg(2, "GraphicsNew: Loading 10 missing shore sprites from extra grf.");
|
auto iter = remap.mapping.find(type);
|
||||||
LoadNextSprite(SPR_SHORE_BASE + 0, _cur.file_index, _cur.nfo_line++, _cur.grf_container_ver); // SLOPE_STEEP_S
|
assert(iter != remap.mapping.end());
|
||||||
LoadNextSprite(SPR_SHORE_BASE + 5, _cur.file_index, _cur.nfo_line++, _cur.grf_container_ver); // SLOPE_STEEP_W
|
const Action5TypeRemapEntry &def = iter->second;
|
||||||
LoadNextSprite(SPR_SHORE_BASE + 7, _cur.file_index, _cur.nfo_line++, _cur.grf_container_ver); // SLOPE_WSE
|
if (def.info == nullptr) {
|
||||||
LoadNextSprite(SPR_SHORE_BASE + 10, _cur.file_index, _cur.nfo_line++, _cur.grf_container_ver); // SLOPE_STEEP_N
|
if (def.fallback_mode == GPMFM_ERROR_ON_USE) {
|
||||||
LoadNextSprite(SPR_SHORE_BASE + 11, _cur.file_index, _cur.nfo_line++, _cur.grf_container_ver); // SLOPE_NWS
|
grfmsg(0, "Error: Unimplemented action 5 type: %s, mapped to: %X", def.name, type);
|
||||||
LoadNextSprite(SPR_SHORE_BASE + 13, _cur.file_index, _cur.nfo_line++, _cur.grf_container_ver); // SLOPE_ENW
|
GRFError *error = DisableGrf(STR_NEWGRF_ERROR_UNIMPLEMETED_MAPPED_ACTION5_TYPE);
|
||||||
LoadNextSprite(SPR_SHORE_BASE + 14, _cur.file_index, _cur.nfo_line++, _cur.grf_container_ver); // SLOPE_SEN
|
error->data = stredup(def.name);
|
||||||
LoadNextSprite(SPR_SHORE_BASE + 15, _cur.file_index, _cur.nfo_line++, _cur.grf_container_ver); // SLOPE_STEEP_E
|
error->param_value[1] = type;
|
||||||
LoadNextSprite(SPR_SHORE_BASE + 16, _cur.file_index, _cur.nfo_line++, _cur.grf_container_ver); // SLOPE_EW
|
} else if (def.fallback_mode == GPMFM_IGNORE) {
|
||||||
LoadNextSprite(SPR_SHORE_BASE + 17, _cur.file_index, _cur.nfo_line++, _cur.grf_container_ver); // SLOPE_NS
|
grfmsg(2, "Ignoring unimplemented action 5 type: %s, mapped to: %X", def.name, type);
|
||||||
if (_loaded_newgrf_features.shore == SHORE_REPLACE_NONE) _loaded_newgrf_features.shore = SHORE_REPLACE_ONLY_NEW;
|
}
|
||||||
return;
|
_cur.skip_sprites = num;
|
||||||
}
|
return;
|
||||||
|
} else {
|
||||||
|
action5_type = def.info;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ((type == 0x0D) && (num == 10) && HasBit(_cur.grfconfig->flags, GCF_SYSTEM)) {
|
||||||
|
/* Special not-TTDP-compatible case used in openttd.grf
|
||||||
|
* Missing shore sprites and initialisation of SPR_SHORE_BASE */
|
||||||
|
grfmsg(2, "GraphicsNew: Loading 10 missing shore sprites from extra grf.");
|
||||||
|
LoadNextSprite(SPR_SHORE_BASE + 0, _cur.file_index, _cur.nfo_line++, _cur.grf_container_ver); // SLOPE_STEEP_S
|
||||||
|
LoadNextSprite(SPR_SHORE_BASE + 5, _cur.file_index, _cur.nfo_line++, _cur.grf_container_ver); // SLOPE_STEEP_W
|
||||||
|
LoadNextSprite(SPR_SHORE_BASE + 7, _cur.file_index, _cur.nfo_line++, _cur.grf_container_ver); // SLOPE_WSE
|
||||||
|
LoadNextSprite(SPR_SHORE_BASE + 10, _cur.file_index, _cur.nfo_line++, _cur.grf_container_ver); // SLOPE_STEEP_N
|
||||||
|
LoadNextSprite(SPR_SHORE_BASE + 11, _cur.file_index, _cur.nfo_line++, _cur.grf_container_ver); // SLOPE_NWS
|
||||||
|
LoadNextSprite(SPR_SHORE_BASE + 13, _cur.file_index, _cur.nfo_line++, _cur.grf_container_ver); // SLOPE_ENW
|
||||||
|
LoadNextSprite(SPR_SHORE_BASE + 14, _cur.file_index, _cur.nfo_line++, _cur.grf_container_ver); // SLOPE_SEN
|
||||||
|
LoadNextSprite(SPR_SHORE_BASE + 15, _cur.file_index, _cur.nfo_line++, _cur.grf_container_ver); // SLOPE_STEEP_E
|
||||||
|
LoadNextSprite(SPR_SHORE_BASE + 16, _cur.file_index, _cur.nfo_line++, _cur.grf_container_ver); // SLOPE_EW
|
||||||
|
LoadNextSprite(SPR_SHORE_BASE + 17, _cur.file_index, _cur.nfo_line++, _cur.grf_container_ver); // SLOPE_NS
|
||||||
|
if (_loaded_newgrf_features.shore == SHORE_REPLACE_NONE) _loaded_newgrf_features.shore = SHORE_REPLACE_ONLY_NEW;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* Supported type? */
|
/* Supported type? */
|
||||||
if ((type >= lengthof(_action5_types)) || (_action5_types[type].block_type == A5BLOCK_INVALID)) {
|
if ((type >= lengthof(_action5_types)) || (_action5_types[type].block_type == A5BLOCK_INVALID)) {
|
||||||
grfmsg(2, "GraphicsNew: Custom graphics (type 0x%02X) sprite block of length %u (unimplemented, ignoring)", type, num);
|
grfmsg(2, "GraphicsNew: Custom graphics (type 0x%02X) sprite block of length %u (unimplemented, ignoring)", type, num);
|
||||||
_cur.skip_sprites = num;
|
_cur.skip_sprites = num;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Action5Type *action5_type = &_action5_types[type];
|
action5_type = &_action5_types[type];
|
||||||
|
}
|
||||||
|
|
||||||
/* Contrary to TTDP we allow always to specify too few sprites as we allow always an offset,
|
/* Contrary to TTDP we allow always to specify too few sprites as we allow always an offset,
|
||||||
* except for the long version of the shore type:
|
* except for the long version of the shore type:
|
||||||
@@ -7929,6 +7935,7 @@ struct GRFFeatureInfo {
|
|||||||
static const GRFFeatureInfo _grf_feature_list[] = {
|
static const GRFFeatureInfo _grf_feature_list[] = {
|
||||||
GRFFeatureInfo("feature_test", 1),
|
GRFFeatureInfo("feature_test", 1),
|
||||||
GRFFeatureInfo("property_mapping", 1),
|
GRFFeatureInfo("property_mapping", 1),
|
||||||
|
GRFFeatureInfo("action5_type_id_mapping", 1),
|
||||||
GRFFeatureInfo(),
|
GRFFeatureInfo(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -8043,6 +8050,11 @@ static const GRFPropertyMapDefinition _grf_action0_remappable_properties[] = {
|
|||||||
GRFPropertyMapDefinition(),
|
GRFPropertyMapDefinition(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** Action14 Action5 remappable type list */
|
||||||
|
static const Action5TypeRemapDefinition _grf_action5_remappable_types[] = {
|
||||||
|
Action5TypeRemapDefinition(),
|
||||||
|
};
|
||||||
|
|
||||||
/** Action14 Action0 property map action instance */
|
/** Action14 Action0 property map action instance */
|
||||||
struct GRFPropertyMapAction {
|
struct GRFPropertyMapAction {
|
||||||
const char *tag_name = NULL;
|
const char *tag_name = NULL;
|
||||||
@@ -8107,7 +8119,7 @@ struct GRFPropertyMapAction {
|
|||||||
const char *str_store = stredup(str);
|
const char *str_store = stredup(str);
|
||||||
grfmsg(2, "Unimplemented mapped %s: %s, feature: %X, mapped to: %X, %s on use",
|
grfmsg(2, "Unimplemented mapped %s: %s, feature: %X, mapped to: %X, %s on use",
|
||||||
this->descriptor, str, this->feature, this->prop_id, (this->fallback_mode == GPMFM_IGNORE) ? "ignoring" : "error");
|
this->descriptor, str, this->feature, this->prop_id, (this->fallback_mode == GPMFM_IGNORE) ? "ignoring" : "error");
|
||||||
*(_cur.grffile->action0_unknown_property_names.Append()) = str_store;
|
*(_cur.grffile->remap_unknown_property_names.Append()) = str_store;
|
||||||
GRFFilePropertyRemapEntry &entry = _cur.grffile->action0_property_remaps[this->feature].Entry(this->prop_id);
|
GRFFilePropertyRemapEntry &entry = _cur.grffile->action0_property_remaps[this->feature].Entry(this->prop_id);
|
||||||
entry.name = str_store;
|
entry.name = str_store;
|
||||||
entry.id = (this->fallback_mode == GPMFM_IGNORE) ? A0RPI_UNKNOWN_IGNORE : A0RPI_UNKNOWN_ERROR;
|
entry.id = (this->fallback_mode == GPMFM_IGNORE) ? A0RPI_UNKNOWN_IGNORE : A0RPI_UNKNOWN_ERROR;
|
||||||
@@ -8116,6 +8128,51 @@ struct GRFPropertyMapAction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ExecuteAction5TypeRemapping()
|
||||||
|
{
|
||||||
|
if (this->prop_id < 0) {
|
||||||
|
grfmsg(2, "Action 14 %s remapping: no type ID defined, doing nothing", this->descriptor);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this->name.empty()) {
|
||||||
|
grfmsg(2, "Action 14 %s remapping: no name defined, doing nothing", this->descriptor);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bool success = false;
|
||||||
|
const char *str = this->name.c_str();
|
||||||
|
for (const Action5TypeRemapDefinition *info = _grf_action5_remappable_types; info->name != NULL; info++) {
|
||||||
|
if (strcmp(info->name, str) == 0) {
|
||||||
|
Action5TypeRemapEntry &entry = _cur.grffile->action5_type_remaps.Entry(this->prop_id);
|
||||||
|
entry.name = info->name;
|
||||||
|
entry.info = &(info->info);
|
||||||
|
entry.type_id = this->prop_id;
|
||||||
|
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 == GPMFM_ERROR_ON_DEFINITION) {
|
||||||
|
grfmsg(0, "Error: Unimplemented mapped %s: %s, mapped to: %X", this->descriptor, str, this->prop_id);
|
||||||
|
GRFError *error = DisableGrf(STR_NEWGRF_ERROR_UNIMPLEMETED_MAPPED_ACTION5_TYPE);
|
||||||
|
error->data = stredup(str);
|
||||||
|
error->param_value[1] = this->prop_id;
|
||||||
|
} else {
|
||||||
|
const char *str_store = stredup(str);
|
||||||
|
grfmsg(2, "Unimplemented mapped %s: %s, mapped to: %X, %s on use",
|
||||||
|
this->descriptor, str, this->prop_id, (this->fallback_mode == GPMFM_IGNORE) ? "ignoring" : "error");
|
||||||
|
*(_cur.grffile->remap_unknown_property_names.Append()) = str_store;
|
||||||
|
Action5TypeRemapEntry &entry = _cur.grffile->action5_type_remaps.Entry(this->prop_id);
|
||||||
|
entry.name = str_store;
|
||||||
|
entry.info = nullptr;
|
||||||
|
entry.type_id = this->prop_id;
|
||||||
|
entry.fallback_mode = this->fallback_mode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static GRFPropertyMapAction _current_grf_property_map_action;
|
static GRFPropertyMapAction _current_grf_property_map_action;
|
||||||
@@ -8145,12 +8202,12 @@ static bool ChangePropertyRemapFeature(size_t len, ByteReader *buf)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Callback function for to set the property ID to which this item is being mapped. */
|
/** Callback function for ->'PROP' to set the property ID to which this item is being mapped. */
|
||||||
static bool ChangePropertyRemapPropertyIdGeneric(size_t len, ByteReader *buf, const char *tag)
|
static bool ChangePropertyRemapPropertyId(size_t len, ByteReader *buf)
|
||||||
{
|
{
|
||||||
GRFPropertyMapAction &action = _current_grf_property_map_action;
|
GRFPropertyMapAction &action = _current_grf_property_map_action;
|
||||||
if (len != 1) {
|
if (len != 1) {
|
||||||
grfmsg(2, "Action 14 %s mapping: expected 1 byte for '%s'->'%s' but got " PRINTF_SIZE ", ignoring this field", action.descriptor, action.tag_name, tag, len);
|
grfmsg(2, "Action 14 %s mapping: expected 1 byte for '%s'->'PROP' but got " PRINTF_SIZE ", ignoring this field", action.descriptor, action.tag_name, len);
|
||||||
buf->Skip(len);
|
buf->Skip(len);
|
||||||
} else {
|
} else {
|
||||||
action.prop_id = buf->ReadByte();
|
action.prop_id = buf->ReadByte();
|
||||||
@@ -8158,10 +8215,22 @@ static bool ChangePropertyRemapPropertyIdGeneric(size_t len, ByteReader *buf, co
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Callback function for ->'PROP' to set the property ID to which this item is being mapped. */
|
/** Callback function for ->'TYPE' to set the property ID to which this item is being mapped. */
|
||||||
static bool ChangePropertyRemapPropertyId(size_t len, ByteReader *buf)
|
static bool ChangePropertyRemapTypeId(size_t len, ByteReader *buf)
|
||||||
{
|
{
|
||||||
return ChangePropertyRemapPropertyIdGeneric(len, buf, "PROP");
|
GRFPropertyMapAction &action = _current_grf_property_map_action;
|
||||||
|
if (len != 1) {
|
||||||
|
grfmsg(2, "Action 14 %s mapping: expected 1 byte for '%s'->'TYPE' but got " PRINTF_SIZE ", ignoring this field", action.descriptor, action.tag_name, len);
|
||||||
|
buf->Skip(len);
|
||||||
|
} else {
|
||||||
|
uint8 prop = buf->ReadByte();
|
||||||
|
if (prop < 128) {
|
||||||
|
action.prop_id = prop;
|
||||||
|
} else {
|
||||||
|
grfmsg(2, "Action 14 %s mapping: expected a type < 128 for '%s'->'TYPE' but got %u, ignoring this field", action.descriptor, action.tag_name, prop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Callback function for ->'FLBK' to set the fallback mode. */
|
/** Callback function for ->'FLBK' to set the fallback mode. */
|
||||||
@@ -8216,11 +8285,32 @@ static bool HandleAction0PropertyMap(ByteReader *buf)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Action14 tags for the A5TM node */
|
||||||
|
AllowedSubtags _tags_a5tm[] = {
|
||||||
|
AllowedSubtags('NAME', ChangePropertyRemapName),
|
||||||
|
AllowedSubtags('TYPE', ChangePropertyRemapTypeId),
|
||||||
|
AllowedSubtags('FLBK', ChangePropertyRemapSetFallbackMode),
|
||||||
|
AllowedSubtags('SETT', ChangePropertyRemapSetTTDVerVarBit),
|
||||||
|
AllowedSubtags()
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback function for 'A5TM' (action 5 type mapping)
|
||||||
|
*/
|
||||||
|
static bool HandleAction5TypeMap(ByteReader *buf)
|
||||||
|
{
|
||||||
|
_current_grf_property_map_action.Reset("A5TM", "Action 5 type");
|
||||||
|
HandleNodes(buf, _tags_a5tm);
|
||||||
|
_current_grf_property_map_action.ExecuteAction5TypeRemapping();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/** Action14 root tags */
|
/** Action14 root tags */
|
||||||
AllowedSubtags _tags_root_static[] = {
|
AllowedSubtags _tags_root_static[] = {
|
||||||
AllowedSubtags('INFO', _tags_info),
|
AllowedSubtags('INFO', _tags_info),
|
||||||
AllowedSubtags('FTST', SkipInfoChunk),
|
AllowedSubtags('FTST', SkipInfoChunk),
|
||||||
AllowedSubtags('A0PM', SkipInfoChunk),
|
AllowedSubtags('A0PM', SkipInfoChunk),
|
||||||
|
AllowedSubtags('A5TM', SkipInfoChunk),
|
||||||
AllowedSubtags()
|
AllowedSubtags()
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -8229,6 +8319,7 @@ AllowedSubtags _tags_root_feature_tests[] = {
|
|||||||
AllowedSubtags('INFO', SkipInfoChunk),
|
AllowedSubtags('INFO', SkipInfoChunk),
|
||||||
AllowedSubtags('FTST', HandleFeatureTestInfo),
|
AllowedSubtags('FTST', HandleFeatureTestInfo),
|
||||||
AllowedSubtags('A0PM', HandleAction0PropertyMap),
|
AllowedSubtags('A0PM', HandleAction0PropertyMap),
|
||||||
|
AllowedSubtags('A5TM', HandleAction5TypeMap),
|
||||||
AllowedSubtags()
|
AllowedSubtags()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
52
src/newgrf.h
52
src/newgrf.h
@@ -152,6 +152,55 @@ struct GRFFilePropertyRemapSet {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** The type of action 5 type. */
|
||||||
|
enum Action5BlockType {
|
||||||
|
A5BLOCK_FIXED, ///< Only allow replacing a whole block of sprites. (TTDP compatible)
|
||||||
|
A5BLOCK_ALLOW_OFFSET, ///< Allow replacing any subset by specifiing an offset.
|
||||||
|
A5BLOCK_INVALID, ///< unknown/not-implemented type
|
||||||
|
};
|
||||||
|
/** Information about a single action 5 type. */
|
||||||
|
struct Action5Type {
|
||||||
|
Action5BlockType block_type; ///< How is this Action5 type processed?
|
||||||
|
SpriteID sprite_base; ///< Load the sprites starting from this sprite.
|
||||||
|
uint16 min_sprites; ///< If the Action5 contains less sprites, the whole block will be ignored.
|
||||||
|
uint16 max_sprites; ///< If the Action5 contains more sprites, only the first max_sprites sprites will be used.
|
||||||
|
const char *name; ///< Name for error messages.
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Action5TypeRemapDefinition {
|
||||||
|
const char *name; // NULL indicates the end of the list
|
||||||
|
const Action5Type info;
|
||||||
|
|
||||||
|
/** Create empty object used to identify the end of a list. */
|
||||||
|
Action5TypeRemapDefinition() :
|
||||||
|
name(NULL),
|
||||||
|
info({ A5BLOCK_INVALID, 0, 0, 0, NULL })
|
||||||
|
{}
|
||||||
|
|
||||||
|
Action5TypeRemapDefinition(const char *type_name, Action5BlockType block_type, SpriteID sprite_base, uint16 min_sprites, uint16 max_sprites, const char *info_name) :
|
||||||
|
name(type_name),
|
||||||
|
info({ block_type, sprite_base, min_sprites, max_sprites, info_name })
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Action5TypeRemapEntry {
|
||||||
|
const Action5Type *info = nullptr;
|
||||||
|
const char *name = nullptr;
|
||||||
|
uint8 type_id = 0;
|
||||||
|
GRFPropertyMapFallbackMode fallback_mode = GPMFM_IGNORE;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Action5TypeRemapSet {
|
||||||
|
std::bitset<256> remapped_ids;
|
||||||
|
std::map<uint8, Action5TypeRemapEntry> mapping;
|
||||||
|
|
||||||
|
Action5TypeRemapEntry &Entry(uint8 property)
|
||||||
|
{
|
||||||
|
this->remapped_ids.set(property);
|
||||||
|
return this->mapping[property];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/** Dynamic data of a loaded NewGRF */
|
/** Dynamic data of a loaded NewGRF */
|
||||||
struct GRFFile : ZeroedMemoryAllocator {
|
struct GRFFile : ZeroedMemoryAllocator {
|
||||||
char *filename;
|
char *filename;
|
||||||
@@ -170,7 +219,8 @@ struct GRFFile : ZeroedMemoryAllocator {
|
|||||||
struct AirportTileSpec **airtspec;
|
struct AirportTileSpec **airtspec;
|
||||||
|
|
||||||
GRFFilePropertyRemapSet action0_property_remaps[GSF_END];
|
GRFFilePropertyRemapSet action0_property_remaps[GSF_END];
|
||||||
AutoFreeSmallVector<const char *, 8> action0_unknown_property_names;
|
Action5TypeRemapSet action5_type_remaps;
|
||||||
|
AutoFreeSmallVector<const char *, 8> remap_unknown_property_names;
|
||||||
|
|
||||||
uint32 param[0x80];
|
uint32 param[0x80];
|
||||||
uint param_end; ///< one more than the highest set parameter
|
uint param_end; ///< one more than the highest set parameter
|
||||||
|
Reference in New Issue
Block a user