Change: Disallow using Action A to load sprites above the baseset unless reserved. (#12435)

Using Action A above the baseset is error prone as the sprites are not fixed and can be moved around.

Any NewGRF doing so is likely to break in the future, so force it to break instead.
This commit is contained in:
Peter Nelson 2024-04-20 10:23:36 +01:00 committed by GitHub
parent e028c15555
commit 1c31e4b68c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 30 additions and 3 deletions

View File

@ -370,7 +370,7 @@ struct GRFLocation {
}
};
static std::map<GRFLocation, SpriteID> _grm_sprites;
static std::map<GRFLocation, std::pair<SpriteID, uint16_t>> _grm_sprites;
typedef std::map<GRFLocation, std::vector<uint8_t>> GRFLineToSpriteOverride;
static GRFLineToSpriteOverride _grf_line_to_action6_sprite_override;
@ -7072,6 +7072,21 @@ static void GRFInfo(ByteReader *buf)
Debug(grf, 1, "GRFInfo: Loaded GRFv{} set {:08X} - {} (palette: {}, version: {})", version, BSWAP32(grfid), name, (_cur.grfconfig->palette & GRFP_USE_MASK) ? "Windows" : "DOS", _cur.grfconfig->version);
}
/**
* Check if a sprite ID range is within the GRM reversed range for the currently loading NewGRF.
* @param first_sprite First sprite of range.
* @param num_sprites Number of sprites in the range.
* @return True iff the NewGRF has reserved a range equal to or greater than the provided range.
*/
static bool IsGRMReservedSprite(SpriteID first_sprite, uint16_t num_sprites)
{
for (const auto &grm_sprite : _grm_sprites) {
if (grm_sprite.first.grfid != _cur.grffile->grfid) continue;
if (grm_sprite.second.first <= first_sprite && grm_sprite.second.first + grm_sprite.second.second >= first_sprite + num_sprites) return true;
}
return false;
}
/* Action 0x0A */
static void SpriteReplace(ByteReader *buf)
{
@ -7093,6 +7108,18 @@ static void SpriteReplace(ByteReader *buf)
i, num_sprites, first_sprite
);
if (first_sprite + num_sprites >= SPR_OPENTTD_BASE) {
/* Outside allowed range, check for GRM sprite reservations. */
if (!IsGRMReservedSprite(first_sprite, num_sprites)) {
GrfMsg(0, "SpriteReplace: [Set {}] Changing {} sprites, beginning with {}, above limit of {} and not within reserved range, ignoring.",
i, num_sprites, first_sprite, SPR_OPENTTD_BASE);
/* Load the sprites at the current location so they will do nothing instead of appearing to work. */
first_sprite = _cur.spriteid;
_cur.spriteid += num_sprites;
}
}
for (uint j = 0; j < num_sprites; j++) {
int load_index = first_sprite + j;
_cur.nfo_line++;
@ -7460,7 +7487,7 @@ static void ParamSet(ByteReader *buf)
/* Reserve space at the current sprite ID */
GrfMsg(4, "ParamSet: GRM: Allocated {} sprites at {}", count, _cur.spriteid);
_grm_sprites[GRFLocation(_cur.grffile->grfid, _cur.nfo_line)] = _cur.spriteid;
_grm_sprites[GRFLocation(_cur.grffile->grfid, _cur.nfo_line)] = std::make_pair(_cur.spriteid, count);
_cur.spriteid += count;
}
}
@ -7494,7 +7521,7 @@ static void ParamSet(ByteReader *buf)
switch (op) {
case 0:
/* Return space reserved during reservation stage */
src1 = _grm_sprites[GRFLocation(_cur.grffile->grfid, _cur.nfo_line)];
src1 = _grm_sprites[GRFLocation(_cur.grffile->grfid, _cur.nfo_line)].first;
GrfMsg(4, "ParamSet: GRM: Using pre-allocated sprites at {}", src1);
break;