summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpeter1138 <peter1138@openttd.org>2010-01-22 13:02:15 +0000
committerpeter1138 <peter1138@openttd.org>2010-01-22 13:02:15 +0000
commit927f149ff2caa2203398ff1bee3c892477fe54e7 (patch)
treee4b55a5b35acf08c92a7a1a5bf4273561c872bd7
parent7ec3d9a0c66f82822fad924de7ab72a02e0521eb (diff)
downloadopenttd-927f149ff2caa2203398ff1bee3c892477fe54e7.tar.xz
(svn r18884) -Fix: [NewGRF] Perform bounds checking for all NewGRF data reads. Explicit length checks (which were not always correct) are no longer needed so these are removed to simplify code.
-rw-r--r--src/lang/english.txt1
-rw-r--r--src/newgrf.cpp624
2 files changed, 272 insertions, 353 deletions
diff --git a/src/lang/english.txt b/src/lang/english.txt
index adb4f3008..1e1ad8021 100644
--- a/src/lang/english.txt
+++ b/src/lang/english.txt
@@ -2370,6 +2370,7 @@ STR_NEWGRF_ERROR_UNKNOWN_PROPERTY :Unknown Action
STR_NEWGRF_ERROR_INVALID_ID :Attempt to use invalid ID.
STR_NEWGRF_ERROR_CORRUPT_SPRITE :{YELLOW}{RAW_STRING} contains a corrupt sprite. All corrupt sprites will be shown as a red question mark (?).
STR_NEWGRF_ERROR_MULTIPLE_ACTION_8 :Contains multiple Action 8 entries.
+STR_NEWGRF_ERROR_READ_BOUNDS :Read past end of pseudo-sprite.
# NewGRF related 'general' warnings
STR_NEWGRF_POPUP_CAUTION_CAPTION :{WHITE}Caution!
diff --git a/src/newgrf.cpp b/src/newgrf.cpp
index 83e4d3f6c..bbfbc6091 100644
--- a/src/newgrf.cpp
+++ b/src/newgrf.cpp
@@ -87,8 +87,47 @@ enum GrfDataType {
static byte _grf_data_blocks;
static GrfDataType _grf_data_type;
+class OTTDByteReaderSignal { };
-typedef void (*SpecialSpriteHandler)(byte *buf, size_t len);
+class ByteReader {
+protected:
+ byte *data;
+ byte *end;
+
+public:
+ ByteReader(byte *data, byte *end) : data(data), end(end) { }
+
+ FORCEINLINE byte ReadByte()
+ {
+ if (data < end) return *(data)++;
+ throw OTTDByteReaderSignal();
+ }
+
+ FORCEINLINE size_t Remaining() const
+ {
+ return (int)(end - data);
+ }
+
+ FORCEINLINE bool HasData() const
+ {
+ return data < end;
+ }
+
+ FORCEINLINE byte *Data()
+ {
+ return data;
+ }
+
+ FORCEINLINE void Skip(size_t len)
+ {
+ data += len;
+ /* It is valid to move the buffer to exactly the end of the data,
+ * as there may not be any more data read. */
+ if (data > end) throw OTTDByteReaderSignal();
+ }
+};
+
+typedef void (*SpecialSpriteHandler)(ByteReader *buf);
enum {
MAX_STATIONS = 256,
@@ -153,25 +192,18 @@ void CDECL grfmsg(int severity, const char *str, ...)
DEBUG(grf, severity, "[%s:%d] %s", _cur_grfconfig->filename, _nfo_line, buf);
}
-static inline bool check_length(size_t real, size_t wanted, const char *str)
-{
- if (real >= wanted) return true;
- grfmsg(0, "%s: Invalid pseudo sprite length " PRINTF_SIZE " (expected " PRINTF_SIZE ")!", str, real, wanted);
- return false;
-}
-
-static inline byte grf_load_byte(byte **buf)
+static inline byte grf_load_byte(ByteReader **buf)
{
- return *(*buf)++;
+ return (*buf)->ReadByte();
}
-static uint16 grf_load_word(byte **buf)
+static uint16 grf_load_word(ByteReader **buf)
{
uint16 val = grf_load_byte(buf);
return val | (grf_load_byte(buf) << 8);
}
-static uint16 grf_load_extended(byte** buf)
+static uint16 grf_load_extended(ByteReader **buf)
{
uint16 val;
val = grf_load_byte(buf);
@@ -179,13 +211,13 @@ static uint16 grf_load_extended(byte** buf)
return val;
}
-static uint32 grf_load_dword(byte **buf)
+static uint32 grf_load_dword(ByteReader **buf)
{
uint32 val = grf_load_word(buf);
return val | (grf_load_word(buf) << 16);
}
-static uint32 grf_load_var(byte size, byte **buf)
+static uint32 grf_load_var(byte size, ByteReader **buf)
{
switch (size) {
case 1: return grf_load_byte(buf);
@@ -197,20 +229,21 @@ static uint32 grf_load_var(byte size, byte **buf)
}
}
-static const char *grf_load_string(byte **buf, size_t max_len)
+static const char *grf_load_string(ByteReader **bufp)
{
- const char *string = *(const char **)buf;
- size_t string_length = ttd_strnlen(string, max_len);
+ ByteReader *buf = *bufp;
+ char *string = reinterpret_cast<char *>(buf->Data());
+ size_t string_length = ttd_strnlen(string, buf->Remaining());
- if (string_length == max_len) {
+ if (string_length == buf->Remaining()) {
/* String was not NUL terminated, so make sure it is now. */
- (*buf)[string_length - 1] = '\0';
+ string[string_length - 1] = '\0';
grfmsg(7, "String was not terminated with a zero byte.");
} else {
/* Increase the string length to include the NUL byte. */
string_length++;
}
- *buf += string_length;
+ buf->Skip(string_length);
return string;
}
@@ -447,9 +480,9 @@ enum ChangeInfoResult {
CIR_INVALID_ID, ///< Attempt to modify an invalid ID
};
-typedef ChangeInfoResult (*VCI_Handler)(uint engine, int numinfo, int prop, byte **buf, int len);
+typedef ChangeInfoResult (*VCI_Handler)(uint engine, int numinfo, int prop, ByteReader *buf);
-static ChangeInfoResult CommonVehicleChangeInfo(EngineInfo *ei, int prop, byte **buf)
+static ChangeInfoResult CommonVehicleChangeInfo(EngineInfo *ei, int prop, ByteReader **buf)
{
switch (prop) {
case 0x00: // Introduction date
@@ -487,9 +520,8 @@ static ChangeInfoResult CommonVehicleChangeInfo(EngineInfo *ei, int prop, byte *
return CIR_SUCCESS;
}
-static ChangeInfoResult RailVehicleChangeInfo(uint engine, int numinfo, int prop, byte **bufp, int len)
+static ChangeInfoResult RailVehicleChangeInfo(uint engine, int numinfo, int prop, ByteReader *buf)
{
- byte *buf = *bufp;
ChangeInfoResult ret = CIR_SUCCESS;
for (int i = 0; i < numinfo; i++) {
@@ -730,13 +762,11 @@ static ChangeInfoResult RailVehicleChangeInfo(uint engine, int numinfo, int prop
}
}
- *bufp = buf;
return ret;
}
-static ChangeInfoResult RoadVehicleChangeInfo(uint engine, int numinfo, int prop, byte **bufp, int len)
+static ChangeInfoResult RoadVehicleChangeInfo(uint engine, int numinfo, int prop, ByteReader *buf)
{
- byte *buf = *bufp;
ChangeInfoResult ret = CIR_SUCCESS;
for (int i = 0; i < numinfo; i++) {
@@ -859,13 +889,11 @@ static ChangeInfoResult RoadVehicleChangeInfo(uint engine, int numinfo, int prop
}
}
- *bufp = buf;
return ret;
}
-static ChangeInfoResult ShipVehicleChangeInfo(uint engine, int numinfo, int prop, byte **bufp, int len)
+static ChangeInfoResult ShipVehicleChangeInfo(uint engine, int numinfo, int prop, ByteReader *buf)
{
- byte *buf = *bufp;
ChangeInfoResult ret = CIR_SUCCESS;
for (int i = 0; i < numinfo; i++) {
@@ -975,13 +1003,11 @@ static ChangeInfoResult ShipVehicleChangeInfo(uint engine, int numinfo, int prop
}
}
- *bufp = buf;
return ret;
}
-static ChangeInfoResult AircraftVehicleChangeInfo(uint engine, int numinfo, int prop, byte **bufp, int len)
+static ChangeInfoResult AircraftVehicleChangeInfo(uint engine, int numinfo, int prop, ByteReader *buf)
{
- byte *buf = *bufp;
ChangeInfoResult ret = CIR_SUCCESS;
for (int i = 0; i < numinfo; i++) {
@@ -1087,13 +1113,11 @@ static ChangeInfoResult AircraftVehicleChangeInfo(uint engine, int numinfo, int
}
}
- *bufp = buf;
return ret;
}
-static ChangeInfoResult StationChangeInfo(uint stid, int numinfo, int prop, byte **bufp, int len)
+static ChangeInfoResult StationChangeInfo(uint stid, int numinfo, int prop, ByteReader *buf)
{
- byte *buf = *bufp;
ChangeInfoResult ret = CIR_SUCCESS;
if (stid + numinfo > MAX_STATIONS) {
@@ -1146,7 +1170,7 @@ static ChangeInfoResult StationChangeInfo(uint stid, int numinfo, int prop, byte
MapSpriteMappingRecolour(&dts->ground);
- while (buf < *bufp + len) {
+ while (buf->HasData()) {
/* no relative bounding box support */
dts->seq = ReallocT(const_cast<DrawTileSeqStruct *>(dts->seq), ++seq_count);
DrawTileSeqStruct *dtss = const_cast<DrawTileSeqStruct *>(&dts->seq[seq_count - 1]);
@@ -1197,7 +1221,7 @@ static ChangeInfoResult StationChangeInfo(uint stid, int numinfo, int prop, byte
case 0x0E: // Define custom layout
statspec->copied_layouts = false;
- while (buf < *bufp + len) {
+ while (buf->HasData()) {
byte length = grf_load_byte(&buf);
byte number = grf_load_byte(&buf);
StationLayout layout;
@@ -1228,10 +1252,15 @@ static ChangeInfoResult StationChangeInfo(uint stid, int numinfo, int prop, byte
p = 0;
layout = MallocT<byte>(length * number);
- for (l = 0; l < length; l++) {
- for (p = 0; p < number; p++) {
- layout[l * number + p] = grf_load_byte(&buf);
+ try {
+ for (l = 0; l < length; l++) {
+ for (p = 0; p < number; p++) {
+ layout[l * number + p] = grf_load_byte(&buf);
+ }
}
+ } catch (...) {
+ free(layout);
+ throw;
}
l--;
@@ -1294,13 +1323,11 @@ static ChangeInfoResult StationChangeInfo(uint stid, int numinfo, int prop, byte
}
}
- *bufp = buf;
return ret;
}
-static ChangeInfoResult CanalChangeInfo(uint id, int numinfo, int prop, byte **bufp, int len)
+static ChangeInfoResult CanalChangeInfo(uint id, int numinfo, int prop, ByteReader *buf)
{
- byte *buf = *bufp;
ChangeInfoResult ret = CIR_SUCCESS;
if (id + numinfo > CF_END) {
@@ -1326,13 +1353,11 @@ static ChangeInfoResult CanalChangeInfo(uint id, int numinfo, int prop, byte **b
}
}
- *bufp = buf;
return ret;
}
-static ChangeInfoResult BridgeChangeInfo(uint brid, int numinfo, int prop, byte **bufp, int len)
+static ChangeInfoResult BridgeChangeInfo(uint brid, int numinfo, int prop, ByteReader *buf)
{
- byte *buf = *bufp;
ChangeInfoResult ret = CIR_SUCCESS;
if (brid + numinfo > MAX_BRIDGES) {
@@ -1428,13 +1453,11 @@ static ChangeInfoResult BridgeChangeInfo(uint brid, int numinfo, int prop, byte
}
}
- *bufp = buf;
return ret;
}
-static ChangeInfoResult TownHouseChangeInfo(uint hid, int numinfo, int prop, byte **bufp, int len)
+static ChangeInfoResult TownHouseChangeInfo(uint hid, int numinfo, int prop, ByteReader *buf)
{
- byte *buf = *bufp;
ChangeInfoResult ret = CIR_SUCCESS;
if (hid + numinfo > HOUSE_MAX) {
@@ -1652,13 +1675,11 @@ static ChangeInfoResult TownHouseChangeInfo(uint hid, int numinfo, int prop, byt
}
}
- *bufp = buf;
return ret;
}
-static ChangeInfoResult GlobalVarChangeInfo(uint gvid, int numinfo, int prop, byte **bufp, int len)
+static ChangeInfoResult GlobalVarChangeInfo(uint gvid, int numinfo, int prop, ByteReader *buf)
{
- byte *buf = *bufp;
ChangeInfoResult ret = CIR_SUCCESS;
for (int i = 0; i < numinfo; i++) {
@@ -1677,7 +1698,7 @@ static ChangeInfoResult GlobalVarChangeInfo(uint gvid, int numinfo, int prop, by
case 0x09: // Cargo translation table
/* This is loaded during the reservation stage, so just skip it here. */
/* Each entry is 4 bytes. */
- buf += 4;
+ buf->Skip(4);
break;
case 0x0A: { // Currency display names
@@ -1756,8 +1777,8 @@ static ChangeInfoResult GlobalVarChangeInfo(uint gvid, int numinfo, int prop, by
case 0x10: // Snow line height table
if (numinfo > 1 || IsSnowLineSet()) {
grfmsg(1, "GlobalVarChangeInfo: The snowline can only be set once (%d)", numinfo);
- } else if (len < SNOW_LINE_MONTHS * SNOW_LINE_DAYS) {
- grfmsg(1, "GlobalVarChangeInfo: Not enough entries set in the snowline table (%d)", len);
+ } else if (buf->Remaining() < SNOW_LINE_MONTHS * SNOW_LINE_DAYS) {
+ grfmsg(1, "GlobalVarChangeInfo: Not enough entries set in the snowline table (" PRINTF_SIZE ")", buf->Remaining());
} else {
byte table[SNOW_LINE_MONTHS][SNOW_LINE_DAYS];
@@ -1773,13 +1794,13 @@ static ChangeInfoResult GlobalVarChangeInfo(uint gvid, int numinfo, int prop, by
case 0x11: // GRF match for engine allocation
/* This is loaded during the reservation stage, so just skip it here. */
/* Each entry is 8 bytes. */
- buf += 8;
+ buf->Skip(8);
break;
case 0x12: // Rail type translation table
/* This is loaded during the reservation stage, so just skip it here. */
/* Each entry is 4 bytes. */
- buf += 4;
+ buf->Skip(4);
break;
default:
@@ -1788,13 +1809,11 @@ static ChangeInfoResult GlobalVarChangeInfo(uint gvid, int numinfo, int prop, by
}
}
- *bufp = buf;
return ret;
}
-static ChangeInfoResult GlobalVarReserveInfo(uint gvid, int numinfo, int prop, byte **bufp, int len)
+static ChangeInfoResult GlobalVarReserveInfo(uint gvid, int numinfo, int prop, ByteReader *buf)
{
- byte *buf = *bufp;
ChangeInfoResult ret = CIR_SUCCESS;
for (int i = 0; i < numinfo; i++) {
@@ -1833,7 +1852,7 @@ static ChangeInfoResult GlobalVarReserveInfo(uint gvid, int numinfo, int prop, b
break;
case 0x10: // Snow line height table
- buf += SNOW_LINE_MONTHS * SNOW_LINE_DAYS;
+ buf->Skip(SNOW_LINE_MONTHS * SNOW_LINE_DAYS);
break;
case 0x11: { // GRF match for engine allocation
@@ -1866,14 +1885,12 @@ static ChangeInfoResult GlobalVarReserveInfo(uint gvid, int numinfo, int prop, b
}
}
- *bufp = buf;
return ret;
}
-static ChangeInfoResult CargoChangeInfo(uint cid, int numinfo, int prop, byte **bufp, int len)
+static ChangeInfoResult CargoChangeInfo(uint cid, int numinfo, int prop, ByteReader *buf)
{
- byte *buf = *bufp;
ChangeInfoResult ret = CIR_SUCCESS;
if (cid + numinfo > NUM_CARGO) {
@@ -1992,14 +2009,12 @@ static ChangeInfoResult CargoChangeInfo(uint cid, int numinfo, int prop, byte **
}
}
- *bufp = buf;
return ret;
}
-static ChangeInfoResult SoundEffectChangeInfo(uint sid, int numinfo, int prop, byte **bufp, int len)
+static ChangeInfoResult SoundEffectChangeInfo(uint sid, int numinfo, int prop, ByteReader *buf)
{
- byte *buf = *bufp;
ChangeInfoResult ret = CIR_SUCCESS;
if (_cur_grffile->sound_offset == 0) {
@@ -2044,13 +2059,11 @@ static ChangeInfoResult SoundEffectChangeInfo(uint sid, int numinfo, int prop, b
}
}
- *bufp = buf;
return ret;
}
-static ChangeInfoResult IndustrytilesChangeInfo(uint indtid, int numinfo, int prop, byte **bufp, int len)
+static ChangeInfoResult IndustrytilesChangeInfo(uint indtid, int numinfo, int prop, ByteReader *buf)
{
- byte *buf = *bufp;
ChangeInfoResult ret = CIR_SUCCESS;
if (indtid + numinfo > NUM_INDUSTRYTILES) {
@@ -2154,7 +2167,6 @@ static ChangeInfoResult IndustrytilesChangeInfo(uint indtid, int numinfo, int pr
}
}
- *bufp = buf;
return ret;
}
@@ -2177,9 +2189,8 @@ static bool ValidateIndustryLayout(const IndustryTileTable *layout, int size)
return true;
}
-static ChangeInfoResult IndustriesChangeInfo(uint indid, int numinfo, int prop, byte **bufp, int len)
+static ChangeInfoResult IndustriesChangeInfo(uint indid, int numinfo, int prop, ByteReader *buf)
{
- byte *buf = *bufp;
ChangeInfoResult ret = CIR_SUCCESS;
if (indid + numinfo > NUM_INDUSTRYTYPES) {
@@ -2261,76 +2272,83 @@ static ChangeInfoResult IndustriesChangeInfo(uint indid, int numinfo, int prop,
uint size;
const IndustryTileTable *copy_from;
- for (byte j = 0; j < indsp->num_table; j++) {
- for (uint k = 0;; k++) {
- if (k >= def_num_tiles) {
- grfmsg(3, "IndustriesChangeInfo: Incorrect size for industry tile layout definition for industry %u.", indid);
- /* Size reported by newgrf was not big enough so enlarge the array. */
- def_num_tiles *= 2;
- itt = ReallocT<IndustryTileTable>(itt, def_num_tiles);
- }
+ try {
+ for (byte j = 0; j < indsp->num_table; j++) {
+ for (uint k = 0;; k++) {
+ if (k >= def_num_tiles) {
+ grfmsg(3, "IndustriesChangeInfo: Incorrect size for industry tile layout definition for industry %u.", indid);
+ /* Size reported by newgrf was not big enough so enlarge the array. */
+ def_num_tiles *= 2;
+ itt = ReallocT<IndustryTileTable>(itt, def_num_tiles);
+ }
- itt[k].ti.x = grf_load_byte(&buf); // Offsets from northermost tile
+ itt[k].ti.x = grf_load_byte(&buf); // Offsets from northermost tile
- if (itt[k].ti.x == 0xFE && k == 0) {
- /* This means we have to borrow the layout from an old industry */
- IndustryType type = grf_load_byte(&buf); // industry holding required layout
- byte laynbr = grf_load_byte(&buf); // layout number to borrow
+ if (itt[k].ti.x == 0xFE && k == 0) {
+ /* This means we have to borrow the layout from an old industry */
+ IndustryType type = grf_load_byte(&buf); // industry holding required layout
+ byte laynbr = grf_load_byte(&buf); // layout number to borrow
- copy_from = _origin_industry_specs[type].table[laynbr];
- for (size = 1;; size++) {
- if (copy_from[size - 1].ti.x == -0x80 && copy_from[size - 1].ti.y == 0) break;
+ copy_from = _origin_industry_specs[type].table[laynbr];
+ for (size = 1;; size++) {
+ if (copy_from[size - 1].ti.x == -0x80 && copy_from[size - 1].ti.y == 0) break;
+ }
+ break;
}
- break;
- }
- itt[k].ti.y = grf_load_byte(&buf); // Or table definition finalisation
+ itt[k].ti.y = grf_load_byte(&buf); // Or table definition finalisation
- if (itt[k].ti.x == 0 && itt[k].ti.y == 0x80) {
- /* Not the same terminator. The one we are using is rather
- x = -80, y = x . So, adjust it. */
- itt[k].ti.x = -0x80;
- itt[k].ti.y = 0;
- itt[k].gfx = 0;
+ if (itt[k].ti.x == 0 && itt[k].ti.y == 0x80) {
+ /* Not the same terminator. The one we are using is rather
+ x = -80, y = x . So, adjust it. */
+ itt[k].ti.x = -0x80;
+ itt[k].ti.y = 0;
+ itt[k].gfx = 0;
- size = k + 1;
- copy_from = itt;
- break;
- }
+ size = k + 1;
+ copy_from = itt;
+ break;
+ }
- itt[k].gfx = grf_load_byte(&buf);
+ itt[k].gfx = grf_load_byte(&buf);
- if (itt[k].gfx == 0xFE) {
- /* Use a new tile from this GRF */
- int local_tile_id = grf_load_word(&buf);
+ if (itt[k].gfx == 0xFE) {
+ /* Use a new tile from this GRF */
+ int local_tile_id = grf_load_word(&buf);
- /* Read the ID from the _industile_mngr. */
- int tempid = _industile_mngr.GetID(local_tile_id, _cur_grffile->grfid);
+ /* Read the ID from the _industile_mngr. */
+ int tempid = _industile_mngr.GetID(local_tile_id, _cur_grffile->grfid);
- if (tempid == INVALID_INDUSTRYTILE) {
- grfmsg(2, "IndustriesChangeInfo: Attempt to use industry tile %u with industry id %u, not yet defined. Ignoring.", local_tile_id, indid);
- } else {
- /* Declared as been valid, can be used */
- itt[k].gfx = tempid;
- size = k + 1;
- copy_from = itt;
+ if (tempid == INVALID_INDUSTRYTILE) {
+ grfmsg(2, "IndustriesChangeInfo: Attempt to use industry tile %u with industry id %u, not yet defined. Ignoring.", local_tile_id, indid);
+ } else {
+ /* Declared as been valid, can be used */
+ itt[k].gfx = tempid;
+ size = k + 1;
+ copy_from = itt;
+ }
+ } else if (itt[k].gfx == 0xFF) {
+ itt[k].ti.x = (int8)GB(itt[k].ti.x, 0, 8);
+ itt[k].ti.y = (int8)GB(itt[k].ti.y, 0, 8);
}
- } else if (itt[k].gfx == 0xFF) {
- itt[k].ti.x = (int8)GB(itt[k].ti.x, 0, 8);
- itt[k].ti.y = (int8)GB(itt[k].ti.y, 0, 8);
}
- }
- if (!ValidateIndustryLayout(copy_from, size)) {
- /* The industry layout was not valid, so skip this one. */
- grfmsg(1, "IndustriesChangeInfo: Invalid industry layout for industry id %u. Ignoring", indid);
- indsp->num_table--;
- j--;
- } else {
- tile_table[j] = CallocT<IndustryTileTable>(size);
- memcpy(tile_table[j], copy_from, sizeof(*copy_from) * size);
+ if (!ValidateIndustryLayout(copy_from, size)) {
+ /* The industry layout was not valid, so skip this one. */
+ grfmsg(1, "IndustriesChangeInfo: Invalid industry layout for industry id %u. Ignoring", indid);
+ indsp->num_table--;
+ j--;
+ } else {
+ tile_table[j] = CallocT<IndustryTileTable>(size);
+ memcpy(tile_table[j], copy_from, sizeof(*copy_from) * size);
+ }
}
+ } catch (...) {
+ free(tile_table);
+ free(itt);
+ throw;
}
+
/* Install final layout construction in the industry spec */
indsp->table = tile_table;
SetBit(indsp->cleanup_flag, 1);
@@ -2386,7 +2404,15 @@ static ChangeInfoResult IndustriesChangeInfo(uint indid, int numinfo, int prop,
indsp->number_of_sounds = grf_load_byte(&buf);
uint8 *sounds = MallocT<uint8>(indsp->number_of_sounds);
- for (uint8 j = 0; j < indsp->number_of_sounds; j++) sounds[j] = grf_load_byte(&buf);
+ try {
+ for (uint8 j = 0; j < indsp->number_of_sounds; j++) {
+ sounds[j] = grf_load_byte(&buf);
+ }
+ } catch (...) {
+ free(sounds);
+ throw;
+ }
+
indsp->random_sounds = sounds;
SetBit(indsp->cleanup_flag, 0);
} break;
@@ -2454,7 +2480,6 @@ static ChangeInfoResult IndustriesChangeInfo(uint indid, int numinfo, int prop,
}
}
- *bufp = buf;
return ret;
}
@@ -2486,10 +2511,8 @@ static bool HandleChangeInfoResult(const char *caller, ChangeInfoResult cir, uin
}
/* Action 0x00 */
-static void FeatureChangeInfo(byte *buf, size_t len)
+static void FeatureChangeInfo(ByteReader *buf)
{
- byte *bufend = buf + len;
-
/* <00> <feature> <num-props> <num-info> <id> (<property <new-info>)...
*
* B feature
@@ -2517,8 +2540,6 @@ static void FeatureChangeInfo(byte *buf, size_t len)
/* GSF_SOUNDFX */ SoundEffectChangeInfo,
};
- if (!check_length(len, 6, "FeatureChangeInfo")) return;
- buf++;
uint8 feature = grf_load_byte(&buf);
uint8 numprops = grf_load_byte(&buf);
uint numinfo = grf_load_byte(&buf);
@@ -2535,19 +2556,17 @@ static void FeatureChangeInfo(byte *buf, size_t len)
/* Mark the feature as used by the grf */
SetBit(_cur_grffile->grf_features, feature);
- while (numprops-- && buf < bufend) {
+ while (numprops-- && buf->HasData()) {
uint8 prop = grf_load_byte(&buf);
- ChangeInfoResult cir = handler[feature](engine, numinfo, prop, &buf, bufend - buf);
+ ChangeInfoResult cir = handler[feature](engine, numinfo, prop, buf);
if (HandleChangeInfoResult("FeatureChangeInfo", cir, feature, prop)) return;
}
}
/* Action 0x00 (GLS_SAFETYSCAN) */
-static void SafeChangeInfo(byte *buf, size_t len)
+static void SafeChangeInfo(ByteReader *buf)
{
- if (!check_length(len, 6, "SafeChangeInfo")) return;
- buf++;
uint8 feature = grf_load_byte(&buf);
uint8 numprops = grf_load_byte(&buf);
uint numinfo = grf_load_byte(&buf);
@@ -2583,12 +2602,8 @@ static void SafeChangeInfo(byte *buf, size_t len)
}
/* Action 0x00 (GLS_RESERVE) */
-static void ReserveChangeInfo(byte *buf, size_t len)
+static void ReserveChangeInfo(ByteReader *buf)
{
- byte *bufend = buf + len;
-
- if (!check_length(len, 6, "ReserveChangeInfo")) return;
- buf++;
uint8 feature = grf_load_byte(&buf);
if (feature != GSF_CARGOS && feature != GSF_GLOBALVAR) return;
@@ -2597,18 +2612,18 @@ static void ReserveChangeInfo(byte *buf, size_t len)
uint8 numinfo = grf_load_byte(&buf);
uint8 index = grf_load_extended(&buf);
- while (numprops-- && buf < bufend) {
+ while (numprops-- && buf->HasData()) {
uint8 prop = grf_load_byte(&buf);
ChangeInfoResult cir = CIR_SUCCESS;
switch (feature) {
default: NOT_REACHED();
case GSF_CARGOS:
- cir = CargoChangeInfo(index, numinfo, prop, &buf, bufend - buf);
+ cir = CargoChangeInfo(index, numinfo, prop, buf);
break;
case GSF_GLOBALVAR:
- cir = GlobalVarReserveInfo(index, numinfo, prop, &buf, bufend - buf);
+ cir = GlobalVarReserveInfo(index, numinfo, prop, buf);
break;
}
@@ -2617,7 +2632,7 @@ static void ReserveChangeInfo(byte *buf, size_t len)
}
/* Action 0x01 */
-static void NewSpriteSet(byte *buf, size_t len)
+static void NewSpriteSet(ByteReader *buf)
{
/* <01> <feature> <num-sets> <num-ent>
*
@@ -2631,8 +2646,6 @@ static void NewSpriteSet(byte *buf, size_t len)
* In that case, use num-dirs=4.
*/
- if (!check_length(len, 4, "NewSpriteSet")) return;
- buf++;
uint8 feature = grf_load_byte(&buf);
uint8 num_sets = grf_load_byte(&buf);
uint16 num_ents = grf_load_extended(&buf);
@@ -2653,10 +2666,8 @@ static void NewSpriteSet(byte *buf, size_t len)
}
/* Action 0x01 (SKIP) */
-static void SkipAct1(byte *buf, size_t len)
+static void SkipAct1(ByteReader *buf)
{
- if (!check_length(len, 4, "SkipAct1")) return;
- buf++;
grf_load_byte(&buf);
uint8 num_sets = grf_load_byte(&buf);
uint16 num_ents = grf_load_extended(&buf);
@@ -2712,7 +2723,7 @@ static const SpriteGroup *CreateGroupFromGroupID(byte feature, byte setid, byte
}
/* Action 0x02 */
-static void NewSpriteGroup(byte *buf, size_t len)
+static void NewSpriteGroup(ByteReader *buf)
{
/* <02> <feature> <set-id> <type/num-entries> <feature-specific-data...>
*
@@ -2725,10 +2736,6 @@ static void NewSpriteGroup(byte *buf, size_t len)
* meaning depends on the feature
* V feature-specific-data (huge mess, don't even look it up --pasky) */
SpriteGroup *act_group = NULL;
- byte *bufend = buf + len;
-
- if (!check_length(len, 5, "NewSpriteGroup")) return;
- buf++;
uint8 feature = grf_load_byte(&buf);
uint8 setid = grf_load_byte(&buf);
@@ -2743,6 +2750,10 @@ static void NewSpriteGroup(byte *buf, size_t len)
}
}
+ /* Sprite Groups are created here but they are allocated from a pool, so
+ * we do not need to delete anything if there is an exception from the
+ * ByteReader. */
+
switch (type) {
/* Deterministic Sprite Group */
case 0x81: // Self scope, byte
@@ -2755,9 +2766,6 @@ static void NewSpriteGroup(byte *buf, size_t len)
byte varadjust;
byte varsize;
- /* Check we can load the var size parameter */
- if (!check_length(bufend - buf, 1, "NewSpriteGroup (Deterministic) (1)")) return;
-
DeterministicSpriteGroup *group = new DeterministicSpriteGroup();
act_group = group;
group->var_scope = HasBit(type, 1) ? VSG_SCOPE_PARENT : VSG_SCOPE_SELF;
@@ -2769,17 +2777,11 @@ static void NewSpriteGroup(byte *buf, size_t len)
case 2: group->size = DSG_SIZE_DWORD; varsize = 4; break;
}
- if (!check_length(bufend - buf, 5 + varsize, "NewSpriteGroup (Deterministic) (2)")) return;
-
/* Loop through the var adjusts. Unfortunately we don't know how many we have
* from the outset, so we shall have to keep reallocing. */
do {
DeterministicSpriteGroupAdjust *adjust;
- if (group->num_adjusts > 0) {
- if (!check_length(bufend - buf, 2 + varsize + 3, "NewSpriteGroup (Deterministic) (3)")) return;
- }
-
group->num_adjusts++;
group->adjusts = ReallocT(group->adjusts, group->num_adjusts);
@@ -2814,8 +2816,6 @@ static void NewSpriteGroup(byte *buf, size_t len)
group->num_ranges = grf_load_byte(&buf);
if (group->num_ranges > 0) group->ranges = CallocT<DeterministicSpriteGroupRange>(group->num_ranges);
- if (!check_length(bufend - buf, 2 + (2 + 2 * varsize) * group->num_ranges, "NewSpriteGroup (Deterministic)")) return;
-
for (uint i = 0; i < group->num_ranges; i++) {
group->ranges[i].group = GetGroupFromGroupID(setid, type, grf_load_word(&buf));
group->ranges[i].low = grf_load_var(varsize, &buf);
@@ -2831,8 +2831,6 @@ static void NewSpriteGroup(byte *buf, size_t len)
case 0x83: // Parent scope
case 0x84: // Relative scope
{
- if (!check_length(bufend - buf, HasBit(type, 2) ? 8 : 7, "NewSpriteGroup (Randomized) (1)")) return;
-
RandomizedSpriteGroup *group = new RandomizedSpriteGroup();
act_group = group;
group->var_scope = HasBit(type, 1) ? VSG_SCOPE_PARENT : VSG_SCOPE_SELF;
@@ -2849,8 +2847,6 @@ static void NewSpriteGroup(byte *buf, size_t len)
group->num_groups = grf_load_byte(&buf);
group->groups = CallocT<const SpriteGroup*>(group->num_groups);
- if (!check_length(bufend - buf, 2 * group->num_groups, "NewSpriteGroup (Randomized) (2)")) return;
-
for (uint i = 0; i < group->num_groups; i++) {
group->groups[i] = GetGroupFromGroupID(setid, type, grf_load_word(&buf));
}
@@ -2879,8 +2875,6 @@ static void NewSpriteGroup(byte *buf, size_t len)
return;
}
- if (!check_length(bufend - buf, 2 * num_loaded + 2 * num_loading, "NewSpriteGroup (Real) (1)")) return;
-
RealSpriteGroup *group = new RealSpriteGroup();
act_group = group;
@@ -3084,7 +3078,7 @@ static bool IsValidGroupID(uint16 groupid, const char *function)
return true;
}
-static void VehicleMapSpriteGroup(byte *buf, byte feature, uint8 idcount)
+static void VehicleMapSpriteGroup(ByteReader *buf, byte feature, uint8 idcount)
{
static EngineID *last_engines;
static uint last_engines_count;
@@ -3158,7 +3152,7 @@ static void VehicleMapSpriteGroup(byte *buf, byte feature, uint8 idcount)
}
-static void CanalMapSpriteGroup(byte *buf, uint8 idcount)
+static void CanalMapSpriteGroup(ByteReader *buf, uint8 idcount)
{
CanalFeature *cfs = AllocaM(CanalFeature, idcount);
for (uint i = 0; i < idcount; i++) {
@@ -3166,7 +3160,7 @@ static void CanalMapSpriteGroup(byte *buf, uint8 idcount)
}
uint8 cidcount = grf_load_byte(&buf);
- buf += cidcount * 3;
+ buf->Skip(cidcount * 3);
uint16 groupid = grf_load_word(&buf);
if (!IsValidGroupID(groupid, "CanalMapSpriteGroup")) return;
@@ -3185,7 +3179,7 @@ static void CanalMapSpriteGroup(byte *buf, uint8 idcount)
}
-static void StationMapSpriteGroup(byte *buf, uint8 idcount)
+static void StationMapSpriteGroup(ByteReader *buf, uint8 idcount)
{
uint8 *stations = AllocaM(uint8, idcount);
for (uint i = 0; i < idcount; i++) {
@@ -3232,7 +3226,7 @@ static void StationMapSpriteGroup(byte *buf, uint8 idcount)
}
-static void TownHouseMapSpriteGroup(byte *buf, uint8 idcount)
+static void TownHouseMapSpriteGroup(ByteReader *buf, uint8 idcount)
{
uint8 *houses = AllocaM(uint8, idcount);
for (uint i = 0; i < idcount; i++) {
@@ -3241,7 +3235,7 @@ static void TownHouseMapSpriteGroup(byte *buf, uint8 idcount)
/* Skip the cargo type section, we only care about the default group */
uint8 cidcount = grf_load_byte(&buf);
- buf += cidcount * 3;
+ buf->Skip(cidcount * 3);
uint16 groupid = grf_load_word(&buf);
if (!IsValidGroupID(groupid, "TownHouseMapSpriteGroup")) return;
@@ -3263,7 +3257,7 @@ static void TownHouseMapSpriteGroup(byte *buf, uint8 idcount)
}
}
-static void IndustryMapSpriteGroup(byte *buf, uint8 idcount)
+static void IndustryMapSpriteGroup(ByteReader *buf, uint8 idcount)
{
uint8 *industries = AllocaM(uint8, idcount);
for (uint i = 0; i < idcount; i++) {
@@ -3272,7 +3266,7 @@ static void IndustryMapSpriteGroup(byte *buf, uint8 idcount)
/* Skip the cargo type section, we only care about the default group */
uint8 cidcount = grf_load_byte(&buf);
- buf += cidcount * 3;
+ buf->Skip(cidcount * 3);
uint16 groupid = grf_load_word(&buf);
if (!IsValidGroupID(groupid, "IndustryMapSpriteGroup")) return;
@@ -3294,7 +3288,7 @@ static void IndustryMapSpriteGroup(byte *buf, uint8 idcount)
}
}
-static void IndustrytileMapSpriteGroup(byte *buf, uint8 idcount)
+static void IndustrytileMapSpriteGroup(ByteReader *buf, uint8 idcount)
{
uint8 *indtiles = AllocaM(uint8, idcount);
for (uint i = 0; i < idcount; i++) {
@@ -3303,7 +3297,7 @@ static void IndustrytileMapSpriteGroup(byte *buf, uint8 idcount)
/* Skip the cargo type section, we only care about the default group */
uint8 cidcount = grf_load_byte(&buf);
- buf += cidcount * 3;
+ buf->Skip(cidcount * 3);
uint16 groupid = grf_load_word(&buf);
if (!IsValidGroupID(groupid, "IndustrytileMapSpriteGroup")) return;
@@ -3325,7 +3319,7 @@ static void IndustrytileMapSpriteGroup(byte *buf, uint8 idcount)
}
}
-static void CargoMapSpriteGroup(byte *buf, uint8 idcount)
+static void CargoMapSpriteGroup(ByteReader *buf, uint8 idcount)
{
CargoID *cargos = AllocaM(CargoID, idcount);
for (uint i = 0; i < idcount; i++) {
@@ -3334,7 +3328,7 @@ static void CargoMapSpriteGroup(byte *buf, uint8 idcount)
/* Skip the cargo type section, we only care about the default group */
uint8 cidcount = grf_load_byte(&buf);
- buf += cidcount * 3;
+ buf->Skip(cidcount * 3);
uint16 groupid = grf_load_word(&buf);
if (!IsValidGroupID(groupid, "CargoMapSpriteGroup")) return;
@@ -3355,7 +3349,7 @@ static void CargoMapSpriteGroup(byte *buf, uint8 idcount)
/* Action 0x03 */
-static void FeatureMapSpriteGroup(byte *buf, size_t len)
+static void FeatureMapSpriteGroup(ByteReader *buf)
{
/* <03> <feature> <n-id> <ids>... <num-cid> [<cargo-type> <cid>]... <def-cid>
* id-list := [<id>] [id-list]
@@ -3376,9 +3370,6 @@ static void FeatureMapSpriteGroup(byte *buf, size_t len)
return;
}
- if (!check_length(len, 6, "FeatureMapSpriteGroup")) return;
-
- buf++;
uint8 feature = grf_load_byte(&buf);
uint8 idcount = grf_load_byte(&buf);
@@ -3438,7 +3429,7 @@ static void FeatureMapSpriteGroup(byte *buf, size_t len)
}
/* Action 0x04 */
-static void FeatureNewName(byte *buf, size_t len)
+static void FeatureNewName(ByteReader *buf)
{
/* <04> <veh-type> <language-id> <num-veh> <offset> <data...>
*
@@ -3458,8 +3449,6 @@ static void FeatureNewName(byte *buf, size_t len)
bool new_scheme = _cur_grffile->grf_version >= 7;
- if (!check_length(len, 6, "FeatureNewName")) return;
- buf++;
uint8 feature = grf_load_byte(&buf);
uint8 lang = grf_load_byte(&buf);
uint8 num = grf_load_byte(&buf);
@@ -3480,14 +3469,8 @@ static void FeatureNewName(byte *buf, size_t len)
grfmsg(6, "FeatureNewName: About to rename engines %d..%d (feature %d) in language 0x%02X",
id, endid, feature, lang);
- len -= generic ? 6 : 5;
-
- for (; id < endid && len > 0; id++) {
- const char *name = grf_load_string(&buf, len);
- size_t name_length = strlen(name) + 1;
-
- len -= (int)name_length;
-
+ for (; id < endid && buf->HasData(); id++) {
+ const char *name = grf_load_string(&buf);
grfmsg(8, "FeatureNewName: 0x%04X <- %s", id, name);
switch (feature) {
@@ -3600,7 +3583,7 @@ static uint16 SanitizeSpriteOffset(uint16& num, uint16 offset, int max_sprites,
}
/* Action 0x05 */
-static void GraphicsNew(byte *buf, size_t len)
+static void GraphicsNew(ByteReader *buf)
{
/* <05> <graphics-type> <num-sprites> <other data...>
*
@@ -3648,8 +3631,6 @@ static void GraphicsNew(byte *buf, size_t len)
/* 0x15 */ { A5BLOCK_ALLOW_OFFSET, SPR_OPENTTD_BASE, 1, OPENTTD_SPRITE_COUNT, "OpenTTD GUI graphics" },
};
- if (!check_length(len, 2, "GraphicsNew")) return;
- buf++;
uint8 type = grf_load_byte(&buf);
uint16 num = grf_load_extended(&buf);
uint16 offset = HasBit(type, 7) ? grf_load_extended(&buf) : 0;
@@ -3714,11 +3695,8 @@ static void GraphicsNew(byte *buf, size_t len)
}
/* Action 0x05 (SKIP) */
-static void SkipAct5(byte *buf, size_t len)
+static void SkipAct5(ByteReader *buf)
{
- if (!check_length(len, 2, "SkipAct5")) return;
- buf++;
-
/* Ignore type byte */
grf_load_byte(&buf);
@@ -3906,7 +3884,7 @@ static uint32 GetParamVal(byte param, uint32 *cond_val)
}
/* Action 0x06 */
-static void CfgApply(byte *buf, size_t len)
+static void CfgApply(ByteReader *buf)
{
/* <06> <param-num> <param-size> <offset> ... <FF>
*
@@ -3950,7 +3928,6 @@ static void CfgApply(byte *buf, size_t len)
}
/* Now perform the Action 0x06 on our data. */
- buf++;
for (;;) {
uint i;
@@ -4030,7 +4007,7 @@ static void DisableStaticNewGRFInfluencingNonStaticNewGRFs(GRFConfig *c)
/* Action 0x07
* Action 0x09 */
-static void SkipIf(byte *buf, size_t len)
+static void SkipIf(ByteReader *buf)
{
/* <07/09> <param-num> <param-size> <condition-type> <value> <num-sprites>
*
@@ -4044,8 +4021,6 @@ static void SkipIf(byte *buf, size_t len)
uint32 mask = 0;
bool result;
- if (!check_length(len, 6, "SkipIf")) return;
- buf++;
uint8 param = grf_load_byte(&buf);
uint8 paramsize = grf_load_byte(&buf);
uint8 condtype = grf_load_byte(&buf);
@@ -4196,10 +4171,8 @@ static void SkipIf(byte *buf, size_t len)
/* Action 0x08 (GLS_FILESCAN) */
-static void ScanInfo(byte *buf, size_t len)
+static void ScanInfo(ByteReader *buf)
{
- if (!check_length(len, 8, "Info")) return;
- buf++;
grf_load_byte(&buf);
uint32 grfid = grf_load_dword(&buf);
@@ -4208,13 +4181,11 @@ static void ScanInfo(byte *buf, size_t len)
/* GRF IDs starting with 0xFF are reserved for internal TTDPatch use */
if (GB(grfid, 24, 8) == 0xFF) SetBit(_cur_grfconfig->flags, GCF_SYSTEM);
- len -= 6;
- const char *name = grf_load_string(&buf, len);
+ const char *name = grf_load_string(&buf);
_cur_grfconfig->name = TranslateTTDPatchCodes(grfid, name);
- len -= strlen(name) + 1;
- if (len > 0) {
- const char *info = grf_load_string(&buf, len);
+ if (buf->HasData()) {
+ const char *info = grf_load_string(&buf);
_cur_grfconfig->info = TranslateTTDPatchCodes(grfid, info);
}
@@ -4223,7 +4194,7 @@ static void ScanInfo(byte *buf, size_t len)
}
/* Action 0x08 */
-static void GRFInfo(byte *buf, size_t len)
+static void GRFInfo(ByteReader *buf)
{
/* <08> <version> <grf-id> <name> <info>
*
@@ -4232,11 +4203,9 @@ static void GRFInfo(byte *buf, size_t len)
* S name name of this .grf set
* S info string describing the set, and e.g. author and copyright */
- if (!check_length(len, 8, "GRFInfo")) return;
- buf++;
uint8 version = grf_load_byte(&buf);
uint32 grfid = grf_load_dword(&buf);
- const char *name = grf_load_string(&buf, len - 6);
+ const char *name = grf_load_string(&buf);
if (_cur_stage < GLS_RESERVE && _cur_grfconfig->status != GCS_UNKNOWN) {
_cur_grfconfig->status = GCS_DISABLED;
@@ -4257,7 +4226,7 @@ static void GRFInfo(byte *buf, size_t len)
}
/* Action 0x0A */
-static void SpriteReplace(byte *buf, size_t len)
+static void SpriteReplace(ByteReader *buf)
{
/* <0A> <num-sets> <set1> [<set2> ...]
* <set>: <num-sprites> <first-sprite>
@@ -4267,7 +4236,6 @@ static void SpriteReplace(byte *buf, size_t len)
* B num-sprites How many sprites are in this set
* W first-sprite First sprite number to replace */
- buf++; // skip action byte
uint8 num_sets = grf_load_byte(&buf);
for (uint i = 0; i < num_sets; i++) {
@@ -4293,9 +4261,8 @@ static void SpriteReplace(byte *buf, size_t len)
}
/* Action 0x0A (SKIP) */
-static void SkipActA(byte *buf, size_t len)
+static void SkipActA(ByteReader *buf)
{
- buf++;
uint8 num_sets = grf_load_byte(&buf);
for (uint i = 0; i < num_sets; i++) {
@@ -4309,7 +4276,7 @@ static void SkipActA(byte *buf, size_t len)
}
/* Action 0x0B */
-static void GRFLoadError(byte *buf, size_t len)
+static void GRFLoadError(ByteReader *buf)
{
/* <0B> <severity> <language-id> <message-id> [<message...> 00] [<data...>] 00 [<parnum>]
*
@@ -4343,16 +4310,12 @@ static void GRFLoadError(byte *buf, size_t len)
STR_NEWGRF_ERROR_MSG_FATAL
};
- if (!check_length(len, 6, "GRFLoadError")) return;
-
/* For now we can only show one message per newgrf file. */
if (_cur_grfconfig->error != NULL) return;
- buf++; // Skip the action byte.
byte severity = grf_load_byte(&buf);
byte lang = grf_load_byte(&buf);
byte message_id = grf_load_byte(&buf);
- len -= 4;
/* Skip the error if it isn't valid for the current language. */
if (!CheckGrfLangID(lang, _cur_grffile->grf_version)) return;
@@ -4381,7 +4344,7 @@ static void GRFLoadError(byte *buf, size_t len)
return;
}
- if (len <= 1) {
+ if (buf->Remaining() <= 1) {
grfmsg(7, "GRFLoadError: No message data supplied.");
return;
}
@@ -4392,17 +4355,20 @@ static void GRFLoadError(byte *buf, size_t len)
if (message_id == 0xFF) {
/* This is a custom error message. */
- const char *message = grf_load_string(&buf, len);
- len -= (strlen(message) + 1);
+ if (buf->HasData()) {
+ const char *message = grf_load_string(&buf);
- error->custom_message = TranslateTTDPatchCodes(_cur_grffile->grfid, message);
+ error->custom_message = TranslateTTDPatchCodes(_cur_grffile->grfid, message);
+ } else {
+ grfmsg(7, "GRFLoadError: No custom message supplied.");
+ error->custom_message = strdup("");
+ }
} else {
error->message = msgstr[message_id];
}
- if (len > 0) {
- const char *data = grf_load_string(&buf, len);
- len -= (strlen(data) + 1);
+ if (buf->HasData()) {
+ const char *data = grf_load_string(&buf);
error->data = TranslateTTDPatchCodes(_cur_grffile->grfid, data);
} else {
@@ -4412,10 +4378,9 @@ static void GRFLoadError(byte *buf, size_t len)
/* Only two parameter numbers can be used in the string. */
uint i = 0;
- for (; i < 2 && len > 0; i++) {
+ for (; i < 2 && buf->HasData(); i++) {
uint param_number = grf_load_byte(&buf);
error->param_value[i] = _cur_grffile->GetParam(param_number);
- len--;
}
error->num_params = i;
@@ -4423,24 +4388,21 @@ static void GRFLoadError(byte *buf, size_t len)
}
/* Action 0x0C */
-static void GRFComment(byte *buf, size_t len)
+static void GRFComment(ByteReader *buf)
{
/* <0C> [<ignored...>]
*
* V ignored Anything following the 0C is ignored */
- if (len == 1) return;
+ if (!buf->HasData()) return;
- size_t text_len = len - 1;
- const char *text = (const char*)(buf + 1);
- grfmsg(2, "GRFComment: %.*s", (int)text_len, text);
+ const char *text = grf_load_string(&buf);
+ grfmsg(2, "GRFComment: %s", text);
}
/* Action 0x0D (GLS_SAFETYSCAN) */
-static void SafeParamSet(byte *buf, size_t len)
+static void SafeParamSet(ByteReader *buf)
{
- if (!check_length(len, 5, "SafeParamSet")) return;
- buf++;
uint8 target = grf_load_byte(&buf);
/* Only writing GRF parameters is considered safe */
@@ -4570,7 +4532,7 @@ static uint32 PerformGRM(uint32 *grm, uint16 num_ids, uint16 count, uint8 op, ui
/* Action 0x0D */
-static void ParamSet(byte *buf, size_t len)
+static void ParamSet(ByteReader *buf)
{
/* <0D> <target> <operation> <source1> <source2> [<data>]
*
@@ -4594,15 +4556,13 @@ static void ParamSet(byte *buf, size_t len)
* (source2 like in 05, and source1 as well)
*/
- if (!check_length(len, 5, "ParamSet")) return;
- buf++;
uint8 target = grf_load_byte(&buf);
uint8 oper = grf_load_byte(&buf);
uint32 src1 = grf_load_byte(&buf);
uint32 src2 = grf_load_byte(&buf);
uint32 data = 0;
- if (len >= 8) data = grf_load_dword(&buf);
+ if (buf->Remaining() >= 4) data = grf_load_dword(&buf);
/* You can add 80 to the operation to make it apply only if the target
* is not defined yet. In this respect, a parameter is taken to be
@@ -4873,17 +4833,14 @@ static void ParamSet(byte *buf, size_t len)
}
/* Action 0x0E (GLS_SAFETYSCAN) */
-static void SafeGRFInhibit(byte *buf, size_t len)
+static void SafeGRFInhibit(ByteReader *buf)
{
/* <0E> <num> <grfids...>
*
* B num Number of GRFIDs that follow
* D grfids GRFIDs of the files to deactivate */
- if (!check_length(len, 2, "GRFInhibit")) return;
- buf++;
uint8 num = grf_load_byte(&buf);
- if (!check_length(len, 2 + 4 * num, "GRFInhibit")) return;
for (uint i = 0; i < num; i++) {
uint32 grfid = grf_load_dword(&buf);
@@ -4901,17 +4858,14 @@ static void SafeGRFInhibit(byte *buf, size_t len)
}
/* Action 0x0E */
-static void GRFInhibit(byte *buf, size_t len)
+static void GRFInhibit(ByteReader *buf)
{
/* <0E> <num> <grfids...>
*
* B num Number of GRFIDs that follow
* D grfids GRFIDs of the files to deactivate */
- if (!check_length(len, 2, "GRFInhibit")) return;
- buf++;
uint8 num = grf_load_byte(&buf);
- if (!check_length(len, 2 + 4 * num, "GRFInhibit")) return;
for (uint i = 0; i < num; i++) {
uint32 grfid = grf_load_dword(&buf);
@@ -4926,7 +4880,7 @@ static void GRFInhibit(byte *buf, size_t len)
}
/* Action 0x0F */
-static void FeatureTownName(byte *buf, size_t len)
+static void FeatureTownName(ByteReader *buf)
{
/* <0F> <id> <style-name> <num-parts> <parts>
*
@@ -4935,15 +4889,11 @@ static void FeatureTownName(byte *buf, size_t len)
* B num-parts Number of parts in this definition
* V parts The parts */
- if (!check_length(len, 1, "FeatureTownName: definition ID")) return;
- buf++; len--;
-
uint32 grfid = _cur_grffile->grfid;
GRFTownName *townname = AddGRFTownName(grfid);
byte id = grf_load_byte(&buf);
- len--;
grfmsg(6, "FeatureTownName: definition 0x%02X", id & 0x7F);
if (HasBit(id, 7)) {
@@ -4951,17 +4901,13 @@ static void FeatureTownName(byte *buf, size_t len)
ClrBit(id, 7);
bool new_scheme = _cur_grffile->grf_version >= 7;
- if (!check_length(len, 1, "FeatureTownName: lang_id")) return;
byte lang = grf_load_byte(&buf);
- len--;
byte nb_gen = townname->nb_gen;
do {
ClrBit(lang, 7);
- if (!check_length(len, 1, "FeatureTownName: style name")) return;
- const char *name = grf_load_string(&buf, len);
- len -= strlen(name) + 1;
+ const char *name = grf_load_string(&buf);
char *lang_name = TranslateTTDPatchCodes(grfid, name);
grfmsg(6, "FeatureTownName: lang 0x%X -> '%s'", lang, lang_name);
@@ -4969,41 +4915,32 @@ static void FeatureTownName(byte *buf, size_t len)
townname->name[nb_gen] = AddGRFString(grfid, id, lang, new_scheme, name, STR_UNDEFINED);
- if (!check_length(len, 1, "FeatureTownName: lang_id")) return;
lang = grf_load_byte(&buf);
- len--;
} while (lang != 0);
townname->id[nb_gen] = id;
townname->nb_gen++;
}
- if (!check_length(len, 1, "FeatureTownName: number of parts")) return;
byte nb = grf_load_byte(&buf);
- len--;
grfmsg(6, "FeatureTownName: %u parts", nb);
townname->nbparts[id] = nb;
townname->partlist[id] = CallocT<NamePartList>(nb);
for (int i = 0; i < nb; i++) {
- if (!check_length(len, 3, "FeatureTownName: parts header")) return;
byte nbtext = grf_load_byte(&buf);
townname->partlist[id][i].bitstart = grf_load_byte(&buf);
townname->partlist[id][i].bitcount = grf_load_byte(&buf);
townname->partlist[id][i].maxprob = 0;
townname->partlist[id][i].partcount = nbtext;
townname->partlist[id][i].parts = CallocT<NamePart>(nbtext);
- len -= 3;
grfmsg(6, "FeatureTownName: part %d contains %d texts and will use GB(seed, %d, %d)", i, nbtext, townname->partlist[id][i].bitstart, townname->partlist[id][i].bitcount);
for (int j = 0; j < nbtext; j++) {
- if (!check_length(len, 2, "FeatureTownName: part")) return;
byte prob = grf_load_byte(&buf);
- len--;
if (HasBit(prob, 7)) {
byte ref_id = grf_load_byte(&buf);
- len--;
if (townname->nbparts[ref_id] == 0) {
grfmsg(0, "FeatureTownName: definition 0x%02X doesn't exist, deactivating", ref_id);
@@ -5017,8 +4954,7 @@ static void FeatureTownName(byte *buf, size_t len)
grfmsg(6, "FeatureTownName: part %d, text %d, uses intermediate definition 0x%02X (with probability %d)", i, j, ref_id, prob & 0x7F);
townname->partlist[id][i].parts[j].data.id = ref_id;
} else {
- const char *text = grf_load_string(&buf, len);
- len -= strlen(text) + 1;
+ const char *text = grf_load_string(&buf);
townname->partlist[id][i].parts[j].data.text = TranslateTTDPatchCodes(grfid, text);
grfmsg(6, "FeatureTownName: part %d, text %d, '%s' (with probability %d)", i, j, townname->partlist[id][i].parts[j].data.text, prob);
}
@@ -5030,18 +4966,17 @@ static void FeatureTownName(byte *buf, size_t len)
}
/* Action 0x10 */
-static void DefineGotoLabel(byte *buf, size_t len)
+static void DefineGotoLabel(ByteReader *buf)
{
/* <10> <label> [<comment>]
*
* B label The label to define
* V comment Optional comment - ignored */
- if (!check_length(len, 1, "DefineGotoLabel")) return;
- buf++; len--;
+ byte nfo_label = grf_load_byte(&buf);
GRFLabel *label = MallocT<GRFLabel>(1);
- label->label = grf_load_byte(&buf);
+ label->label = nfo_label;
label->nfo_line = _nfo_line;
label->pos = FioGetPos();
label->next = NULL;
@@ -5060,14 +4995,12 @@ static void DefineGotoLabel(byte *buf, size_t len)
}
/* Action 0x11 */
-static void GRFSound(byte *buf, size_t len)
+static void GRFSound(ByteReader *buf)
{
/* <11> <num>
*
* W num Number of sound files that follow */
- if (!check_length(len, 1, "GRFSound")) return;
- buf++;
uint16 num = grf_load_word(&buf);
_grf_data_blocks = num;
@@ -5077,20 +5010,18 @@ static void GRFSound(byte *buf, size_t len)
}
/* Action 0x11 (SKIP) */
-static void SkipAct11(byte *buf, size_t len)
+static void SkipAct11(ByteReader *buf)
{
/* <11> <num>
*
* W num Number of sound files that follow */
- if (!check_length(len, 1, "SkipAct11")) return;
- buf++;
_skip_sprites = grf_load_word(&buf);
grfmsg(3, "SkipAct11: Skipping %d sprites", _skip_sprites);
}
-static void ImportGRFSound(byte *buf, int len)
+static void ImportGRFSound(ByteReader *buf)
{
const GRFFile *file;
SoundEntry *sound = AllocateSound();
@@ -5118,15 +5049,13 @@ static void ImportGRFSound(byte *buf, int len)
}
/* 'Action 0xFE' */
-static void GRFImportBlock(byte *buf, int len)
+static void GRFImportBlock(ByteReader *buf)
{
if (_grf_data_blocks == 0) {
grfmsg(2, "GRFImportBlock: Unexpected import block, skipping");
return;
}
- buf++;
-
_grf_data_blocks--;
/* XXX 'Action 0xFE' isn't really specified. It is only mentioned for
@@ -5136,15 +5065,13 @@ static void GRFImportBlock(byte *buf, int len)
}
switch (_grf_data_type) {
- case GDT_SOUND: ImportGRFSound(buf, len - 1); break;
+ case GDT_SOUND: ImportGRFSound(buf); break;
default: NOT_REACHED();
}
}
-static void LoadGRFSound(byte *buf, uint len)
+static void LoadGRFSound(ByteReader *buf)
{
- byte *buf_start = buf;
-
/* Allocate a sound entry. This is done even if the data is not loaded
* so that the indices used elsewhere are still correct. */
SoundEntry *sound = AllocateSound();
@@ -5155,7 +5082,7 @@ static void LoadGRFSound(byte *buf, uint len)
}
uint32 total_size = grf_load_dword(&buf);
- if (total_size > len + 8) {
+ if (total_size > buf->Remaining()) {
grfmsg(1, "LoadGRFSound: RIFF was truncated");
return;
}
@@ -5194,7 +5121,7 @@ static void LoadGRFSound(byte *buf, uint len)
case 'atad': // 'data'
sound->file_size = size;
- sound->file_offset = FioGetPos() - (len - (buf - buf_start));
+ sound->file_offset = FioGetPos() - buf->Remaining();
sound->file_slot = _file_index;
/* Set default volume and priority */
@@ -5220,7 +5147,7 @@ static void LoadGRFSound(byte *buf, uint len)
}
/* Action 0x12 */
-static void LoadFontGlyph(byte *buf, size_t len)
+static void LoadFontGlyph(ByteReader *buf)
{
/* <12> <num_def> <font_size> <num_char> <base_char>
*
@@ -5229,13 +5156,8 @@ static void LoadFontGlyph(byte *buf, size_t len)
* B num_char Number of consecutive glyphs
* W base_char First character index */
- buf++; len--;
- if (!check_length(len, 1, "LoadFontGlyph")) return;
-
uint8 num_def = grf_load_byte(&buf);
- if (!check_length(len, 1 + num_def * 4, "LoadFontGlyph")) return;
-
for (uint i = 0; i < num_def; i++) {
FontSize size = (FontSize)grf_load_byte(&buf);
uint8 num_char = grf_load_byte(&buf);
@@ -5252,7 +5174,7 @@ static void LoadFontGlyph(byte *buf, size_t len)
}
/* Action 0x12 (SKIP) */
-static void SkipAct12(byte *buf, size_t len)
+static void SkipAct12(ByteReader *buf)
{
/* <12> <num_def> <font_size> <num_char> <base_char>
*
@@ -5261,12 +5183,8 @@ static void SkipAct12(byte *buf, size_t len)
* B num_char Number of consecutive glyphs
* W base_char First character index */
- buf++; len--;
- if (!check_length(len, 1, "SkipAct12")) return;
uint8 num_def = grf_load_byte(&buf);
- if (!check_length(len, 1 + num_def * 4, "SkipAct12")) return;
-
for (uint i = 0; i < num_def; i++) {
/* Ignore 'size' byte */
grf_load_byte(&buf);
@@ -5282,7 +5200,7 @@ static void SkipAct12(byte *buf, size_t len)
}
/* Action 0x13 */
-static void TranslateGRFStrings(byte *buf, size_t len)
+static void TranslateGRFStrings(ByteReader *buf)
{
/* <13> <grfid> <num-ent> <offset> <text...>
*
@@ -5291,9 +5209,6 @@ static void TranslateGRFStrings(byte *buf, size_t len)
* W offset First text ID
* S text... Zero-terminated strings */
- buf++; len--;
- if (!check_length(len, 7, "TranslateGRFString")) return;
-
uint32 grfid = grf_load_dword(&buf);
const GRFConfig *c = GetGRFConfig(grfid);
if (c == NULL || (c->status != GCS_INITIALISED && c->status != GCS_ACTIVATED)) {
@@ -5330,15 +5245,10 @@ static void TranslateGRFStrings(byte *buf, size_t len)
return;
}
- len -= 7;
-
- for (uint i = 0; i < num_strings && len > 0; i++) {
- const char *string = grf_load_string(&buf, len);
- size_t string_length = strlen(string) + 1;
+ for (uint i = 0; i < num_strings && buf->HasData(); i++) {
+ const char *string = grf_load_string(&buf);
- len -= (int)string_length;
-
- if (string_length == 1) {
+ if (StrEmpty(string)) {
grfmsg(7, "TranslateGRFString: Ignoring empty string.");
continue;
}
@@ -5353,7 +5263,7 @@ static void TranslateGRFStrings(byte *buf, size_t len)
}
/* 'Action 0xFF' */
-static void GRFDataBlock(byte *buf, int len)
+static void GRFDataBlock(ByteReader *buf)
{
/* <FF> <name_len> <name> '\0' <data> */
@@ -5362,12 +5272,9 @@ static void GRFDataBlock(byte *buf, int len)
return;
}
- if (!check_length(len, 3, "GRFDataBlock")) return;
-
- buf++;
uint8 name_len = grf_load_byte(&buf);
- const char *name = (const char *)buf;
- buf += name_len;
+ const char *name = reinterpret_cast<const char *>(buf->Data());
+ buf->Skip(name_len);
/* Test string termination */
if (grf_load_byte(&buf) != 0) {
@@ -5375,21 +5282,19 @@ static void GRFDataBlock(byte *buf, int len)
return;
}
- if (!check_length(len, 3 + name_len, "GRFDataBlock")) return;
-
grfmsg(2, "GRFDataBlock: block name '%s'...", name);
_grf_data_blocks--;
switch (_grf_data_type) {
- case GDT_SOUND: LoadGRFSound(buf, len - name_len - 3); break;
+ case GDT_SOUND: LoadGRFSound(buf); break;
default: NOT_REACHED();
}
}
/* Used during safety scan on unsafe actions */
-static void GRFUnsafe(byte *buf, size_t len)
+static void GRFUnsafe(ByteReader *buf)
{
SetBit(_cur_grfconfig->flags, GCF_UNSAFE);
@@ -6045,21 +5950,34 @@ static void DecodeSpecialSprite(byte *buf, uint num, GrfLoadingStage stage)
FioSeekTo(num, SEEK_CUR);
}
- byte action = buf[0];
+ ByteReader br(buf, buf + num);
+ ByteReader *bufp = &br;
- if (action == 0xFF) {
- grfmsg(7, "DecodeSpecialSprite: Handling data block in stage %d", stage);
- GRFDataBlock(buf, num);
- } else if (action == 0xFE) {
- grfmsg(7, "DecodeSpecialSprite: Handling import block in stage %d", stage);
- GRFImportBlock(buf, num);
- } else if (action >= lengthof(handlers)) {
- grfmsg(7, "DecodeSpecialSprite: Skipping unknown action 0x%02X", action);
- } else if (handlers[action][stage] == NULL) {
- grfmsg(7, "DecodeSpecialSprite: Skipping action 0x%02X in stage %d", action, stage);
- } else {
- grfmsg(7, "DecodeSpecialSprite: Handling action 0x%02X in stage %d", action, stage);
- handlers[action][stage](buf, num);
+ try {
+ byte action = grf_load_byte(&bufp);
+
+ if (action == 0xFF) {
+ grfmsg(7, "DecodeSpecialSprite: Handling data block in stage %d", stage);
+ GRFDataBlock(bufp);
+ } else if (action == 0xFE) {
+ grfmsg(7, "DecodeSpecialSprite: Handling import block in stage %d", stage);
+ GRFImportBlock(bufp);
+ } else if (action >= lengthof(handlers)) {
+ grfmsg(7, "DecodeSpecialSprite: Skipping unknown action 0x%02X", action);
+ } else if (handlers[action][stage] == NULL) {
+ grfmsg(7, "DecodeSpecialSprite: Skipping action 0x%02X in stage %d", action, stage);
+ } else {
+ grfmsg(7, "DecodeSpecialSprite: Handling action 0x%02X in stage %d", action, stage);
+ handlers[action][stage](bufp);
+ }
+ } catch (...) {
+ grfmsg(1, "DecodeSpecialSprite: Tried to read past end of pseudo-sprite data");
+
+ _skip_sprites = -1;
+ _cur_grfconfig->status = GCS_DISABLED;
+ _cur_grfconfig->error = CallocT<GRFError>(1);
+ _cur_grfconfig->error->severity = STR_NEWGRF_ERROR_MSG_FATAL;
+ _cur_grfconfig->error->message = STR_NEWGRF_ERROR_READ_BOUNDS;
}
}