diff --git a/src/newgrf.cpp b/src/newgrf.cpp index 662fc3e38b..77c46b61b0 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -370,7 +370,7 @@ struct GRFLocation { } }; -static std::map _grm_sprites; +static std::map> _grm_sprites; typedef std::map> 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;