Initial implementation of NewGRF feature ID mapping

This commit is contained in:
Jonathan G Rennison
2022-01-27 23:41:32 +00:00
parent ef3916928c
commit eeef6c485a
5 changed files with 219 additions and 3 deletions

View File

@@ -4224,6 +4224,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_FEATURE_ID :Unimplemented remapped feature ID: 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})

View File

@@ -4932,6 +4932,24 @@ static bool HandleChangeInfoResult(const char *caller, ChangeInfoResult cir, Grf
static GrfSpecFeatureRef ReadFeature(uint8 raw_byte, bool allow_48 = false)
{
if (unlikely(HasBit(_cur.grffile->ctrl_flags, GFCF_HAVE_FEATURE_ID_REMAP))) {
const GRFFeatureMapRemapSet &remap = _cur.grffile->feature_id_remaps;
if (remap.remapped_ids[raw_byte]) {
auto iter = remap.mapping.find(raw_byte);
const GRFFeatureMapRemapEntry &def = iter->second;
if (def.feature == GSF_ERROR_ON_USE) {
grfmsg(0, "Error: Unimplemented mapped feature: %s, mapped to: %02X", def.name, raw_byte);
GRFError *error = DisableGrf(STR_NEWGRF_ERROR_UNIMPLEMETED_MAPPED_FEATURE_ID);
error->data = stredup(def.name);
error->param_value[1] = GSF_INVALID;
error->param_value[2] = raw_byte;
} else if (def.feature == GSF_INVALID) {
grfmsg(2, "Ignoring unimplemented mapped feature: %s, mapped to: %02X", def.name, raw_byte);
}
return { def.feature, raw_byte };
}
}
GrfSpecFeature feature;
if (raw_byte >= GSF_REAL_FEATURE_END && !(allow_48 && raw_byte == 0x48)) {
feature = GSF_INVALID;
@@ -4971,6 +4989,15 @@ const char *GetFeatureString(GrfSpecFeatureRef feature)
if (feature.id < GSF_END) {
seprintf(buffer, lastof(buffer), "0x%02X (%s)", feature.raw_byte, _feature_names[feature.id]);
} else {
if (unlikely(HasBit(_cur.grffile->ctrl_flags, GFCF_HAVE_FEATURE_ID_REMAP))) {
const GRFFeatureMapRemapSet &remap = _cur.grffile->feature_id_remaps;
if (remap.remapped_ids[feature.raw_byte]) {
auto iter = remap.mapping.find(feature.raw_byte);
const GRFFeatureMapRemapEntry &def = iter->second;
seprintf(buffer, lastof(buffer), "0x%02X (%s)", feature.raw_byte, def.name);
return buffer;
}
}
seprintf(buffer, lastof(buffer), "0x%02X", feature.raw_byte);
}
return buffer;
@@ -4978,7 +5005,16 @@ const char *GetFeatureString(GrfSpecFeatureRef feature)
const char *GetFeatureString(GrfSpecFeature feature)
{
return GetFeatureString(GrfSpecFeatureRef{ feature, feature });
uint8 raw_byte = feature;
if (feature >= GSF_REAL_FEATURE_END) {
for (const auto &entry : _cur.grffile->feature_id_remaps.mapping) {
if (entry.second.feature == feature) {
raw_byte = entry.second.raw_id;
break;
}
}
}
return GetFeatureString(GrfSpecFeatureRef{ feature, raw_byte });
}
struct GRFFilePropertyDescriptor {
@@ -5059,7 +5095,7 @@ static void FeatureChangeInfo(ByteReader *buf)
uint engine = buf->ReadExtendedByte();
if (feature >= GSF_END) {
grfmsg(1, "FeatureChangeInfo: Unsupported feature %s skipping", GetFeatureString(feature));
grfmsg(1, "FeatureChangeInfo: Unsupported feature %s skipping", GetFeatureString(feature_ref));
return;
}
@@ -5194,7 +5230,7 @@ static void NewSpriteSet(ByteReader *buf)
if (feature >= GSF_END) {
_cur.skip_sprites = num_sets * num_ents;
grfmsg(1, "NewSpriteSet: Unsupported feature %s, skipping %d sprites", GetFeatureString(feature), _cur.skip_sprites);
grfmsg(1, "NewSpriteSet: Unsupported feature %s, skipping %d sprites", GetFeatureString(feature_ref), _cur.skip_sprites);
return;
}
@@ -8758,6 +8794,53 @@ struct GRFPropertyMapAction {
this->output_mask = 0;
}
void ExecuteFeatureIDRemapping()
{
if (this->prop_id < 0) {
grfmsg(2, "Action 14 %s remapping: no feature 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;
}
SetBit(_cur.grffile->ctrl_flags, GFCF_HAVE_FEATURE_ID_REMAP);
bool success = false;
const char *str = this->name.c_str();
extern const GRFFeatureMapDefinition _grf_remappable_features[];
for (const GRFFeatureMapDefinition *info = _grf_remappable_features; info->name != nullptr; info++) {
if (strcmp(info->name, str) == 0) {
GRFFeatureMapRemapEntry &entry = _cur.grffile->feature_id_remaps.Entry(this->prop_id);
entry.name = info->name;
entry.feature = info->feature;
entry.raw_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: 0x%02X", this->descriptor, str, this->prop_id);
GRFError *error = DisableGrf(STR_NEWGRF_ERROR_UNIMPLEMETED_MAPPED_FEATURE_ID);
error->data = stredup(str);
error->param_value[1] = GSF_INVALID;
error->param_value[2] = 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.emplace_back(str_store);
GRFFeatureMapRemapEntry &entry = _cur.grffile->feature_id_remaps.Entry(this->prop_id);
entry.name = str_store;
entry.feature = (this->fallback_mode == GPMFM_IGNORE) ? GSF_INVALID : GSF_ERROR_ON_USE;
entry.raw_id = this->prop_id;
}
}
}
void ExecutePropertyRemapping()
{
if (this->feature == GSF_INVALID) {
@@ -8925,6 +9008,19 @@ static bool ChangePropertyRemapPropertyId(size_t len, ByteReader *buf)
return true;
}
/** Callback function for ->'FTID' to set the feature ID to which this feature is being mapped. */
static bool ChangePropertyRemapFeatureId(size_t len, ByteReader *buf)
{
GRFPropertyMapAction &action = _current_grf_property_map_action;
if (len != 1) {
grfmsg(2, "Action 14 %s mapping: expected 1 byte for '%s'->'FTID' but got " PRINTF_SIZE ", ignoring this field", action.descriptor, action.tag_name, len);
buf->Skip(len);
} else {
action.prop_id = buf->ReadByte();
}
return true;
}
/** Callback function for ->'TYPE' to set the property ID to which this item is being mapped. */
static bool ChangePropertyRemapTypeId(size_t len, ByteReader *buf)
{
@@ -9050,6 +9146,26 @@ static bool ChangePropertyRemapSetOutputParam(size_t len, ByteReader *buf)
return true;
}
/** Action14 tags for the FIDM node */
AllowedSubtags _tags_fidm[] = {
AllowedSubtags('NAME', ChangePropertyRemapName),
AllowedSubtags('FTID', ChangePropertyRemapFeatureId),
AllowedSubtags('FLBK', ChangePropertyRemapSetFallbackMode),
AllowedSubtags('SETT', ChangePropertyRemapSetTTDVerVarBit),
AllowedSubtags()
};
/**
* Callback function for 'FIDM' (feature ID mapping)
*/
static bool HandleFeatureIDMap(ByteReader *buf)
{
_current_grf_property_map_action.Reset("FIDM", "feature");
HandleNodes(buf, _tags_fidm);
_current_grf_property_map_action.ExecuteFeatureIDRemapping();
return true;
}
/** Action14 tags for the A0PM node */
AllowedSubtags _tags_a0pm[] = {
AllowedSubtags('NAME', ChangePropertyRemapName),
@@ -9119,6 +9235,7 @@ static bool HandleAction5TypeMap(ByteReader *buf)
AllowedSubtags _tags_root_static[] = {
AllowedSubtags('INFO', _tags_info),
AllowedSubtags('FTST', SkipInfoChunk),
AllowedSubtags('FIDM', SkipInfoChunk),
AllowedSubtags('A0PM', SkipInfoChunk),
AllowedSubtags('A2VM', SkipInfoChunk),
AllowedSubtags('A5TM', SkipInfoChunk),
@@ -9129,6 +9246,7 @@ AllowedSubtags _tags_root_static[] = {
AllowedSubtags _tags_root_feature_tests[] = {
AllowedSubtags('INFO', SkipInfoChunk),
AllowedSubtags('FTST', HandleFeatureTestInfo),
AllowedSubtags('FIDM', HandleFeatureIDMap),
AllowedSubtags('A0PM', HandleAction0PropertyMap),
AllowedSubtags('A2VM', HandleAction2VariableMap),
AllowedSubtags('A5TM', HandleAction5TypeMap),

View File

@@ -95,6 +95,7 @@ enum GrfSpecFeature {
GSF_FAKE_STATION_STRUCT, ///< Fake station struct GrfSpecFeature for NewGRF debugging
GSF_FAKE_END, ///< End of the fake features
GSF_ERROR_ON_USE = 0xFE, ///< An invalid value which generates an immediate error on mapping
GSF_INVALID = 0xFF, ///< An invalid spec feature
};
@@ -114,6 +115,39 @@ enum GRFPropertyMapFallbackMode {
GPMFM_END,
};
struct GRFFeatureMapDefinition {
const char *name; // nullptr indicates the end of the list
GrfSpecFeature feature;
/** Create empty object used to identify the end of a list. */
GRFFeatureMapDefinition() :
name(nullptr),
feature((GrfSpecFeature)0)
{}
GRFFeatureMapDefinition(GrfSpecFeature feature, const char *name) :
name(name),
feature(feature)
{}
};
struct GRFFeatureMapRemapEntry {
const char *name = nullptr;
GrfSpecFeature feature = (GrfSpecFeature)0;
uint8 raw_id = 0;
};
struct GRFFeatureMapRemapSet {
std::bitset<256> remapped_ids;
btree::btree_map<uint8, GRFFeatureMapRemapEntry> mapping;
GRFFeatureMapRemapEntry &Entry(uint8 raw_id)
{
this->remapped_ids.set(raw_id);
return this->mapping[raw_id];
}
};
struct GRFPropertyMapDefinition {
const char *name; // nullptr indicates the end of the list
int id;
@@ -246,6 +280,11 @@ enum NewSignalAction3ID {
NSA3ID_CUSTOM_SIGNALS = 0, ///< Action 3 ID for custom signal sprites
};
/** GRFFile control flags. */
enum GRFFileCtrlFlags {
GFCF_HAVE_FEATURE_ID_REMAP = 0, ///< This GRF has one or more feature ID mappings
};
/** Dynamic data of a loaded NewGRF */
struct GRFFile : ZeroedMemoryAllocator {
char *filename;
@@ -263,6 +302,7 @@ struct GRFFile : ZeroedMemoryAllocator {
struct AirportSpec **airportspec;
struct AirportTileSpec **airtspec;
GRFFeatureMapRemapSet feature_id_remaps;
GRFFilePropertyRemapSet action0_property_remaps[GSF_END];
Action5TypeRemapSet action5_type_remaps;
std::vector<GRFVariableMapEntry> grf_variable_remaps;
@@ -302,6 +342,8 @@ struct GRFFile : ZeroedMemoryAllocator {
byte new_signal_ctrl_flags; ///< Ctrl flags for new signals
byte new_signal_extra_aspects; ///< Number of extra aspects for new signals
byte ctrl_flags; ///< General GRF control flags
GRFFile(const struct GRFConfig *config);
~GRFFile();

View File

@@ -19,6 +19,7 @@ extern const GRFFeatureInfo _grf_feature_list[] = {
GRFFeatureInfo("feature_test", 1),
GRFFeatureInfo("property_mapping", 1),
GRFFeatureInfo("variable_mapping", 1),
GRFFeatureInfo("feature_id_mapping", 1),
GRFFeatureInfo("action5_type_id_mapping", 1),
GRFFeatureInfo("action0_station_prop1B", 1),
GRFFeatureInfo("action0_station_disallowed_bridge_pillars", 1),
@@ -51,6 +52,12 @@ extern const GRFFeatureInfo _grf_feature_list[] = {
GRFFeatureInfo(),
};
/** Action14 remappable feature list */
extern const GRFFeatureMapDefinition _grf_remappable_features[] = {
GRFFeatureMapDefinition(),
};
/** Action14 Action0 remappable property list */
extern const GRFPropertyMapDefinition _grf_action0_remappable_properties[] = {
GRFPropertyMapDefinition(GSF_STATIONS, A0RPI_STATION_MIN_BRIDGE_HEIGHT, "station_min_bridge_height"),