Add general NewGRF feature detection scheme to Action 14

pull/73/head
Jonathan G Rennison 6 years ago
parent 5df3a65074
commit 9f2515d793

@ -5840,8 +5840,8 @@ bool GetGlobalVariable(byte param, uint32 *value, const GRFFile *grffile)
*value = 0x3F; // constant fake value to avoid desync
return true;
case 0x1D: // TTD Platform, 00=TTDPatch, 01=OpenTTD
*value = 1;
case 0x1D: // TTD Platform, 00=TTDPatch, 01=OpenTTD, also used for feature tests (bits 31..4)
*value = 1 | grffile->var9D_overlay;
return true;
case 0x1E: // Miscellaneous GRF features
@ -7593,6 +7593,23 @@ struct AllowedSubtags {
static bool SkipUnknownInfo(ByteReader *buf, byte type);
static bool HandleNodes(ByteReader *buf, AllowedSubtags *tags);
/**
* Try to skip the current branch node and all subnodes.
* This is suitable for use with AllowedSubtags.
* @param buf Buffer.
* @return True if we could skip the node, false if an error occurred.
*/
static bool SkipInfoChunk(ByteReader *buf)
{
byte type = buf->ReadByte();
while (type != 0) {
buf->ReadDWord(); // chunk ID
if (!SkipUnknownInfo(buf, type)) return false;
type = buf->ReadByte();
}
return true;
}
/**
* Callback function for 'INFO'->'PARA'->param_num->'VALU' to set the names
* of some parameter values (type uint/enum) or the names of some bits
@ -7688,9 +7705,146 @@ AllowedSubtags _tags_info[] = {
AllowedSubtags()
};
/** Action14 feature definition */
struct GRFFeatureInfo {
const char *name; // NULL indicates the end of the list
uint16 version;
/** Create empty object used to identify the end of a list. */
GRFFeatureInfo() :
name(NULL),
version(0)
{}
GRFFeatureInfo(const char *name, uint16 version) :
name(name),
version(version)
{}
};
/** Action14 feature list */
static const GRFFeatureInfo _grf_feature_list[] = {
GRFFeatureInfo("feature_test", 1),
GRFFeatureInfo(),
};
/** Action14 feature test instance */
struct GRFFeatureTest {
const GRFFeatureInfo *feature;
uint16 min_version;
uint16 max_version;
uint8 platform_var_bit;
void Reset()
{
this->feature = NULL;
this->min_version = 1;
this->max_version = UINT16_MAX;
this->platform_var_bit = 0;
}
void ExecuteTest()
{
uint16 version = (this->feature != NULL) ? this->feature->version : 0;
bool has_feature = (version >= this->min_version && version <= this->max_version);
if (this->platform_var_bit > 0) {
SB(_cur.grffile->var9D_overlay, this->platform_var_bit, 1, has_feature ? 1 : 0);
grfmsg(2, "Action 14 feature test: feature test: setting bit %u of var 0x9D to %u, %u", platform_var_bit, has_feature ? 1 : 0, _cur.grffile->var9D_overlay);
} else {
grfmsg(2, "Action 14 feature test: feature test: doing nothing: %u", has_feature ? 1 : 0);
}
}
};
static GRFFeatureTest _current_grf_feature_test;
/** Callback function for 'FTST'->'NAME' to set the name of the feature being tested. */
static bool ChangeGRFFeatureTestName(byte langid, const char *str)
{
for (const GRFFeatureInfo *info = _grf_feature_list; info->name != NULL; info++) {
if (strcmp(info->name, str) == 0) {
_current_grf_feature_test.feature = info;
grfmsg(2, "Action 14 feature test: found feature named: '%s' (version: %u) in 'FTST'->'NAME'", str, info->version);
return true;
}
}
grfmsg(2, "Action 14 feature test: could not find feature named: '%s' in 'FTST'->'NAME'", str);
_current_grf_feature_test.feature = NULL;
return true;
}
/** Callback function for 'FTST'->'MINV' to set the minimum version of the feature being tested. */
static bool ChangeGRFFeatureMinVersion(size_t len, ByteReader *buf)
{
if (len != 2) {
grfmsg(2, "Action 14 feature test: expected 2 bytes for 'FTST'->'MINV' but got " PRINTF_SIZE ", ignoring this field", len);
buf->Skip(len);
} else {
_current_grf_feature_test.min_version = buf->ReadWord();
}
return true;
}
/** Callback function for 'FTST'->'MAXV' to set the maximum version of the feature being tested. */
static bool ChangeGRFFeatureMaxVersion(size_t len, ByteReader *buf)
{
if (len != 2) {
grfmsg(2, "Action 14 feature test: expected 2 bytes for 'FTST'->'MAXV' but got " PRINTF_SIZE ", ignoring this field", len);
buf->Skip(len);
} else {
_current_grf_feature_test.max_version = buf->ReadWord();
}
return true;
}
/** Callback function for 'FTST'->'SETP' to set the maximum version of the feature being tested. */
static bool ChangeGRFFeatureSetPlatformVarBit(size_t len, ByteReader *buf)
{
if (len != 1) {
grfmsg(2, "Action 14 feature test: expected 1 byte for 'FTST'->'SETP' 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_feature_test.platform_var_bit = bit_number;
} else {
grfmsg(2, "Action 14 feature test: expected a bit number >= 4 and <= 32 for 'FTST'->'SETP' but got %u, ignoring this field", bit_number);
}
}
return true;
}
/** Action14 tags for the FTST node */
AllowedSubtags _tags_ftst[] = {
AllowedSubtags('NAME', ChangeGRFFeatureTestName),
AllowedSubtags('MINV', ChangeGRFFeatureMinVersion),
AllowedSubtags('MAXV', ChangeGRFFeatureMaxVersion),
AllowedSubtags('SETP', ChangeGRFFeatureSetPlatformVarBit),
AllowedSubtags()
};
/**
* Callback function for 'FTST' (feature test)
*/
static bool HandleFeatureTestInfo(ByteReader *buf)
{
_current_grf_feature_test.Reset();
HandleNodes(buf, _tags_ftst);
_current_grf_feature_test.ExecuteTest();
return true;
}
/** Action14 root tags */
AllowedSubtags _tags_root[] = {
AllowedSubtags _tags_root_static[] = {
AllowedSubtags('INFO', _tags_info),
AllowedSubtags('FTST', SkipInfoChunk),
AllowedSubtags()
};
/** Action14 root tags */
AllowedSubtags _tags_root_feature_tests[] = {
AllowedSubtags('INFO', SkipInfoChunk),
AllowedSubtags('FTST', HandleFeatureTestInfo),
AllowedSubtags()
};
@ -7791,13 +7945,23 @@ static bool HandleNodes(ByteReader *buf, AllowedSubtags subtags[])
}
/**
* Handle Action 0x14
* Handle Action 0x14 (static info)
* @param buf Buffer.
*/
static void StaticGRFInfo(ByteReader *buf)
{
/* <14> <type> <id> <text/data...> */
HandleNodes(buf, _tags_root);
HandleNodes(buf, _tags_root_static);
}
/**
* Handle Action 0x14 (feature tests)
* @param buf Buffer.
*/
static void Act14FeatureTest(ByteReader *buf)
{
/* <14> <type> <id> <text/data...> */
HandleNodes(buf, _tags_root_feature_tests);
}
/**
@ -8784,7 +8948,7 @@ static void DecodeSpecialSprite(byte *buf, uint num, GrfLoadingStage stage)
/* 0x11 */ { SkipAct11,GRFUnsafe, SkipAct11, GRFSound, SkipAct11, GRFSound, },
/* 0x12 */ { SkipAct12, SkipAct12, SkipAct12, SkipAct12, SkipAct12, LoadFontGlyph, },
/* 0x13 */ { NULL, NULL, NULL, NULL, NULL, TranslateGRFStrings, },
/* 0x14 */ { StaticGRFInfo, NULL, NULL, NULL, NULL, NULL, },
/* 0x14 */ { StaticGRFInfo, NULL, NULL, Act14FeatureTest, NULL, NULL, },
};
GRFLocation location(_cur.grfconfig->ident.grfid, _cur.nfo_line);

@ -138,6 +138,8 @@ 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 var9D_overlay; ///< Overlay for global variable 9D (action 0x14)
GRFFile(const struct GRFConfig *config);
~GRFFile();

Loading…
Cancel
Save