GRF: Add property mapping ID extension mechanism

Bump property_mapping feature version to 3
This commit is contained in:
Jonathan G Rennison
2023-02-19 23:13:23 +00:00
parent 5a877b49b4
commit 2ffb245b33
4 changed files with 101 additions and 20 deletions

View File

@@ -206,6 +206,11 @@ public:
* as there may not be any more data read. */ * as there may not be any more data read. */
if (data > end) throw OTTDByteReaderSignal(); if (data > end) throw OTTDByteReaderSignal();
} }
inline void ResetReadPosition(byte *pos)
{
data = pos;
}
}; };
typedef void (*SpecialSpriteHandler)(ByteReader *buf); typedef void (*SpecialSpriteHandler)(ByteReader *buf);
@@ -927,8 +932,10 @@ static bool MappedPropertyLengthMismatch(ByteReader *buf, uint expected_size, co
uint length = buf->ReadExtendedByte(); uint length = buf->ReadExtendedByte();
if (length != expected_size) { if (length != expected_size) {
if (mapping_entry != nullptr) { if (mapping_entry != nullptr) {
grfmsg(2, "Ignoring use of mapped property: %s, feature: %s, mapped to: %X, with incorrect data size: %u instead of %u", grfmsg(2, "Ignoring use of mapped property: %s, feature: %s, mapped to: %X%s, with incorrect data size: %u instead of %u",
mapping_entry->name, GetFeatureString(mapping_entry->feature), mapping_entry->property_id, length, expected_size); mapping_entry->name, GetFeatureString(mapping_entry->feature),
mapping_entry->property_id, mapping_entry->extended ? " (extended)" : "",
length, expected_size);
} }
buf->Skip(length); buf->Skip(length);
return true; return true;
@@ -5400,6 +5407,38 @@ static GRFFilePropertyDescriptor ReadAction0PropertyID(ByteReader *buf, uint8 fe
error->param_value[2] = raw_prop; error->param_value[2] = raw_prop;
} else if (prop == A0RPI_UNKNOWN_IGNORE) { } else if (prop == A0RPI_UNKNOWN_IGNORE) {
grfmsg(2, "Ignoring unimplemented mapped property: %s, feature: %s, mapped to: %X", def.name, GetFeatureString(def.feature), raw_prop); grfmsg(2, "Ignoring unimplemented mapped property: %s, feature: %s, mapped to: %X", def.name, GetFeatureString(def.feature), raw_prop);
} else if (prop == A0RPI_ID_EXTENSION) {
byte *outer_data = buf->Data();
size_t outer_length = buf->ReadExtendedByte();
uint16 mapped_id = buf->ReadWord();
byte *inner_data = buf->Data();
size_t inner_length = buf->ReadExtendedByte();
if (inner_length + (inner_data - outer_data) != outer_length) {
grfmsg(2, "Ignoring extended ID property with malformed lengths: %s, feature: %s, mapped to: %X", def.name, GetFeatureString(def.feature), raw_prop);
buf->ResetReadPosition(outer_data);
return GRFFilePropertyDescriptor(A0RPI_UNKNOWN_IGNORE, &def);
}
auto ext = _cur.grffile->action0_extended_property_remaps.find((((uint32)feature) << 16) | mapped_id);
if (ext != _cur.grffile->action0_extended_property_remaps.end()) {
buf->ResetReadPosition(inner_data);
const GRFFilePropertyRemapEntry &ext_def = ext->second;
prop = ext_def.id;
if (prop == A0RPI_UNKNOWN_ERROR) {
grfmsg(0, "Error: Unimplemented mapped extended ID property: %s, feature: %s, mapped to: %X (via %X)", ext_def.name, GetFeatureString(ext_def.feature), mapped_id, raw_prop);
GRFError *error = DisableGrf(STR_NEWGRF_ERROR_UNIMPLEMETED_MAPPED_PROPERTY);
error->data = stredup(ext_def.name);
error->param_value[1] = ext_def.feature;
error->param_value[2] = 0xE0000 | mapped_id;
} else if (prop == A0RPI_UNKNOWN_IGNORE) {
grfmsg(2, "Ignoring unimplemented mapped extended ID property: %s, feature: %s, mapped to: %X (via %X)", ext_def.name, GetFeatureString(ext_def.feature), mapped_id, raw_prop);
}
return GRFFilePropertyDescriptor(prop, &ext_def);
} else {
grfmsg(2, "Ignoring unknown extended ID property: %s, feature: %s, mapped to: %X (via %X)", def.name, GetFeatureString(def.feature), mapped_id, raw_prop);
buf->ResetReadPosition(outer_data);
return GRFFilePropertyDescriptor(A0RPI_UNKNOWN_IGNORE, &def);
}
} }
return GRFFilePropertyDescriptor(prop, &def); return GRFFilePropertyDescriptor(prop, &def);
} else { } else {
@@ -9362,6 +9401,7 @@ struct GRFPropertyMapAction {
GrfSpecFeature feature; GrfSpecFeature feature;
int prop_id; int prop_id;
int ext_prop_id;
std::string name; std::string name;
GRFPropertyMapFallbackMode fallback_mode; GRFPropertyMapFallbackMode fallback_mode;
uint8 ttd_ver_var_bit; uint8 ttd_ver_var_bit;
@@ -9379,6 +9419,7 @@ struct GRFPropertyMapAction {
this->feature = GSF_INVALID; this->feature = GSF_INVALID;
this->prop_id = -1; this->prop_id = -1;
this->ext_prop_id = -1;
this->name.clear(); this->name.clear();
this->fallback_mode = GPMFM_IGNORE; this->fallback_mode = GPMFM_IGNORE;
this->ttd_ver_var_bit = 0; this->ttd_ver_var_bit = 0;
@@ -9446,7 +9487,7 @@ struct GRFPropertyMapAction {
grfmsg(2, "Action 14 %s remapping: no feature defined, doing nothing", this->descriptor); grfmsg(2, "Action 14 %s remapping: no feature defined, doing nothing", this->descriptor);
return; return;
} }
if (this->prop_id < 0) { if (this->prop_id < 0 && this->ext_prop_id < 0) {
grfmsg(2, "Action 14 %s remapping: no property ID defined, doing nothing", this->descriptor); grfmsg(2, "Action 14 %s remapping: no property ID defined, doing nothing", this->descriptor);
return; return;
} }
@@ -9458,12 +9499,22 @@ struct GRFPropertyMapAction {
const char *str = this->name.c_str(); const char *str = this->name.c_str();
extern const GRFPropertyMapDefinition _grf_action0_remappable_properties[]; extern const GRFPropertyMapDefinition _grf_action0_remappable_properties[];
for (const GRFPropertyMapDefinition *info = _grf_action0_remappable_properties; info->name != nullptr; info++) { for (const GRFPropertyMapDefinition *info = _grf_action0_remappable_properties; info->name != nullptr; info++) {
if (info->feature == this->feature && strcmp(info->name, str) == 0) { if ((info->feature == GSF_INVALID || info->feature == this->feature) && strcmp(info->name, str) == 0) {
GRFFilePropertyRemapEntry &entry = _cur.grffile->action0_property_remaps[this->feature].Entry(this->prop_id); if (this->prop_id > 0) {
entry.name = info->name; GRFFilePropertyRemapEntry &entry = _cur.grffile->action0_property_remaps[this->feature].Entry(this->prop_id);
entry.id = info->id; entry.name = info->name;
entry.feature = this->feature; entry.id = info->id;
entry.property_id = this->prop_id; entry.feature = this->feature;
entry.property_id = this->prop_id;
}
if (this->ext_prop_id > 0) {
GRFFilePropertyRemapEntry &entry = _cur.grffile->action0_extended_property_remaps[(((uint32)this->feature) << 16) | this->ext_prop_id];
entry.name = info->name;
entry.id = info->id;
entry.feature = this->feature;
entry.extended = true;
entry.property_id = this->ext_prop_id;
}
success = true; success = true;
break; break;
} }
@@ -9475,22 +9526,34 @@ struct GRFPropertyMapAction {
include(_cur.grffile->var91_values, this->test_91_value); include(_cur.grffile->var91_values, this->test_91_value);
} }
if (!success) { if (!success) {
uint mapped_to = (this->prop_id > 0) ? this->prop_id : this->ext_prop_id;
const char *extended = (this->prop_id > 0) ? "" : " (extended)";
if (this->fallback_mode == GPMFM_ERROR_ON_DEFINITION) { if (this->fallback_mode == GPMFM_ERROR_ON_DEFINITION) {
grfmsg(0, "Error: Unimplemented mapped %s: %s, feature: %s, mapped to: %X", this->descriptor, str, GetFeatureString(this->feature), this->prop_id); grfmsg(0, "Error: Unimplemented mapped %s: %s, feature: %s, mapped to: %X%s", this->descriptor, str, GetFeatureString(this->feature), mapped_to, extended);
GRFError *error = DisableGrf(STR_NEWGRF_ERROR_UNIMPLEMETED_MAPPED_PROPERTY); GRFError *error = DisableGrf(STR_NEWGRF_ERROR_UNIMPLEMETED_MAPPED_PROPERTY);
error->data = stredup(str); error->data = stredup(str);
error->param_value[1] = this->feature; error->param_value[1] = this->feature;
error->param_value[2] = this->prop_id; error->param_value[2] = ((this->prop_id > 0) ? 0 : 0xE0000) | mapped_to;
} else { } else {
const char *str_store = stredup(str); const char *str_store = stredup(str);
grfmsg(2, "Unimplemented mapped %s: %s, feature: %s, mapped to: %X, %s on use", grfmsg(2, "Unimplemented mapped %s: %s, feature: %s, mapped to: %X%s, %s on use",
this->descriptor, str, GetFeatureString(this->feature), this->prop_id, (this->fallback_mode == GPMFM_IGNORE) ? "ignoring" : "error"); this->descriptor, str, GetFeatureString(this->feature), mapped_to, extended, (this->fallback_mode == GPMFM_IGNORE) ? "ignoring" : "error");
_cur.grffile->remap_unknown_property_names.emplace_back(str_store); _cur.grffile->remap_unknown_property_names.emplace_back(str_store);
GRFFilePropertyRemapEntry &entry = _cur.grffile->action0_property_remaps[this->feature].Entry(this->prop_id); if (this->prop_id > 0) {
entry.name = str_store; GRFFilePropertyRemapEntry &entry = _cur.grffile->action0_property_remaps[this->feature].Entry(this->prop_id);
entry.id = (this->fallback_mode == GPMFM_IGNORE) ? A0RPI_UNKNOWN_IGNORE : A0RPI_UNKNOWN_ERROR; entry.name = str_store;
entry.feature = this->feature; entry.id = (this->fallback_mode == GPMFM_IGNORE) ? A0RPI_UNKNOWN_IGNORE : A0RPI_UNKNOWN_ERROR;
entry.property_id = this->prop_id; entry.feature = this->feature;
entry.property_id = this->prop_id;
}
if (this->ext_prop_id > 0) {
GRFFilePropertyRemapEntry &entry = _cur.grffile->action0_extended_property_remaps[(((uint32)this->feature) << 16) | this->ext_prop_id];
entry.name = str_store;
entry.id = (this->fallback_mode == GPMFM_IGNORE) ? A0RPI_UNKNOWN_IGNORE : A0RPI_UNKNOWN_ERROR;;
entry.feature = this->feature;
entry.extended = true;
entry.property_id = this->ext_prop_id;
}
} }
} }
} }
@@ -9616,6 +9679,19 @@ static bool ChangePropertyRemapPropertyId(size_t len, ByteReader *buf)
return true; return true;
} }
/** Callback function for ->'XPRP' to set the extended property ID to which this item is being mapped. */
static bool ChangePropertyRemapExtendedPropertyId(size_t len, ByteReader *buf)
{
GRFPropertyMapAction &action = _current_grf_property_map_action;
if (len != 2) {
grfmsg(2, "Action 14 %s mapping: expected 2 bytes for '%s'->'XPRP' but got " PRINTF_SIZE ", ignoring this field", action.descriptor, action.tag_name, len);
buf->Skip(len);
} else {
action.ext_prop_id = buf->ReadWord();
}
return true;
}
/** Callback function for ->'FTID' to set the feature ID to which this feature is being mapped. */ /** Callback function for ->'FTID' to set the feature ID to which this feature is being mapped. */
static bool ChangePropertyRemapFeatureId(size_t len, ByteReader *buf) static bool ChangePropertyRemapFeatureId(size_t len, ByteReader *buf)
{ {
@@ -9792,6 +9868,7 @@ AllowedSubtags _tags_a0pm[] = {
AllowedSubtags('NAME', ChangePropertyRemapName), AllowedSubtags('NAME', ChangePropertyRemapName),
AllowedSubtags('FEAT', ChangePropertyRemapFeature), AllowedSubtags('FEAT', ChangePropertyRemapFeature),
AllowedSubtags('PROP', ChangePropertyRemapPropertyId), AllowedSubtags('PROP', ChangePropertyRemapPropertyId),
AllowedSubtags('XPRP', ChangePropertyRemapExtendedPropertyId),
AllowedSubtags('FLBK', ChangePropertyRemapSetFallbackMode), AllowedSubtags('FLBK', ChangePropertyRemapSetFallbackMode),
AllowedSubtags('SETT', ChangePropertyRemapSetTTDVerVarBit), AllowedSubtags('SETT', ChangePropertyRemapSetTTDVerVarBit),
AllowedSubtags('SVAL', ChangePropertyRemapSuccessResultValue), AllowedSubtags('SVAL', ChangePropertyRemapSuccessResultValue),

View File

@@ -175,7 +175,8 @@ struct GRFFilePropertyRemapEntry {
const char *name = nullptr; const char *name = nullptr;
int id = 0; int id = 0;
GrfSpecFeature feature = (GrfSpecFeature)0; GrfSpecFeature feature = (GrfSpecFeature)0;
uint8 property_id = 0; bool extended = false;
uint16 property_id = 0;
}; };
struct GRFFilePropertyRemapSet { struct GRFFilePropertyRemapSet {
@@ -324,6 +325,7 @@ struct GRFFile : ZeroedMemoryAllocator {
GRFFeatureMapRemapSet feature_id_remaps; GRFFeatureMapRemapSet feature_id_remaps;
GRFFilePropertyRemapSet action0_property_remaps[GSF_END]; GRFFilePropertyRemapSet action0_property_remaps[GSF_END];
btree::btree_map<uint32, GRFFilePropertyRemapEntry> action0_extended_property_remaps;
Action5TypeRemapSet action5_type_remaps; Action5TypeRemapSet action5_type_remaps;
std::vector<GRFVariableMapEntry> grf_variable_remaps; std::vector<GRFVariableMapEntry> grf_variable_remaps;
std::vector<std::unique_ptr<const char, FreeDeleter>> remap_unknown_property_names; std::vector<std::unique_ptr<const char, FreeDeleter>> remap_unknown_property_names;

View File

@@ -17,7 +17,7 @@
/** Action14 feature list */ /** Action14 feature list */
extern const GRFFeatureInfo _grf_feature_list[] = { extern const GRFFeatureInfo _grf_feature_list[] = {
GRFFeatureInfo("feature_test", 2), GRFFeatureInfo("feature_test", 2),
GRFFeatureInfo("property_mapping", 2), GRFFeatureInfo("property_mapping", 3),
GRFFeatureInfo("variable_mapping", 3), GRFFeatureInfo("variable_mapping", 3),
GRFFeatureInfo("feature_id_mapping", 2), GRFFeatureInfo("feature_id_mapping", 2),
GRFFeatureInfo("action5_type_id_mapping", 2), GRFFeatureInfo("action5_type_id_mapping", 2),
@@ -74,6 +74,7 @@ extern const GRFFeatureMapDefinition _grf_remappable_features[] = {
/** Action14 Action0 remappable property list */ /** Action14 Action0 remappable property list */
extern const GRFPropertyMapDefinition _grf_action0_remappable_properties[] = { extern const GRFPropertyMapDefinition _grf_action0_remappable_properties[] = {
GRFPropertyMapDefinition(GSF_INVALID, A0RPI_ID_EXTENSION, "id_extension"),
GRFPropertyMapDefinition(GSF_STATIONS, A0RPI_STATION_MIN_BRIDGE_HEIGHT, "station_min_bridge_height"), GRFPropertyMapDefinition(GSF_STATIONS, A0RPI_STATION_MIN_BRIDGE_HEIGHT, "station_min_bridge_height"),
GRFPropertyMapDefinition(GSF_STATIONS, A0RPI_STATION_DISALLOWED_BRIDGE_PILLARS, "station_disallowed_bridge_pillars"), GRFPropertyMapDefinition(GSF_STATIONS, A0RPI_STATION_DISALLOWED_BRIDGE_PILLARS, "station_disallowed_bridge_pillars"),
GRFPropertyMapDefinition(GSF_BRIDGES, A0RPI_BRIDGE_MENU_ICON, "bridge_menu_icon"), GRFPropertyMapDefinition(GSF_BRIDGES, A0RPI_BRIDGE_MENU_ICON, "bridge_menu_icon"),

View File

@@ -13,6 +13,7 @@
enum Action0RemapPropertyIds { enum Action0RemapPropertyIds {
A0RPI_UNKNOWN_IGNORE = 0x200, A0RPI_UNKNOWN_IGNORE = 0x200,
A0RPI_UNKNOWN_ERROR, A0RPI_UNKNOWN_ERROR,
A0RPI_ID_EXTENSION,
A0RPI_STATION_MIN_BRIDGE_HEIGHT, A0RPI_STATION_MIN_BRIDGE_HEIGHT,
A0RPI_STATION_DISALLOWED_BRIDGE_PILLARS, A0RPI_STATION_DISALLOWED_BRIDGE_PILLARS,