Initial implementation of NewGRF feature ID mapping
This commit is contained in:
@@ -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})
|
||||
|
124
src/newgrf.cpp
124
src/newgrf.cpp
@@ -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),
|
||||
|
42
src/newgrf.h
42
src/newgrf.h
@@ -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();
|
||||
|
||||
|
@@ -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"),
|
||||
|
Reference in New Issue
Block a user