diff --git a/src/saveload/company_sl.cpp b/src/saveload/company_sl.cpp --- a/src/saveload/company_sl.cpp +++ b/src/saveload/company_sl.cpp @@ -372,8 +372,19 @@ public: class SlCompanyOldEconomy : public SlCompanyEconomy { public: - void GenericSaveLoad(CompanyProperties *c) const + void Save(CompanyProperties *c) const override { + SlSetStructListLength(c->num_valid_stat_ent); + for (int i = 0; i < c->num_valid_stat_ent; i++) { + SlObject(&c->old_economy[i], this->GetDescription()); + } + } + + void Load(CompanyProperties *c) const override + { + if (!IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH)) { + c->num_valid_stat_ent = (uint8)SlGetStructListLength(UINT8_MAX); + } if (c->num_valid_stat_ent > lengthof(c->old_economy)) SlErrorCorrupt("Too many old economy entries"); for (int i = 0; i < c->num_valid_stat_ent; i++) { @@ -381,10 +392,7 @@ public: } } - void Save(CompanyProperties *c) const override { this->GenericSaveLoad(c); } - void Load(CompanyProperties *c) const override { this->GenericSaveLoad(c); } - void LoadCheck(CompanyProperties *c) const override { this->GenericSaveLoad(c); } - void FixPointers(CompanyProperties *c) const override { this->GenericSaveLoad(c); } + void LoadCheck(CompanyProperties *c) const override { this->Load(c); } }; class SlCompanyLiveries : public DefaultSaveLoadHandler { @@ -395,12 +403,33 @@ public: SLE_CONDVAR(Livery, colour2, SLE_UINT8, SLV_34, SL_MAX_VERSION), }; - void GenericSaveLoad(CompanyProperties *c) const + /** + * Get the number of liveries used by this savegame version. + * @return The number of liveries used by this savegame version. + */ + size_t GetNumLiveries() const { - int num_liveries = IsSavegameVersionBefore(SLV_63) ? LS_END - 4 : (IsSavegameVersionBefore(SLV_85) ? LS_END - 2: LS_END); + if (IsSavegameVersionBefore(SLV_63)) return LS_END - 4; + if (IsSavegameVersionBefore(SLV_85)) return LS_END - 2; + if (IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH)) return LS_END; + /* Read from the savegame how long the list is. */ + return SlGetStructListLength(LS_END); + } + + void Save(CompanyProperties *c) const override + { + SlSetStructListLength(LS_END); + for (int i = 0; i < LS_END; i++) { + SlObject(&c->livery[i], this->GetDescription()); + } + } + + void Load(CompanyProperties *c) const override + { + size_t num_liveries = this->GetNumLiveries(); bool update_in_use = IsSavegameVersionBefore(SLV_GROUP_LIVERIES); - for (int i = 0; i < num_liveries; i++) { + for (size_t i = 0; i < num_liveries; i++) { SlObject(&c->livery[i], this->GetDescription()); if (update_in_use && i != LS_DEFAULT) { if (c->livery[i].in_use == 0) { @@ -426,10 +455,7 @@ public: } } - void Save(CompanyProperties *c) const override { this->GenericSaveLoad(c); } - void Load(CompanyProperties *c) const override { this->GenericSaveLoad(c); } - void LoadCheck(CompanyProperties *c) const override { this->GenericSaveLoad(c); } - void FixPointers(CompanyProperties *c) const override { this->GenericSaveLoad(c); } + void LoadCheck(CompanyProperties *c) const override { this->Load(c); } }; /* Save/load of companies */ @@ -467,7 +493,7 @@ static const SaveLoad _company_desc[] = SLE_ARR(CompanyProperties, share_owners, SLE_UINT8, 4), - SLE_VAR(CompanyProperties, num_valid_stat_ent, SLE_UINT8), + SLE_CONDVAR(CompanyProperties, num_valid_stat_ent, SLE_UINT8, SL_MIN_VERSION, SLV_SAVELOAD_LIST_LENGTH), SLE_VAR(CompanyProperties, months_of_bankruptcy, SLE_UINT8), SLE_CONDVAR(CompanyProperties, bankrupt_asked, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_104), diff --git a/src/saveload/game_sl.cpp b/src/saveload/game_sl.cpp --- a/src/saveload/game_sl.cpp +++ b/src/saveload/game_sl.cpp @@ -121,6 +121,8 @@ public: void Save(LanguageStrings *ls) const override { + SlSetStructListLength(ls->lines.size()); + for (const auto &string : ls->lines) { _game_saveload_string = string; SlObject(nullptr, this->GetDescription()); @@ -129,7 +131,9 @@ public: void Load(LanguageStrings *ls) const override { - for (uint32 i = 0; i < _game_saveload_strings; i++) { + uint32 length = IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) ? _game_saveload_strings : (uint32)SlGetStructListLength(UINT32_MAX); + + for (uint32 i = 0; i < length; i++) { SlObject(nullptr, this->GetDescription()); ls->lines.emplace_back(_game_saveload_string); } @@ -138,7 +142,7 @@ public: static const SaveLoad _game_language_desc[] = { SLE_SSTR(LanguageStrings, language, SLE_STR), - SLEG_VAR(_game_saveload_strings, SLE_UINT32), + SLEG_CONDVAR(_game_saveload_strings, SLE_UINT32, SL_MIN_VERSION, SLV_SAVELOAD_LIST_LENGTH), SLEG_STRUCTLIST(SlGameLanguageString), }; @@ -170,9 +174,7 @@ static void Save_GSTR() for (uint i = 0; i < _current_data->raw_strings.size(); i++) { SlSetArrayIndex(i); - LanguageStrings *ls = &_current_data->raw_strings[i]; - _game_saveload_strings = (uint32)ls->lines.size(); - SlObject(ls, _game_language_desc); + SlObject(&_current_data->raw_strings[i], _game_language_desc); } } diff --git a/src/saveload/linkgraph_sl.cpp b/src/saveload/linkgraph_sl.cpp --- a/src/saveload/linkgraph_sl.cpp +++ b/src/saveload/linkgraph_sl.cpp @@ -37,6 +37,12 @@ public: void Save(Node *bn) const override { + uint16 size = 0; + for (NodeID to = _linkgraph_from; to != INVALID_NODE; to = _linkgraph->edges[_linkgraph_from][to].next_edge) { + size++; + } + + SlSetStructListLength(size); for (NodeID to = _linkgraph_from; to != INVALID_NODE; to = _linkgraph->edges[_linkgraph_from][to].next_edge) { SlObject(&_linkgraph->edges[_linkgraph_from][to], this->GetDescription()); } @@ -54,11 +60,18 @@ public: return; } + size_t used_size = IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) ? max_size : SlGetStructListLength(UINT16_MAX); + /* ... but as that wasted a lot of space we save a sparse matrix now. */ for (NodeID to = _linkgraph_from; to != INVALID_NODE; to = _linkgraph->edges[_linkgraph_from][to].next_edge) { + if (used_size == 0) SlErrorCorrupt("Link graph structure overflow"); + used_size--; + if (to >= max_size) SlErrorCorrupt("Link graph structure overflow"); SlObject(&_linkgraph->edges[_linkgraph_from][to], this->GetDescription()); } + + if (!IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) && used_size > 0) SlErrorCorrupt("Corrupted link graph"); } }; @@ -77,6 +90,7 @@ public: { _linkgraph = lg; + SlSetStructListLength(lg->Size()); for (NodeID from = 0; from < lg->Size(); ++from) { _linkgraph_from = from; SlObject(&lg->nodes[from], this->GetDescription()); @@ -87,8 +101,9 @@ public: { _linkgraph = lg; - lg->Init(_num_nodes); - for (NodeID from = 0; from < _num_nodes; ++from) { + uint16 length = IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) ? _num_nodes : (uint16)SlGetStructListLength(UINT16_MAX); + lg->Init(length); + for (NodeID from = 0; from < length; ++from) { _linkgraph_from = from; SlObject(&lg->nodes[from], this->GetDescription()); } @@ -103,7 +118,7 @@ SaveLoadTable GetLinkGraphDesc() { static const SaveLoad link_graph_desc[] = { SLE_VAR(LinkGraph, last_compression, SLE_INT32), - SLEG_VAR(_num_nodes, SLE_UINT16), + SLEG_CONDVAR(_num_nodes, SLE_UINT16, SL_MIN_VERSION, SLV_SAVELOAD_LIST_LENGTH), SLE_VAR(LinkGraph, cargo, SLE_UINT8), SLEG_STRUCTLIST(SlLinkgraphNode), }; @@ -227,8 +242,6 @@ void AfterLoadLinkGraphs() static void Save_LGRP() { for (LinkGraph *lg : LinkGraph::Iterate()) { - _num_nodes = lg->Size(); - SlSetArrayIndex(lg->index); SlObject(lg, GetLinkGraphDesc()); } @@ -252,8 +265,6 @@ static void Load_LGRP() static void Save_LGRJ() { for (LinkGraphJob *lgj : LinkGraphJob::Iterate()) { - _num_nodes = lgj->Size(); - SlSetArrayIndex(lgj->index); SlObject(lgj, GetLinkGraphJobDesc()); } diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp --- a/src/saveload/saveload.cpp +++ b/src/saveload/saveload.cpp @@ -1590,6 +1590,34 @@ static bool SlObjectMember(void *object, } /** + * Set the length of this list. + * @param The length of the list. + */ +void SlSetStructListLength(size_t length) +{ + /* Automatically calculate the length? */ + if (_sl.need_length != NL_NONE) { + SlSetLength(SlGetArrayLength(length)); + if (_sl.need_length == NL_CALCLENGTH) return; + } + + SlWriteArrayLength(length); +} + +/** + * Get the length of this list; if it exceeds the limit, error out. + * @param limit The maximum size the list can be. + * @return The length of the list. + */ +size_t SlGetStructListLength(size_t limit) +{ + size_t length = SlReadArrayLength(); + if (length > limit) SlErrorCorrupt("List exceeds storage size"); + + return length; +} + +/** * Main SaveLoad function. * @param object The object that is being saved or loaded. * @param slt The SaveLoad table with objects to save/load. diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -326,9 +326,11 @@ enum SaveLoadVersion : uint16 { SLV_GS_INDUSTRY_CONTROL, ///< 287 PR#7912 and PR#8115 GS industry control. SLV_VEH_MOTION_COUNTER, ///< 288 PR#8591 Desync safe motion counter SLV_INDUSTRY_TEXT, ///< 289 PR#8576 v1.11.0-RC1 Additional GS text for industries. + SLV_MAPGEN_SETTINGS_REVAMP, ///< 290 PR#8891 v1.11 Revamp of some mapgen settings (snow coverage, desert coverage, heightmap height, custom terrain type). SLV_GROUP_REPLACE_WAGON_REMOVAL, ///< 291 PR#7441 Per-group wagon removal flag. SLV_CUSTOM_SUBSIDY_DURATION, ///< 292 PR#9081 Configurable subsidy duration. + SLV_SAVELOAD_LIST_LENGTH, ///< 293 PR#9374 Consistency in list length with SL_STRUCT / SL_STRUCTLIST / SL_DEQUE / SL_REFLIST. SL_MAX_VERSION, ///< Highest possible saveload version }; @@ -992,6 +994,9 @@ void WriteValue(void *ptr, VarType conv, void SlSetArrayIndex(uint index); int SlIterateArray(); +void SlSetStructListLength(size_t length); +size_t SlGetStructListLength(size_t limit); + void SlAutolength(AutolengthProc *proc, void *arg); size_t SlGetFieldLength(); void SlSetLength(size_t length); diff --git a/src/saveload/station_sl.cpp b/src/saveload/station_sl.cpp --- a/src/saveload/station_sl.cpp +++ b/src/saveload/station_sl.cpp @@ -157,14 +157,14 @@ static const SaveLoad _roadstop_desc[] = }; static uint16 _waiting_acceptance; -static uint32 _num_flows; +static uint32 _old_num_flows; static uint16 _cargo_source; static uint32 _cargo_source_xy; static uint8 _cargo_days; static Money _cargo_feeder_share; std::list _packets; -uint32 _num_dests; +uint32 _old_num_dests; struct FlowSaveLoad { FlowSaveLoad() : source(0), via(0), share(0), restricted(false) {} @@ -209,6 +209,7 @@ public: void Save(BaseStation *bst) const override { + SlSetStructListLength(bst->num_specs); for (uint i = 0; i < bst->num_specs; i++) { SlObject(&bst->speclist[i], this->GetDescription()); } @@ -216,6 +217,10 @@ public: void Load(BaseStation *bst) const override { + if (!IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH)) { + bst->num_specs = (uint8)SlGetStructListLength(UINT8_MAX); + } + if (bst->num_specs != 0) { /* Allocate speclist memory when loading a game */ bst->speclist = CallocT(bst->num_specs); @@ -235,6 +240,7 @@ public: void Save(GoodsEntry *ge) const override { + SlSetStructListLength(ge->cargo.Packets()->MapSize()); for (StationCargoPacketMap::ConstMapIterator it(ge->cargo.Packets()->begin()); it != ge->cargo.Packets()->end(); ++it) { SlObject(const_cast(&(*it)), this->GetDescription()); } @@ -242,8 +248,10 @@ public: void Load(GoodsEntry *ge) const override { + size_t num_dests = IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) ? _old_num_dests : SlGetStructListLength(UINT32_MAX); + StationCargoPair pair; - for (uint j = 0; j < _num_dests; ++j) { + for (uint j = 0; j < num_dests; ++j) { SlObject(&pair, this->GetDescription()); const_cast(*(ge->cargo.Packets()))[pair.first].swap(pair.second); assert(pair.second.empty()); @@ -269,6 +277,12 @@ public: void Save(GoodsEntry *ge) const override { + uint32 num_flows = 0; + for (FlowStatMap::const_iterator it(ge->flows.begin()); it != ge->flows.end(); ++it) { + num_flows += (uint32)it->second.GetShares()->size(); + } + SlSetStructListLength(num_flows); + for (FlowStatMap::const_iterator outer_it(ge->flows.begin()); outer_it != ge->flows.end(); ++outer_it) { const FlowStat::SharesMap *shares = outer_it->second.GetShares(); uint32 sum_shares = 0; @@ -287,10 +301,12 @@ public: void Load(GoodsEntry *ge) const override { + size_t num_flows = IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) ? _old_num_flows : SlGetStructListLength(UINT32_MAX); + FlowSaveLoad flow; FlowStat *fs = nullptr; StationID prev_source = INVALID_STATION; - for (uint32 j = 0; j < _num_flows; ++j) { + for (uint32 j = 0; j < num_flows; ++j) { SlObject(&flow, this->GetDescription()); if (fs == nullptr || prev_source != flow.source) { fs = &(ge->flows.insert(std::make_pair(flow.source, FlowStat(flow.via, flow.share, flow.restricted))).first->second); @@ -330,11 +346,11 @@ public: SLEG_CONDVAR( _cargo_feeder_share, SLE_INT64, SLV_65, SLV_68), SLE_CONDVAR(GoodsEntry, amount_fract, SLE_UINT8, SLV_150, SL_MAX_VERSION), SLEG_CONDREFLIST( _packets, REF_CARGO_PACKET, SLV_68, SLV_183), - SLEG_CONDVAR( _num_dests, SLE_UINT32, SLV_183, SL_MAX_VERSION), + SLEG_CONDVAR( _old_num_dests, SLE_UINT32, SLV_183, SLV_SAVELOAD_LIST_LENGTH), SLE_CONDVAR(GoodsEntry, cargo.reserved_count, SLE_UINT, SLV_181, SL_MAX_VERSION), SLE_CONDVAR(GoodsEntry, link_graph, SLE_UINT16, SLV_183, SL_MAX_VERSION), SLE_CONDVAR(GoodsEntry, node, SLE_UINT16, SLV_183, SL_MAX_VERSION), - SLEG_CONDVAR( _num_flows, SLE_UINT32, SLV_183, SL_MAX_VERSION), + SLEG_CONDVAR( _old_num_flows, SLE_UINT32, SLV_183, SLV_SAVELOAD_LIST_LENGTH), SLE_CONDVAR(GoodsEntry, max_waiting_cargo, SLE_UINT32, SLV_183, SL_MAX_VERSION), SLEG_CONDSTRUCTLIST(SlStationFlow, SLV_183, SL_MAX_VERSION), SLEG_CONDSTRUCTLIST(SlStationCargo, SLV_183, SL_MAX_VERSION), @@ -344,15 +360,26 @@ public: } #endif + /** + * Get the number of cargoes used by this savegame version. + * @return The number of cargoes used by this savegame version. + */ + size_t GetNumCargo() const + { + if (IsSavegameVersionBefore(SLV_55)) return 12; + if (IsSavegameVersionBefore(SLV_EXTEND_CARGOTYPES)) return 32; + if (IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH)) return NUM_CARGO; + /* Read from the savegame how long the list is. */ + return SlGetStructListLength(NUM_CARGO); + } + void Save(BaseStation *bst) const override { Station *st = Station::From(bst); + + SlSetStructListLength(NUM_CARGO); + for (CargoID i = 0; i < NUM_CARGO; i++) { - _num_dests = (uint32)st->goods[i].cargo.Packets()->MapSize(); - _num_flows = 0; - for (FlowStatMap::const_iterator it(st->goods[i].flows.begin()); it != st->goods[i].flows.end(); ++it) { - _num_flows += (uint32)it->second.GetShares()->size(); - } SlObject(&st->goods[i], this->GetDescription()); } } @@ -369,7 +396,7 @@ public: memcpy(st->airport.psa->storage, _old_st_persistent_storage.storage, sizeof(_old_st_persistent_storage.storage)); } - uint num_cargo = IsSavegameVersionBefore(SLV_55) ? 12 : IsSavegameVersionBefore(SLV_EXTEND_CARGOTYPES) ? 32 : NUM_CARGO; + size_t num_cargo = this->GetNumCargo(); for (CargoID i = 0; i < num_cargo; i++) { GoodsEntry *ge = &st->goods[i]; SlObject(ge, this->GetDescription()); @@ -520,7 +547,7 @@ public: /* Used by newstations for graphic variations */ SLE_VAR(BaseStation, random_bits, SLE_UINT16), SLE_VAR(BaseStation, waiting_triggers, SLE_UINT8), - SLE_VAR(BaseStation, num_specs, SLE_UINT8), + SLE_CONDVAR(BaseStation, num_specs, SLE_UINT8, SL_MIN_VERSION, SLV_SAVELOAD_LIST_LENGTH), }; void GenericSaveLoad(BaseStation *bst) const @@ -626,7 +653,7 @@ static void Save_STNN() static void Load_STNN() { - _num_flows = 0; + _old_num_flows = 0; int index; while ((index = SlIterateArray()) != -1) { diff --git a/src/saveload/town_sl.cpp b/src/saveload/town_sl.cpp --- a/src/saveload/town_sl.cpp +++ b/src/saveload/town_sl.cpp @@ -122,8 +122,21 @@ public: SLE_CONDVAR(TransportedCargoStat, new_act, SLE_UINT32, SLV_165, SL_MAX_VERSION), }; + /** + * Get the number of cargoes used by this savegame version. + * @return The number of cargoes used by this savegame version. + */ + size_t GetNumCargo() const + { + if (IsSavegameVersionBefore(SLV_EXTEND_CARGOTYPES)) return 32; + if (IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH)) return NUM_CARGO; + /* Read from the savegame how long the list is. */ + return SlGetStructListLength(NUM_CARGO); + } + void Save(Town *t) const override { + SlSetStructListLength(NUM_CARGO); for (CargoID i = 0; i < NUM_CARGO; i++) { SlObject(&t->supplied[i], this->GetDescription()); } @@ -131,7 +144,7 @@ public: void Load(Town *t) const override { - uint num_cargo = IsSavegameVersionBefore(SLV_EXTEND_CARGOTYPES) ? 32 : NUM_CARGO; + size_t num_cargo = this->GetNumCargo(); for (CargoID i = 0; i < num_cargo; i++) { SlObject(&t->supplied[i], this->GetDescription()); } @@ -149,14 +162,16 @@ public: void Save(Town *t) const override { - for (int i = TE_BEGIN; i < TE_END; i++) { + SlSetStructListLength(NUM_TE); + for (size_t i = TE_BEGIN; i < TE_END; i++) { SlObject(&t->received[i], this->GetDescription()); } } void Load(Town *t) const override { - for (int i = TE_BEGIN; i < TE_END; i++) { + size_t length = IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) ? (size_t)TE_END : SlGetStructListLength(TE_END); + for (size_t i = 0; i < length; i++) { SlObject(&t->received[i], this->GetDescription()); } }