summaryrefslogtreecommitdiff
path: root/src/saveload
diff options
context:
space:
mode:
authorPatric Stout <truebrain@openttd.org>2021-06-06 23:23:12 +0200
committerPatric Stout <github@truebrain.nl>2021-06-15 16:45:04 +0200
commita146bcfe93a6c913781cfe471166359e744195d7 (patch)
tree1c4d5065705dfe540620ca974214bf36664a5361 /src/saveload
parentd0bcb9839a9080326ade985e4b109752fd88e2a6 (diff)
downloadopenttd-a146bcfe93a6c913781cfe471166359e744195d7.tar.xz
Change: store length of SL_STRUCTLIST in the savegame
This wasn't consistently done, and often variables were used that were read by an earlier blob. By moving it next to the struct itself, the code becomes a bit more self-contained and easier to read. Additionally, this allows for external tooling to know how many structs to expect, instead of having to know where to find the length-field or a hard-coded value that can change at any moment.
Diffstat (limited to 'src/saveload')
-rw-r--r--src/saveload/company_sl.cpp52
-rw-r--r--src/saveload/game_sl.cpp12
-rw-r--r--src/saveload/linkgraph_sl.cpp25
-rw-r--r--src/saveload/saveload.cpp28
-rw-r--r--src/saveload/saveload.h5
-rw-r--r--src/saveload/station_sl.cpp55
-rw-r--r--src/saveload/town_sl.cpp21
7 files changed, 156 insertions, 42 deletions
diff --git a/src/saveload/company_sl.cpp b/src/saveload/company_sl.cpp
index 550816a07..880fc9e89 100644
--- 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<SlCompanyLiveries, CompanyProperties> {
@@ -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
index af01d5bd0..76a81e167 100644
--- 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
index cf0292e6e..fc72d4383 100644
--- 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
index cb28fb6d2..3ca10b527 100644
--- a/src/saveload/saveload.cpp
+++ b/src/saveload/saveload.cpp
@@ -1590,6 +1590,34 @@ static bool SlObjectMember(void *object, const SaveLoad &sld)
}
/**
+ * 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
index 7b0127709..42d14c785 100644
--- 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, int64 val);
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
index 4421e0f9f..7144c5de4 100644
--- 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<CargoPacket *> _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<StationSpecList>(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<StationCargoPacketMap::value_type *>(&(*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<StationCargoPacketMap &>(*(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
index 4151e3c75..38c000e3b 100644
--- a/src/saveload/town_sl.cpp
+++ b/src/saveload/town_sl.cpp
@@ -122,8 +122,21 @@ public:
SLE_CONDVAR(TransportedCargoStat<uint32>, 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());
}
}