diff --git a/src/cargopacket.h b/src/cargopacket.h index e1604fcd12..98b622699b 100644 --- a/src/cargopacket.h +++ b/src/cargopacket.h @@ -582,7 +582,7 @@ public: /** The super class ought to know what it's doing. */ friend class CargoList; /** The stations, via GoodsEntry, have a CargoList. */ - friend SaveLoadTable GetGoodsDesc(); + friend NamedSaveLoadTable GetGoodsDesc(); friend upstream_sl::SlStationGoods; friend class CargoLoad; diff --git a/src/sl/extended_ver_sl.cpp b/src/sl/extended_ver_sl.cpp index 78c9a4cfb0..7190303317 100644 --- a/src/sl/extended_ver_sl.cpp +++ b/src/sl/extended_ver_sl.cpp @@ -219,6 +219,7 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = { { XSLFI_TABLE_SCRIPT_SL, XSCF_NULL, 1, 1, "table_script_sl", nullptr, nullptr, nullptr }, { XSLFI_TABLE_NEWGRF_SL, XSCF_NULL, 2, 2, "table_newgrf_sl", nullptr, nullptr, nullptr }, { XSLFI_TABLE_INDUSTRY_SL, XSCF_NULL, 2, 2, "table_industry_sl", nullptr, nullptr, nullptr }, + { XSLFI_TABLE_STATION_SL, XSCF_NULL, 1, 1, "table_station_sl", nullptr, nullptr, nullptr }, { XSLFI_NULL, XSCF_NULL, 0, 0, nullptr, nullptr, nullptr, nullptr }, // This is the end marker }; diff --git a/src/sl/extended_ver_sl.h b/src/sl/extended_ver_sl.h index 1964c3b04a..9924059f47 100644 --- a/src/sl/extended_ver_sl.h +++ b/src/sl/extended_ver_sl.h @@ -174,6 +174,8 @@ enum SlXvFeatureIndex { XSLFI_TABLE_INDUSTRY_SL, ///< Use table format for industry chunks: ///< v1: IBLD, ITBL ///< v2: INDY + XSLFI_TABLE_STATION_SL, ///< Use table format for station chunks: + ///< v1: STNN XSLFI_RIFF_HEADER_60_BIT, ///< Size field in RIFF chunk header is 60 bit XSLFI_HEIGHT_8_BIT, ///< Map tile height is 8 bit instead of 4 bit, but savegame version may be before this became true in trunk diff --git a/src/sl/saveload.cpp b/src/sl/saveload.cpp index 32f09476bb..8395dbec25 100644 --- a/src/sl/saveload.cpp +++ b/src/sl/saveload.cpp @@ -1898,7 +1898,6 @@ size_t SlCalcObjMemberLength(const void *object, const SaveLoad &sld) break; case SL_WRITEBYTE: return 1; // a uint8_t is logically of size 1 case SL_VEH_INCLUDE: return SlCalcObjLength(object, GetVehicleDescription(VEH_END)); - case SL_ST_INCLUDE: return SlCalcObjLength(object, GetBaseStationDescription()); case SL_STRUCT: case SL_STRUCTLIST: @@ -1968,8 +1967,8 @@ static void SlFilterObjectMember(const SaveLoad &sld, std::vector &sav SlFilterObject(GetVehicleDescription(VEH_END), save); break; - case SL_ST_INCLUDE: - SlFilterObject(GetBaseStationDescription(), save); + case SL_INCLUDE: + sld.include_functor(save); break; default: NOT_REACHED(); @@ -2133,10 +2132,6 @@ bool SlObjectMemberGeneric(void *object, const SaveLoad &sld) SlObject(ptr, GetVehicleDescription(VEH_END)); break; - case SL_ST_INCLUDE: - SlObject(ptr, GetBaseStationDescription()); - break; - default: NOT_REACHED(); } return true; diff --git a/src/sl/saveload.h b/src/sl/saveload.h index ca0e97d120..34014df9dd 100644 --- a/src/sl/saveload.h +++ b/src/sl/saveload.h @@ -719,7 +719,9 @@ inline constexpr void *SlVarWrapper(void* ptr) #define SLE_WRITEBYTE(base, variable) SLE_GENERAL(SL_WRITEBYTE, base, variable, 0, 0, SL_MIN_VERSION, SL_MAX_VERSION) #define SLE_VEH_INCLUDE() SaveLoad { false, SL_VEH_INCLUDE, 0, 0, SL_MIN_VERSION, SL_MAX_VERSION, SLTAG_DEFAULT, { nullptr }, SlXvFeatureTest()} -#define SLE_ST_INCLUDE() SaveLoad { false, SL_ST_INCLUDE, 0, 0, SL_MIN_VERSION, SL_MAX_VERSION, SLTAG_DEFAULT, { nullptr }, SlXvFeatureTest()} + +/** SaveLoad include, for non-table use with SlFilterObject/SlFilterNamedSaveLoadTable. */ +#define SLE_INCLUDE(inc_functor) SaveLoad { false, SL_INCLUDE, 0, 0, SL_MIN_VERSION, SL_MAX_VERSION, SLTAG_DEFAULT, { .include_functor = inc_functor }, SlXvFeatureTest()} /** * Storage of global simple variables, references (pointers), and arrays. @@ -987,6 +989,7 @@ size_t SlCalcObjLength(const void *object, const SaveLoadTable &slt); uint SlReadSimpleGamma(); void SlWriteSimpleGamma(size_t i); uint SlGetGammaLength(size_t i); +constexpr uint SlGetMaxGammaLength() { return 5; } /** * Run proc, automatically prepending the written length @@ -1084,6 +1087,7 @@ void SlObject(void *object, const SaveLoadTable &slt); bool SlObjectMember(void *object, const SaveLoad &sld); std::vector SlFilterObject(const SaveLoadTable &slt); +void SlFilterNamedSaveLoadTable(const NamedSaveLoadTable &nslt, std::vector &save); std::vector SlFilterNamedSaveLoadTable(const NamedSaveLoadTable &nslt); void SlObjectSaveFiltered(void *object, const SaveLoadTable &slt); void SlObjectLoadFiltered(void *object, const SaveLoadTable &slt); diff --git a/src/sl/saveload_internal.h b/src/sl/saveload_internal.h index fe8baaccac..da26989bca 100644 --- a/src/sl/saveload_internal.h +++ b/src/sl/saveload_internal.h @@ -23,7 +23,6 @@ void ResetOldNames(); void ResetOldWaypoints(); void MoveBuoysToWaypoints(); void MoveWaypointsToBaseStations(); -SaveLoadTable GetBaseStationDescription(); void AfterLoadVehiclesPhase1(bool part_of_load); void AfterLoadVehiclesPhase2(bool part_of_load); diff --git a/src/sl/saveload_types.h b/src/sl/saveload_types.h index 49c529186f..56e2fb4a65 100644 --- a/src/sl/saveload_types.h +++ b/src/sl/saveload_types.h @@ -120,12 +120,13 @@ enum SaveLoadTypes { /* non-normal save-load types */ SL_WRITEBYTE, SL_VEH_INCLUDE, - SL_ST_INCLUDE, + SL_INCLUDE, }; typedef uint8_t SaveLoadType; ///< Save/load type. @see SaveLoadTypes using SaveLoadStructHandlerFactory = std::unique_ptr (*)(); +using SaveLoadIncludeFunctor = void (*)(std::vector &); /** SaveLoad type struct. Do NOT use this directly but use the SLE_ macros defined just below! */ struct SaveLoad { @@ -144,6 +145,7 @@ struct SaveLoad { * that is called to save it. address: global=true, offset: global=false */ void *address; ///< address of variable OR offset of variable in the struct (max offset is 65536) SaveLoadStructHandlerFactory struct_handler_factory; ///< factory function pointer for SaveLoadStructHandler + SaveLoadIncludeFunctor include_functor; ///< include functor for SL_INCLUDE }; SlXvFeatureTest ext_feature_test; ///< extended feature test @@ -162,6 +164,7 @@ enum SaveLoadTags { SLTAG_CUSTOM_START, SLTAG_CUSTOM_0 = SLTAG_CUSTOM_START, SLTAG_CUSTOM_1, + SLTAG_CUSTOM_2, }; enum NamedSaveLoadFlags : uint8_t { @@ -215,6 +218,12 @@ inline constexpr NamedSaveLoad NSLT_STRUCTLIST(const char *name, SaveLoadVersion return NSLT_STRUCTLIST(name, factory, from, to, extver); } +inline constexpr NamedSaveLoad NSLTAG(uint16_t label_tag, NamedSaveLoad nsl) +{ + nsl.save_load.label_tag = label_tag; + return nsl; +} + struct SaveLoadTableData : public std::vector { std::vector> struct_handlers; }; diff --git a/src/sl/station_sl.cpp b/src/sl/station_sl.cpp index 3359e28c8a..316350db34 100644 --- a/src/sl/station_sl.cpp +++ b/src/sl/station_sl.cpp @@ -28,6 +28,7 @@ static uint8_t _num_roadstop_specs; static uint32_t _num_roadstop_custom_tiles; static std::vector _custom_road_stop_tiles; static std::vector _custom_road_stop_data; +static std::vector _station_history_data_dummy; /** * Update the buoy orders to be waypoint orders. @@ -223,27 +224,28 @@ static uint8_t _cargo_periods; static Money _cargo_feeder_share; static uint _cargo_reserved_count; -static const SaveLoad _station_speclist_desc[] = { - SLE_CONDVAR(StationSpecList, grfid, SLE_UINT32, SLV_27, SL_MAX_VERSION), - SLE_CONDVAR_X(StationSpecList, localidx, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_NEWGRF_ENTITY_EXTRA, 0, 1)), - SLE_CONDVAR_X(StationSpecList, localidx, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_NEWGRF_ENTITY_EXTRA, 2)), +static const NamedSaveLoad _station_speclist_desc[] = { + NSL("grfid", SLE_CONDVAR(StationSpecList, grfid, SLE_UINT32, SLV_27, SL_MAX_VERSION)), + NSL("localidx", SLE_CONDVAR_X(StationSpecList, localidx, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_NEWGRF_ENTITY_EXTRA, 0, 1))), + NSL("localidx", SLE_CONDVAR_X(StationSpecList, localidx, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_NEWGRF_ENTITY_EXTRA, 2))), }; -static const SaveLoad _roadstop_speclist_desc[] = { - SLE_CONDVAR(RoadStopSpecList, grfid, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION), - SLE_CONDVAR_X(RoadStopSpecList, localidx, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_GRF_ROADSTOPS, 0, 2)), - SLE_CONDVAR_X(RoadStopSpecList, localidx, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_GRF_ROADSTOPS, 3)), +static const NamedSaveLoad _roadstop_speclist_desc[] = { + NSL("grfid", SLE_CONDVAR(RoadStopSpecList, grfid, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION)), + NSL("localidx", SLE_CONDVAR_X(RoadStopSpecList, localidx, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_GRF_ROADSTOPS, 0, 2))), + NSL("localidx", SLE_CONDVAR_X(RoadStopSpecList, localidx, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_GRF_ROADSTOPS, 3))), }; CargoPacketList _packets; uint32_t _num_dests; struct FlowSaveLoad { - FlowSaveLoad() : source(0), via(0), share(0), restricted(false) {} + FlowSaveLoad() : source(0), via(0), share(0), restricted(false), flags(0) {} StationID source; StationID via; uint32_t share; bool restricted; + uint16_t flags; }; #if 0 @@ -255,50 +257,195 @@ static const SaveLoad _flow_desc[] = { }; #endif +static const NamedSaveLoad _inner_flow_desc[] = { + NSL("via", SLTAG(SLTAG_CUSTOM_0, SLE_VAR(FlowSaveLoad, via, SLE_UINT16))), + NSL("share", SLTAG(SLTAG_CUSTOM_1, SLE_VAR(FlowSaveLoad, share, SLE_UINT32))), + NSL("restricted", SLTAG(SLTAG_CUSTOM_2, SLE_CONDVAR(FlowSaveLoad, restricted, SLE_BOOL, SLV_187, SL_MAX_VERSION))), +}; + +struct StationGoodsInnerFlowStructHandler final : public HeaderOnlySaveLoadStructHandler { + NamedSaveLoadTable GetDescription() const override + { + return _inner_flow_desc; + } + + void LoadedTableDescription() override + { + SaveLoadTable slt = this->GetLoadDescription(); + if (slt.size() != 3 || slt[0].label_tag != SLTAG_CUSTOM_0 || slt[1].label_tag != SLTAG_CUSTOM_1 || slt[2].label_tag != SLTAG_CUSTOM_2) { + SlErrorCorrupt("Station goods flow inner sub-chunk fields not as expected"); + } + } +}; + +static const NamedSaveLoad _outer_flow_desc[] = { + NSL("source", SLTAG(SLTAG_CUSTOM_0, SLE_VAR(FlowSaveLoad, source, SLE_UINT16))), + NSL("flags", SLTAG(SLTAG_CUSTOM_1, SLE_VAR(FlowSaveLoad, flags, SLE_UINT16))), + NSLTAG(SLTAG_CUSTOM_2, NSLT_STRUCTLIST("flow")), +}; + +struct StationGoodsFlowStructHandler final : public TypedSaveLoadStructHandler { + NamedSaveLoadTable GetDescription() const override + { + return _outer_flow_desc; + } + + void Save(GoodsEntry *ge) const override + { + const GoodsEntryData *ged = ge->data.get(); + if (ged == nullptr) { + SlSetStructListLength(0); + return; + } + + MemoryDumper *dumper = MemoryDumper::GetCurrent(); + + SlSetStructListLength(ged->flows.size()); + + for (const FlowStat &stat : ged->flows) { + uint32_t sum_shares = 0; + dumper->CheckBytes(2 + 2); + dumper->RawWriteUint16(stat.GetOrigin()); + dumper->RawWriteUint16(stat.GetRawFlags()); + SlWriteSimpleGamma(stat.size()); + for (const auto &it : stat) { + StationID via = it.second; + uint32_t share = it.first - sum_shares; + bool restricted = it.first > stat.GetUnrestricted(); + sum_shares = it.first; + dbg_assert(share > 0); + + /* This is performance-sensitive, manually unroll */ + dumper->CheckBytes(2 + 4 + 1); + dumper->RawWriteUint16(via); + dumper->RawWriteUint32(share); + dumper->RawWriteByte(restricted ? 1 : 0); + } + } + } + + void Load(GoodsEntry *ge) const override + { + ReadBuffer *buffer = ReadBuffer::GetCurrent(); + + uint num_flows = SlGetStructListLength(UINT32_MAX); + + FlowStatMap &flows = ge->data->flows; + flows.reserve(num_flows); + for (uint32_t j = 0; j < num_flows; ++j) { + buffer->CheckBytes(2 + 2); + StationID source = buffer->RawReadUint16(); + uint16_t flags = buffer->RawReadUint16(); + uint32_t flow_count = SlReadSimpleGamma(); + + buffer->CheckBytes(2 + 4 + 1); + StationID via = buffer->RawReadUint16(); + uint32_t share = buffer->RawReadUint32(); + bool restricted = (buffer->RawReadByte() != 0); + FlowStat &fs = *(flows.insert(flows.end(), FlowStat(source, via, share, restricted))); + fs.SetRawFlags(flags); + for (uint32_t k = 1; k < flow_count; ++k) { + buffer->CheckBytes(2 + 4 + 1); + via = buffer->RawReadUint16(); + share = buffer->RawReadUint32(); + restricted = (buffer->RawReadByte() != 0); + fs.AppendShare(via, share, restricted); + } + } + } + + void LoadedTableDescription() override + { + if (!SlXvIsFeaturePresent(XSLFI_FLOW_STAT_FLAGS)) { + SlErrorCorrupt("XSLFI_FLOW_STAT_FLAGS unexpectedly not present"); + } + SaveLoadTable slt = this->GetLoadDescription(); + if (slt.size() != 3 || slt[0].label_tag != SLTAG_CUSTOM_0 || slt[1].label_tag != SLTAG_CUSTOM_1 || slt[2].label_tag != SLTAG_CUSTOM_2) { + SlErrorCorrupt("Station goods flow outer sub-chunk fields not as expected"); + } + } +}; + +typedef std::pair StationCargoPair; + +static const NamedSaveLoad _cargo_list_desc[] = { + NSL("first", SLE_VAR(StationCargoPair, first, SLE_UINT16)), + NSL("second", SLE_PTRRING(StationCargoPair, second, REF_CARGO_PACKET)), +}; + +struct StationGoodsCargoStructHandler final : public TypedSaveLoadStructHandler { + NamedSaveLoadTable GetDescription() const override + { + return _cargo_list_desc; + } + + void Save(GoodsEntry *ge) const override + { + const GoodsEntryData *ged = ge->data.get(); + if (ged == nullptr) { + SlSetStructListLength(0); + return; + } + + SlSetStructListLength(ged->cargo.Packets()->MapSize()); + for (StationCargoPacketMap::ConstMapIterator it(ged->cargo.Packets()->begin()); it != ged->cargo.Packets()->end(); ++it) { + SlObjectSaveFiltered(const_cast(&(*it)), this->GetLoadDescription()); + } + } + + void Load(GoodsEntry *ge) const override + { + uint num_dests = SlGetStructListLength(UINT32_MAX); + + StationCargoPair pair; + for (uint j = 0; j < num_dests; ++j) { + SlObjectLoadFiltered(&pair, this->GetLoadDescription()); + const_cast(*(ge->data->cargo.Packets()))[pair.first].swap(pair.second); + assert(pair.second.empty()); + } + } +}; + /** * Wrapper function to get the GoodsEntry's internal structure while * some of the variables itself are private. * @return the saveload description for GoodsEntry. */ -SaveLoadTable GetGoodsDesc() +NamedSaveLoadTable GetGoodsDesc() { - static const SaveLoad goods_desc[] = { - SLEG_CONDVAR( _waiting_acceptance, SLE_UINT16, SL_MIN_VERSION, SLV_68), - SLE_CONDVAR(GoodsEntry, status, SLE_UINT8, SLV_68, SL_MAX_VERSION), - SLE_CONDNULL(2, SLV_51, SLV_68), - SLE_VAR(GoodsEntry, time_since_pickup, SLE_UINT8), - SLE_CONDNULL_X(6, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP, 4)), - SLE_VAR(GoodsEntry, rating, SLE_UINT8), - SLEG_CONDVAR( _cargo_source, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_7), - SLEG_CONDVAR( _cargo_source, SLE_UINT16, SLV_7, SLV_68), - SLEG_CONDVAR( _cargo_source_xy, SLE_UINT32, SLV_44, SLV_68), - SLEG_CONDVAR( _cargo_periods, SLE_UINT8, SL_MIN_VERSION, SLV_68), - SLE_VAR(GoodsEntry, last_speed, SLE_UINT8), - SLE_VAR(GoodsEntry, last_age, SLE_UINT8), - SLEG_CONDVAR( _cargo_feeder_share, SLE_FILE_U32 | SLE_VAR_I64, SLV_14, SLV_65), - SLEG_CONDVAR( _cargo_feeder_share, SLE_INT64, SLV_65, SLV_68), - SLE_CONDVAR(GoodsEntry, amount_fract, SLE_UINT8, SLV_150, SL_MAX_VERSION), - SLEG_CONDPTRRING_X( _packets, REF_CARGO_PACKET, SLV_68, SLV_183, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, 0, 0)), - SLEG_CONDVAR_X( _num_dests, SLE_UINT32, SLV_183, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_OR, XSLFI_CHILLPP)), - SLEG_CONDVAR( _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), - SLE_CONDVAR(GoodsEntry, max_waiting_cargo, SLE_UINT32, SLV_183, SL_MAX_VERSION), - SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP)), - SLE_CONDVAR_X(GoodsEntry, last_vehicle_type, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ST_LAST_VEH_TYPE, 1)), + static const NamedSaveLoad goods_desc[] = { + NSL("", SLEG_CONDVAR( _waiting_acceptance, SLE_UINT16, SL_MIN_VERSION, SLV_68)), + NSL("status", SLE_CONDVAR(GoodsEntry, status, SLE_UINT8, SLV_68, SL_MAX_VERSION)), + NSL("", SLE_CONDNULL(2, SLV_51, SLV_68)), + NSL("time_since_pickup", SLE_VAR(GoodsEntry, time_since_pickup, SLE_UINT8)), + NSL("", SLE_CONDNULL_X(6, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP, 4))), + NSL("rating", SLE_VAR(GoodsEntry, rating, SLE_UINT8)), + NSL("", SLEG_CONDVAR( _cargo_source, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_7)), + NSL("", SLEG_CONDVAR( _cargo_source, SLE_UINT16, SLV_7, SLV_68)), + NSL("", SLEG_CONDVAR( _cargo_source_xy, SLE_UINT32, SLV_44, SLV_68)), + NSL("", SLEG_CONDVAR( _cargo_periods, SLE_UINT8, SL_MIN_VERSION, SLV_68)), + NSL("last_speed", SLE_VAR(GoodsEntry, last_speed, SLE_UINT8)), + NSL("last_age", SLE_VAR(GoodsEntry, last_age, SLE_UINT8)), + NSL("", SLEG_CONDVAR( _cargo_feeder_share, SLE_FILE_U32 | SLE_VAR_I64, SLV_14, SLV_65)), + NSL("", SLEG_CONDVAR( _cargo_feeder_share, SLE_INT64, SLV_65, SLV_68)), + NSL("amount_fract", SLE_CONDVAR(GoodsEntry, amount_fract, SLE_UINT8, SLV_150, SL_MAX_VERSION)), + NSL("", SLEG_CONDPTRRING_X( _packets, REF_CARGO_PACKET, SLV_68, SLV_183, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, 0, 0))), + NSL("", SLEG_CONDVAR_X( _num_dests, SLE_UINT32, SLV_183, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_OR, XSLFI_CHILLPP))), + NSL("cargo.reserved_count", SLEG_CONDVAR( _cargo_reserved_count, SLE_UINT, SLV_181, SL_MAX_VERSION)), + NSL("link_graph", SLE_CONDVAR(GoodsEntry, link_graph, SLE_UINT16, SLV_183, SL_MAX_VERSION)), + NSL("node", SLE_CONDVAR(GoodsEntry, node, SLE_UINT16, SLV_183, SL_MAX_VERSION)), + NSL("", SLEG_CONDVAR( _num_flows, SLE_UINT32, SLV_183, SL_MAX_VERSION)), + NSL("max_waiting_cargo", SLE_CONDVAR(GoodsEntry, max_waiting_cargo, SLE_UINT32, SLV_183, SL_MAX_VERSION)), + NSL("", SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP))), + NSL("last_vehicle_type", SLE_CONDVAR_X(GoodsEntry, last_vehicle_type, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ST_LAST_VEH_TYPE, 1))), + + NSLT_STRUCTLIST("flow"), + NSLT_STRUCTLIST("cargo"), }; return goods_desc; } -typedef std::pair StationCargoPair; - -static const SaveLoad _cargo_list_desc[] = { - SLE_VAR(StationCargoPair, first, SLE_UINT16), - SLE_PTRRING(StationCargoPair, second, REF_CARGO_PACKET), -}; - /** * Swap the temporary packets with the packets without specific destination in * the given goods entry. Assert that at least one of those is empty. @@ -331,6 +478,9 @@ static void Load_STNS() _num_specs = 0; _cargo_reserved_count = 0; + std::vector goods_desc = SlFilterNamedSaveLoadTable(GetGoodsDesc()); + std::vector speclist_desc = SlFilterNamedSaveLoadTable(_station_speclist_desc); + uint num_cargo = IsSavegameVersionBefore(SLV_55) ? 12 : IsSavegameVersionBefore(SLV_EXTEND_CARGOTYPES) ? 32 : NUM_CARGO; int index; while ((index = SlIterateArray()) != -1) { @@ -342,7 +492,7 @@ static void Load_STNS() for (CargoID i = 0; i < num_cargo; i++) { GoodsEntry *ge = &st->goods[i]; - SlObject(ge, GetGoodsDesc()); + SlObjectLoadFiltered(ge, goods_desc); if (_cargo_reserved_count) ge->CreateData().cargo.LoadSetReservedCount(_cargo_reserved_count); SwapPackets(ge); if (IsSavegameVersionBefore(SLV_68)) { @@ -370,7 +520,7 @@ static void Load_STNS() /* Allocate speclist memory when loading a game */ st->speclist.resize(_num_specs); for (uint i = 0; i < st->speclist.size(); i++) { - SlObject(&st->speclist[i], _station_speclist_desc); + SlObjectLoadFiltered(&st->speclist[i], speclist_desc); } } } @@ -381,13 +531,15 @@ static void Ptrs_STNS() /* Don't run when savegame version is higher than or equal to 123. */ if (!IsSavegameVersionBefore(SLV_123)) return; + std::vector goods_desc = SlFilterNamedSaveLoadTable(GetGoodsDesc()); + uint num_cargo = IsSavegameVersionBefore(SLV_EXTEND_CARGOTYPES) ? 32 : NUM_CARGO; for (Station *st : Station::Iterate()) { if (!IsSavegameVersionBefore(SLV_68)) { for (CargoID i = 0; i < num_cargo; i++) { GoodsEntry *ge = &st->goods[i]; SwapPackets(ge); - SlObject(ge, GetGoodsDesc()); + SlObject(ge, goods_desc); SwapPackets(ge); } } @@ -396,177 +548,124 @@ static void Ptrs_STNS() } -static const SaveLoad _base_station_desc[] = { - SLE_VAR(BaseStation, xy, SLE_UINT32), - SLE_REF(BaseStation, town, REF_TOWN), - SLE_VAR(BaseStation, string_id, SLE_STRINGID), - SLE_STR(BaseStation, name, SLE_STR | SLF_ALLOW_CONTROL, 0), +static const NamedSaveLoad _base_station_desc[] = { + NSL("xy", SLE_VAR(BaseStation, xy, SLE_UINT32)), + NSL("town", SLE_REF(BaseStation, town, REF_TOWN)), + NSL("string_id", SLE_VAR(BaseStation, string_id, SLE_STRINGID)), + NSL("name", SLE_STR(BaseStation, name, SLE_STR | SLF_ALLOW_CONTROL, 0)), - SLE_CONDVAR_X(Station, delete_ctr, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP, 0, 3)), - SLE_CONDVAR_X(Station, delete_ctr, SLE_FILE_U16 | SLE_VAR_U8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP, 4)), - SLE_VAR(BaseStation, owner, SLE_UINT8), - SLE_VAR(BaseStation, facilities, SLE_UINT8), - SLE_VAR(BaseStation, build_date, SLE_INT32), + NSL("delete_ctr", SLE_CONDVAR_X(Station, delete_ctr, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP, 0, 3))), + NSL("delete_ctr", SLE_CONDVAR_X(Station, delete_ctr, SLE_FILE_U16 | SLE_VAR_U8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP, 4))), + NSL("owner", SLE_VAR(BaseStation, owner, SLE_UINT8)), + NSL("facilities", SLE_VAR(BaseStation, facilities, SLE_UINT8)), + NSL("build_date", SLE_VAR(BaseStation, build_date, SLE_INT32)), /* Used by newstations for graphic variations */ - SLE_VAR(BaseStation, random_bits, SLE_UINT16), - SLE_VAR(BaseStation, waiting_triggers, SLE_UINT8), - SLEG_VAR(_num_specs, SLE_UINT8), - SLEG_CONDVAR_X(_num_roadstop_specs, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_GRF_ROADSTOPS)), - SLEG_CONDVARVEC_X(_custom_road_stop_tiles, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_GRF_ROADSTOPS, 1, 1)), - SLEG_CONDVARVEC_X(_custom_road_stop_data, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_GRF_ROADSTOPS, 1, 1)), - SLEG_CONDVAR_X(_num_roadstop_custom_tiles, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_GRF_ROADSTOPS, 2)), + NSL("random_bits", SLE_VAR(BaseStation, random_bits, SLE_UINT16)), + NSL("waiting_triggers", SLE_VAR(BaseStation, waiting_triggers, SLE_UINT8)), + NSL("", SLEG_VAR(_num_specs, SLE_UINT8)), + NSL("", SLEG_CONDVAR_X(_num_roadstop_specs, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_GRF_ROADSTOPS))), + NSL("", SLEG_CONDVARVEC_X(_custom_road_stop_tiles, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_GRF_ROADSTOPS, 1, 1))), + NSL("", SLEG_CONDVARVEC_X(_custom_road_stop_data, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_GRF_ROADSTOPS, 1, 1))), + NSL("", SLEG_CONDVAR_X(_num_roadstop_custom_tiles, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_GRF_ROADSTOPS, 2))), }; -static OldPersistentStorage _old_st_persistent_storage; - -static const SaveLoad _station_desc[] = { - SLE_WRITEBYTE(Station, facilities), - SLE_ST_INCLUDE(), - - SLE_VAR(Station, train_station.tile, SLE_UINT32), - SLE_VAR(Station, train_station.w, SLE_FILE_U8 | SLE_VAR_U16), - SLE_VAR(Station, train_station.h, SLE_FILE_U8 | SLE_VAR_U16), - - SLE_REF(Station, bus_stops, REF_ROADSTOPS), - SLE_REF(Station, truck_stops, REF_ROADSTOPS), - SLE_CONDVAR_X(Station, ship_station.tile, SLE_UINT32, SL_MIN_VERSION, SLV_MULTITILE_DOCKS, SlXvFeatureTest(XSLFTO_AND, XSLFI_MULTIPLE_DOCKS, 0, 0)), - SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_MULTIPLE_DOCKS, 1, 1)), - SLE_CONDVAR(Station, ship_station.tile, SLE_UINT32, SLV_MULTITILE_DOCKS, SL_MAX_VERSION), - SLE_CONDVAR(Station, ship_station.w, SLE_FILE_U8 | SLE_VAR_U16, SLV_MULTITILE_DOCKS, SL_MAX_VERSION), - SLE_CONDVAR(Station, ship_station.h, SLE_FILE_U8 | SLE_VAR_U16, SLV_MULTITILE_DOCKS, SL_MAX_VERSION), - SLE_CONDVAR(Station, docking_station.tile, SLE_UINT32, SLV_MULTITILE_DOCKS, SL_MAX_VERSION), - SLE_CONDVAR(Station, docking_station.w, SLE_FILE_U8 | SLE_VAR_U16, SLV_MULTITILE_DOCKS, SL_MAX_VERSION), - SLE_CONDVAR(Station, docking_station.h, SLE_FILE_U8 | SLE_VAR_U16, SLV_MULTITILE_DOCKS, SL_MAX_VERSION), - SLE_CONDVARVEC_X(Station, docking_tiles, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_MULTIPLE_DOCKS, 2)), - SLE_VAR(Station, airport.tile, SLE_UINT32), - SLE_CONDVAR(Station, airport.w, SLE_FILE_U8 | SLE_VAR_U16, SLV_140, SL_MAX_VERSION), - SLE_CONDVAR(Station, airport.h, SLE_FILE_U8 | SLE_VAR_U16, SLV_140, SL_MAX_VERSION), - SLE_VAR(Station, airport.type, SLE_UINT8), - SLE_CONDVAR(Station, airport.layout, SLE_UINT8, SLV_145, SL_MAX_VERSION), - SLE_CONDNULL_X(1, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP, 1, 6)), - SLE_VAR(Station, airport.flags, SLE_UINT64), - SLE_CONDNULL_X(8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP, 1, 6)), - SLE_CONDVAR(Station, airport.rotation, SLE_UINT8, SLV_145, SL_MAX_VERSION), - SLEG_CONDARR(_old_st_persistent_storage.storage, SLE_UINT32, 16, SLV_145, SLV_161), - SLE_CONDREF(Station, airport.psa, REF_STORAGE, SLV_161, SL_MAX_VERSION), - - SLE_VAR(Station, indtype, SLE_UINT8), - SLE_CONDVAR_X(Station, extra_name_index, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_EXTRA_STATION_NAMES)), - - SLE_VAR(Station, time_since_load, SLE_UINT8), - SLE_VAR(Station, time_since_unload, SLE_UINT8), - SLEG_CONDVAR_X(_old_last_vehicle_type, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ST_LAST_VEH_TYPE, 0, 0)), - SLE_CONDNULL_X(1, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP)), - SLE_VAR(Station, had_vehicle_of_type, SLE_UINT8), - SLE_VEC(Station, loading_vehicles, REF_VEHICLE), - SLE_CONDVAR(Station, always_accepted, SLE_FILE_U32 | SLE_VAR_U64, SLV_127, SLV_EXTEND_CARGOTYPES), - SLE_CONDVAR(Station, always_accepted, SLE_UINT64, SLV_EXTEND_CARGOTYPES, SL_MAX_VERSION), - SLE_CONDNULL_X(32 * 24, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP, SL_JOKER_1_22)), - SLE_CONDVAR_X(Station, station_cargo_history_cargoes, SLE_UINT64, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_STATION_CARGO_HISTORY)), -}; - -static const SaveLoad _waypoint_desc[] = { - SLE_WRITEBYTE(Waypoint, facilities), - SLE_ST_INCLUDE(), - - SLE_VAR(Waypoint, town_cn, SLE_UINT16), - - SLE_CONDVAR(Waypoint, train_station.tile, SLE_UINT32, SLV_124, SL_MAX_VERSION), - SLE_CONDVAR(Waypoint, train_station.w, SLE_FILE_U8 | SLE_VAR_U16, SLV_124, SL_MAX_VERSION), - SLE_CONDVAR(Waypoint, train_station.h, SLE_FILE_U8 | SLE_VAR_U16, SLV_124, SL_MAX_VERSION), - SLE_CONDVAR_X(Waypoint, waypoint_flags, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_WAYPOINT_FLAGS)), - SLE_CONDVAR_X(Waypoint, road_waypoint_area.tile, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ROAD_WAYPOINTS)), - SLE_CONDVAR_X(Waypoint, road_waypoint_area.w, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ROAD_WAYPOINTS)), - SLE_CONDVAR_X(Waypoint, road_waypoint_area.h, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ROAD_WAYPOINTS)), -}; - -static const SaveLoad _custom_roadstop_tile_data_desc[] = { - SLE_VAR(RoadStopTileData, tile, SLE_UINT32), - SLE_VAR(RoadStopTileData, random_bits, SLE_UINT8), - SLE_VAR(RoadStopTileData, animation_frame, SLE_UINT8), -}; - -/** - * Get the base station description to be used for SL_ST_INCLUDE - * @return the base station description. - */ -SaveLoadTable GetBaseStationDescription() +void IncludeBaseStationDescription(std::vector &slt) { - return _base_station_desc; + SlFilterNamedSaveLoadTable(_base_station_desc, slt); } -std::vector _filtered_station_desc; -std::vector _filtered_waypoint_desc; -std::vector _filtered_goods_desc; -std::vector _filtered_station_speclist_desc; -std::vector _filtered_roadstop_speclist_desc; +struct BaseStationStructHandler final : public TypedSaveLoadStructHandler { + NamedSaveLoadTable GetDescription() const override + { + return _base_station_desc; + } -static void SetupDescs_STNN() + void Save(BaseStation *bst) const override + { + SlObjectSaveFiltered(bst, this->GetLoadDescription()); + } + + void Load(BaseStation *bst) const override + { + SlObjectLoadFiltered(bst, this->GetLoadDescription()); + } +}; + +static const NamedSaveLoad _station_cargo_history_desc[] = { + NSL("cargoes", SLTAG(SLTAG_CUSTOM_0, SLE_CONDVAR_X(Station, station_cargo_history_cargoes, SLE_UINT64, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_STATION_CARGO_HISTORY)))), + NSL("history", SLTAG(SLTAG_CUSTOM_1, SLEG_CONDVARVEC_X(_station_history_data_dummy, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_STATION_CARGO_HISTORY)))), +}; + +static void LoadStationCargoHistoryData(Station *st) { - _filtered_station_desc = SlFilterObject(_station_desc); - _filtered_waypoint_desc = SlFilterObject(_waypoint_desc); - _filtered_goods_desc = SlFilterObject(GetGoodsDesc()); - _filtered_station_speclist_desc = SlFilterObject(_station_speclist_desc); - _filtered_roadstop_speclist_desc = SlFilterObject(_roadstop_speclist_desc); -} - -static void RealSave_STNN(BaseStation *bst) -{ - _num_specs = (uint8_t)bst->speclist.size(); - _num_roadstop_specs = (uint8_t)bst->roadstop_speclist.size(); - _num_roadstop_custom_tiles = (uint32_t)bst->custom_roadstop_tile_data.size(); - - bool waypoint = (bst->facilities & FACIL_WAYPOINT) != 0; - SlObjectSaveFiltered(bst, waypoint ? SaveLoadTable(_filtered_waypoint_desc) : SaveLoadTable(_filtered_station_desc)); - - MemoryDumper *dumper = MemoryDumper::GetCurrent(); - - if (!waypoint) { - Station *st = Station::From(bst); - for (CargoID i = 0; i < NUM_CARGO; i++) { - const GoodsEntryData *ged = st->goods[i].data.get(); - if (ged != nullptr) { - _cargo_reserved_count = ged->cargo.ReservedCount(); - _num_dests = (uint32_t)ged->cargo.Packets()->MapSize(); - _num_flows = (uint32_t)ged->flows.size(); - } else { - _cargo_reserved_count = 0; - _num_dests = 0; - _num_flows = 0; - } - SlObjectSaveFiltered(&st->goods[i], _filtered_goods_desc); - if (ged == nullptr) continue; - for (FlowStatMap::const_iterator outer_it(ged->flows.begin()); outer_it != ged->flows.end(); ++outer_it) { - uint32_t sum_shares = 0; - FlowSaveLoad flow; - flow.source = outer_it->GetOrigin(); - dumper->CheckBytes(2 + 4); - dumper->RawWriteUint16(flow.source); - dumper->RawWriteUint32((uint32_t)outer_it->size()); - FlowStat::const_iterator inner_it(outer_it->begin()); - const FlowStat::const_iterator end(outer_it->end()); - for (; inner_it != end; ++inner_it) { - flow.via = inner_it->second; - flow.share = inner_it->first - sum_shares; - flow.restricted = inner_it->first > outer_it->GetUnrestricted(); - sum_shares = inner_it->first; - assert(flow.share > 0); - - // SlObject(&flow, _flow_desc); /* this is highly performance-sensitive, manually unroll */ - dumper->CheckBytes(2 + 4 + 1); - dumper->RawWriteUint16(flow.via); - dumper->RawWriteUint32(flow.share); - dumper->RawWriteByte(flow.restricted != 0); - } - SlWriteUint16(outer_it->GetRawFlags()); - } - for (StationCargoPacketMap::ConstMapIterator it(ged->cargo.Packets()->begin()); it != ged->cargo.Packets()->end(); ++it) { - SlObjectSaveFiltered(const_cast(&(*it)), _cargo_list_desc); // _cargo_list_desc has no conditionals + uint16_t *data = st->station_cargo_history[0].data(); + ReadBuffer::GetCurrent()->ReadUint16sToHandler(st->station_cargo_history.size() * MAX_STATION_CARGO_HISTORY_DAYS, [&](uint16_t val) { + *data = val; + data++; + }); + if (SlXvIsFeaturePresent(XSLFI_STATION_CARGO_HISTORY, 1, 1)) { + for (auto &history : st->station_cargo_history) { + for (uint16_t &amount : history) { + amount = RXCompressUint(amount); } } + } + st->station_cargo_history_offset = 0; +} + +struct StationGoodsStructHandler final : public TypedSaveLoadStructHandler { + mutable std::unique_ptr spare_ged; + + NamedSaveLoadTable GetDescription() const override + { + return GetGoodsDesc(); + } + + void Save(Station *st) const override + { + SlSetStructListLength(NUM_CARGO); + + for (GoodsEntry &ge : st->goods) { + SlObjectSaveFiltered(&ge, this->GetLoadDescription()); + } + } + + void Load(Station *st) const override + { + uint num_cargo = static_cast(SlGetStructListLength(NUM_CARGO)); + + for (CargoID i = 0; i < num_cargo; i++) { + GoodsEntry &ge = st->goods[i]; + if (ge.data == nullptr) { + if (this->spare_ged != nullptr) { + ge.data = std::move(this->spare_ged); + } else { + ge.data.reset(new GoodsEntryData()); + } + } + SlObjectLoadFiltered(&ge, this->GetLoadDescription()); + ge.data->cargo.LoadSetReservedCount(_cargo_reserved_count); + if (SlXvIsFeatureMissing(XSLFI_ST_LAST_VEH_TYPE)) ge.last_vehicle_type = _old_last_vehicle_type; + if (ge.data->MayBeRemoved()) { + this->spare_ged = std::move(ge.data); + } + } + } +}; + +struct StationCargoHistoryStructHandler final : public TypedSaveLoadStructHandler { + NamedSaveLoadTable GetDescription() const override + { + return _station_cargo_history_desc; + } + + void Save(Station *st) const override + { + MemoryDumper *dumper = MemoryDumper::GetCurrent(); + SlWriteUint64(st->station_cargo_history_cargoes); + SlWriteSimpleGamma(st->station_cargo_history.size() * MAX_STATION_CARGO_HISTORY_DAYS); - assert(st->station_cargo_history.size() == CountBits(st->station_cargo_history_cargoes)); dumper->CheckBytes(st->station_cargo_history.size() * MAX_STATION_CARGO_HISTORY_DAYS * 2); for (const auto &history : st->station_cargo_history) { uint i = st->station_cargo_history_offset; @@ -578,40 +677,276 @@ static void RealSave_STNN(BaseStation *bst) } } - for (uint i = 0; i < bst->speclist.size(); i++) { - SlObjectSaveFiltered(&bst->speclist[i], _filtered_station_speclist_desc); + void Load(Station *st) const override + { + st->station_cargo_history_cargoes = SlReadUint64(); + st->station_cargo_history.resize(CountBits(st->station_cargo_history_cargoes)); + if (SlReadSimpleGamma() != st->station_cargo_history.size() * MAX_STATION_CARGO_HISTORY_DAYS) { + SlErrorCorrupt("Station cargo history data of wrong size"); + } + LoadStationCargoHistoryData(st); } - for (uint i = 0; i < bst->roadstop_speclist.size(); i++) { - SlObjectSaveFiltered(&bst->roadstop_speclist[i], _filtered_roadstop_speclist_desc); + void LoadedTableDescription() override + { + SaveLoadTable slt = this->GetLoadDescription(); + if (slt.size() != 2 || slt[0].label_tag != SLTAG_CUSTOM_0 || slt[1].label_tag != SLTAG_CUSTOM_1) { + SlErrorCorrupt("Station cargo history sub-chunk fields not as expected"); + } + } +}; + +static OldPersistentStorage _old_st_persistent_storage; + +static const NamedSaveLoad _station_desc[] = { + NSL("", SLE_WRITEBYTE(Station, facilities)), + NSL("", SLE_INCLUDE(IncludeBaseStationDescription)), + NSLT_STRUCT("base"), + + NSL("train_station.tile", SLE_VAR(Station, train_station.tile, SLE_UINT32)), + NSL("train_station.w", SLE_VAR(Station, train_station.w, SLE_FILE_U8 | SLE_VAR_U16)), + NSL("train_station.h", SLE_VAR(Station, train_station.h, SLE_FILE_U8 | SLE_VAR_U16)), + + NSL("bus_stops", SLE_REF(Station, bus_stops, REF_ROADSTOPS)), + NSL("truck_stops", SLE_REF(Station, truck_stops, REF_ROADSTOPS)), + NSL("ship_station.tile", SLE_CONDVAR_X(Station, ship_station.tile, SLE_UINT32, SL_MIN_VERSION, SLV_MULTITILE_DOCKS, SlXvFeatureTest(XSLFTO_AND, XSLFI_MULTIPLE_DOCKS, 0, 0))), + NSL("", SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_MULTIPLE_DOCKS, 1, 1))), + NSL("ship_station.tile", SLE_CONDVAR(Station, ship_station.tile, SLE_UINT32, SLV_MULTITILE_DOCKS, SL_MAX_VERSION)), + NSL("ship_station.w", SLE_CONDVAR(Station, ship_station.w, SLE_FILE_U8 | SLE_VAR_U16, SLV_MULTITILE_DOCKS, SL_MAX_VERSION)), + NSL("ship_station.h", SLE_CONDVAR(Station, ship_station.h, SLE_FILE_U8 | SLE_VAR_U16, SLV_MULTITILE_DOCKS, SL_MAX_VERSION)), + NSL("docking_station.tile", SLE_CONDVAR(Station, docking_station.tile, SLE_UINT32, SLV_MULTITILE_DOCKS, SL_MAX_VERSION)), + NSL("docking_station.w", SLE_CONDVAR(Station, docking_station.w, SLE_FILE_U8 | SLE_VAR_U16, SLV_MULTITILE_DOCKS, SL_MAX_VERSION)), + NSL("docking_station.h", SLE_CONDVAR(Station, docking_station.h, SLE_FILE_U8 | SLE_VAR_U16, SLV_MULTITILE_DOCKS, SL_MAX_VERSION)), + NSL("docking_tiles", SLE_CONDVARVEC_X(Station, docking_tiles, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_MULTIPLE_DOCKS, 2))), + NSL("airport.tile", SLE_VAR(Station, airport.tile, SLE_UINT32)), + NSL("airport.w", SLE_CONDVAR(Station, airport.w, SLE_FILE_U8 | SLE_VAR_U16, SLV_140, SL_MAX_VERSION)), + NSL("airport.h", SLE_CONDVAR(Station, airport.h, SLE_FILE_U8 | SLE_VAR_U16, SLV_140, SL_MAX_VERSION)), + NSL("airport.type", SLE_VAR(Station, airport.type, SLE_UINT8)), + NSL("airport.layout", SLE_CONDVAR(Station, airport.layout, SLE_UINT8, SLV_145, SL_MAX_VERSION)), + NSL("", SLE_CONDNULL_X(1, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP, 1, 6))), + NSL("airport.flags", SLE_VAR(Station, airport.flags, SLE_UINT64)), + NSL("", SLE_CONDNULL_X(8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP, 1, 6))), + NSL("airport.rotation", SLE_CONDVAR(Station, airport.rotation, SLE_UINT8, SLV_145, SL_MAX_VERSION)), + NSL("", SLEG_CONDARR(_old_st_persistent_storage.storage, SLE_UINT32, 16, SLV_145, SLV_161)), + NSL("irport.psa", SLE_CONDREF(Station, airport.psa, REF_STORAGE, SLV_161, SL_MAX_VERSION)), + + NSL("indtype", SLE_VAR(Station, indtype, SLE_UINT8)), + NSL("extra_name_index", SLE_CONDVAR_X(Station, extra_name_index, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_EXTRA_STATION_NAMES))), + + NSL("time_since_load", SLE_VAR(Station, time_since_load, SLE_UINT8)), + NSL("time_since_unload", SLE_VAR(Station, time_since_unload, SLE_UINT8)), + NSL("", SLEG_CONDVAR_X(_old_last_vehicle_type, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ST_LAST_VEH_TYPE, 0, 0))), + NSL("", SLE_CONDNULL_X(1, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP))), + NSL("had_vehicle_of_type", SLE_VAR(Station, had_vehicle_of_type, SLE_UINT8)), + NSL("loading_vehicles", SLE_VEC(Station, loading_vehicles, REF_VEHICLE)), + NSL("always_accepted", SLE_CONDVAR(Station, always_accepted, SLE_FILE_U32 | SLE_VAR_U64, SLV_127, SLV_EXTEND_CARGOTYPES)), + NSL("always_accepted", SLE_CONDVAR(Station, always_accepted, SLE_UINT64, SLV_EXTEND_CARGOTYPES, SL_MAX_VERSION)), + NSL("", SLE_CONDNULL_X(32 * 24, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP, SL_JOKER_1_22))), + + NSL("", SLE_CONDVAR_X(Station, station_cargo_history_cargoes, SLE_UINT64, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_STATION_CARGO_HISTORY))), + NSLT_STRUCTLIST("goods"), + NSLT_STRUCT("cargo_history"), +}; + +static const NamedSaveLoad _waypoint_desc[] = { + NSL("", SLE_WRITEBYTE(Waypoint, facilities)), + NSL("", SLE_INCLUDE(IncludeBaseStationDescription)), + NSLT_STRUCT("base"), + + NSL("town_cn", SLE_VAR(Waypoint, town_cn, SLE_UINT16)), + + NSL("train_station.tile", SLE_CONDVAR(Waypoint, train_station.tile, SLE_UINT32, SLV_124, SL_MAX_VERSION)), + NSL("train_station.w", SLE_CONDVAR(Waypoint, train_station.w, SLE_FILE_U8 | SLE_VAR_U16, SLV_124, SL_MAX_VERSION)), + NSL("train_station.h", SLE_CONDVAR(Waypoint, train_station.h, SLE_FILE_U8 | SLE_VAR_U16, SLV_124, SL_MAX_VERSION)), + NSL("waypoint_flags", SLE_CONDVAR_X(Waypoint, waypoint_flags, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_WAYPOINT_FLAGS))), + NSL("road_waypoint_area.tile", SLE_CONDVAR_X(Waypoint, road_waypoint_area.tile, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ROAD_WAYPOINTS))), + NSL("road_waypoint_area.w", SLE_CONDVAR_X(Waypoint, road_waypoint_area.w, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ROAD_WAYPOINTS))), + NSL("road_waypoint_area.h", SLE_CONDVAR_X(Waypoint, road_waypoint_area.h, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ROAD_WAYPOINTS))), +}; + +static const NamedSaveLoad _custom_roadstop_tile_data_desc[] = { + NSL("tile", SLE_VAR(RoadStopTileData, tile, SLE_UINT32)), + NSL("random_bits", SLE_VAR(RoadStopTileData, random_bits, SLE_UINT8)), + NSL("animation_frame", SLE_VAR(RoadStopTileData, animation_frame, SLE_UINT8)), +}; + +class StationSpecListStructHandler final : public TypedSaveLoadStructHandler { +public: + NamedSaveLoadTable GetDescription() const override + { + return _station_speclist_desc; } - for (uint i = 0; i < bst->custom_roadstop_tile_data.size(); i++) { - SlObjectSaveFiltered(&bst->custom_roadstop_tile_data[i], _custom_roadstop_tile_data_desc); // _custom_roadstop_tile_data_desc has no conditionals + void Save(BaseStation *bst) const override + { + SlSetStructListLength(bst->speclist.size()); + for (StationSpecList &spec : bst->speclist) { + SlObjectSaveFiltered(&spec, this->GetLoadDescription()); + } } -} + + void Load(BaseStation *bst) const override + { + bst->speclist.resize(SlGetStructListLength(UINT8_MAX)); + for (StationSpecList &spec : bst->speclist) { + SlObjectLoadFiltered(&spec, this->GetLoadDescription()); + } + } +}; + +class RoadStopSpecListStructHandler final : public TypedSaveLoadStructHandler { +public: + NamedSaveLoadTable GetDescription() const override + { + return _roadstop_speclist_desc; + } + + void Save(BaseStation *bst) const override + { + SlSetStructListLength(bst->roadstop_speclist.size()); + for (RoadStopSpecList &spec : bst->roadstop_speclist) { + SlObjectSaveFiltered(&spec, this->GetLoadDescription()); + } + } + + void Load(BaseStation *bst) const override + { + bst->roadstop_speclist.resize(SlGetStructListLength(UINT8_MAX)); + for (RoadStopSpecList &spec : bst->roadstop_speclist) { + SlObjectLoadFiltered(&spec, this->GetLoadDescription()); + } + } +}; + +class RoadStopTileDataStructHandler final : public TypedSaveLoadStructHandler { +public: + NamedSaveLoadTable GetDescription() const override + { + return _custom_roadstop_tile_data_desc; + } + + void Save(BaseStation *bst) const override + { + SlSetStructListLength(bst->custom_roadstop_tile_data.size()); + for (RoadStopTileData &data : bst->custom_roadstop_tile_data) { + SlObjectSaveFiltered(&data, this->GetLoadDescription()); + } + } + + void Load(BaseStation *bst) const override + { + bst->custom_roadstop_tile_data.resize(SlGetStructListLength(UINT32_MAX)); + for (RoadStopTileData &data : bst->custom_roadstop_tile_data) { + SlObjectLoadFiltered(&data, this->GetLoadDescription()); + } + } +}; + +struct NormalStationStructHandler final : public TypedSaveLoadStructHandler { + NamedSaveLoadTable GetDescription() const override + { + return _station_desc; + } + + void Save(BaseStation *bst) const override + { + if ((bst->facilities & FACIL_WAYPOINT) != 0) return; + SlObjectSaveFiltered(static_cast(bst), this->GetLoadDescription()); + } + + void Load(BaseStation *bst) const override + { + if ((bst->facilities & FACIL_WAYPOINT) != 0) SlErrorCorrupt("Waypoint with normal station struct"); + SlObjectLoadFiltered(static_cast(bst), this->GetLoadDescription()); + } +}; + +struct WaypointStructHandler final : public TypedSaveLoadStructHandler { + NamedSaveLoadTable GetDescription() const override + { + return _waypoint_desc; + } + + void Save(BaseStation *bst) const override + { + if ((bst->facilities & FACIL_WAYPOINT) == 0) return; + SlObjectSaveFiltered(static_cast(bst), this->GetLoadDescription()); + } + + void Load(BaseStation *bst) const override + { + if ((bst->facilities & FACIL_WAYPOINT) == 0) SlErrorCorrupt("Normal station with waypoint struct"); + SlObjectLoadFiltered(static_cast(bst), this->GetLoadDescription()); + } +}; + + +static const NamedSaveLoad _table_station_desc[] = { + NSLT("facilities", SLE_WRITEBYTE(BaseStation, facilities)), + NSLT_STRUCT("normal"), + NSLT_STRUCT("waypoint"), + NSLT_STRUCTLIST("speclist"), + NSLT_STRUCTLIST("roadstopspeclist"), + NSLT_STRUCTLIST("roadstoptiledata"), +}; static void Save_STNN() { - SetupDescs_STNN(); + SaveLoadTableData slt = SlTableHeader(_table_station_desc); /* Write the stations */ for (BaseStation *st : BaseStation::Iterate()) { SlSetArrayIndex(st->index); - SlAutolength(RealSave_STNN, st); + SlObjectSaveFiltered(st, slt); + } +} + +static void PostLoadStation_STNN(BaseStation *bst) +{ + if (SlXvIsFeaturePresent(XSLFI_GRF_ROADSTOPS, 1, 1)) { + for (size_t i = 0; i < _custom_road_stop_tiles.size(); i++) { + bst->custom_roadstop_tile_data.push_back({ _custom_road_stop_tiles[i], (uint8_t)GB(_custom_road_stop_data[i], 0, 8), (uint8_t)GB(_custom_road_stop_data[i], 8, 8) }); + } + _custom_road_stop_tiles.clear(); + _custom_road_stop_data.clear(); + } +} + +static void Load_STNN_table() +{ + SaveLoadTableData slt = SlTableHeaderOrRiff(_table_station_desc); + + int index; + while ((index = SlIterateArray()) != -1) { + bool waypoint = (SlReadByte() & FACIL_WAYPOINT) != 0; + + BaseStation *bst = waypoint ? (BaseStation *)new (index) Waypoint() : new (index) Station(); + SlObjectLoadFiltered(bst, slt); + PostLoadStation_STNN(bst); } } static void Load_STNN() { - SetupDescs_STNN(); - _num_flows = 0; _num_specs = 0; _num_roadstop_specs = 0; _num_roadstop_custom_tiles = 0; _cargo_reserved_count = 0; + if (SlIsTableChunk()) { + Load_STNN_table(); + return; + } + + std::vector filtered_station_desc = SlFilterNamedSaveLoadTable(_station_desc); + std::vector filtered_waypoint_desc = SlFilterNamedSaveLoadTable(_waypoint_desc); + std::vector filtered_goods_desc = SlFilterNamedSaveLoadTable(GetGoodsDesc()); + std::vector cargo_list_desc = SlFilterNamedSaveLoadTable(_cargo_list_desc); + std::vector filtered_station_speclist_desc = SlFilterNamedSaveLoadTable(_station_speclist_desc); + std::vector filtered_roadstop_speclist_desc = SlFilterNamedSaveLoadTable(_roadstop_speclist_desc); + std::vector custom_roadstop_tile_data_desc = SlFilterNamedSaveLoadTable(_custom_roadstop_tile_data_desc); + std::unique_ptr spare_ged; const uint num_cargo = IsSavegameVersionBefore(SLV_EXTEND_CARGOTYPES) ? 32 : NUM_CARGO; @@ -622,7 +957,7 @@ static void Load_STNN() bool waypoint = (SlReadByte() & FACIL_WAYPOINT) != 0; BaseStation *bst = waypoint ? (BaseStation *)new (index) Waypoint() : new (index) Station(); - SlObjectLoadFiltered(bst, waypoint ? SaveLoadTable(_filtered_waypoint_desc) : SaveLoadTable(_filtered_station_desc)); + SlObjectLoadFiltered(bst, waypoint ? SaveLoadTable(filtered_waypoint_desc) : SaveLoadTable(filtered_station_desc)); if (!waypoint) { Station *st = Station::From(bst); @@ -644,7 +979,7 @@ static void Load_STNN() ge.data.reset(new GoodsEntryData()); } } - SlObjectLoadFiltered(&ge, _filtered_goods_desc); + SlObjectLoadFiltered(&ge, filtered_goods_desc); ge.data->cargo.LoadSetReservedCount(_cargo_reserved_count); StationID prev_source = INVALID_STATION; if (SlXvIsFeaturePresent(XSLFI_FLOW_STAT_FLAGS)) { @@ -702,7 +1037,7 @@ static void Load_STNN() StationCargoPair pair; for (uint j = 0; j < _num_dests; ++j) { - SlObjectLoadFiltered(&pair, _cargo_list_desc); // _cargo_list_desc has no conditionals + SlObjectLoadFiltered(&pair, cargo_list_desc); const_cast(*(ge.data->cargo.Packets()))[pair.first].swap(pair.second); assert(pair.second.empty()); } @@ -714,27 +1049,14 @@ static void Load_STNN() } st->station_cargo_history.resize(CountBits(st->station_cargo_history_cargoes)); - buffer->CheckBytes(st->station_cargo_history.size() * MAX_STATION_CARGO_HISTORY_DAYS * 2); - for (auto &history : st->station_cargo_history) { - for (uint16_t &amount : history) { - amount = buffer->RawReadUint16(); - } - } - if (SlXvIsFeaturePresent(XSLFI_STATION_CARGO_HISTORY, 1, 1)) { - for (auto &history : st->station_cargo_history) { - for (uint16_t &amount : history) { - amount = RXCompressUint(amount); - } - } - } - st->station_cargo_history_offset = 0; + LoadStationCargoHistoryData(st); } if (_num_specs != 0) { /* Allocate speclist memory when loading a game */ bst->speclist.resize(_num_specs); for (uint i = 0; i < bst->speclist.size(); i++) { - SlObjectLoadFiltered(&bst->speclist[i], _filtered_station_speclist_desc); + SlObjectLoadFiltered(&bst->speclist[i], filtered_station_speclist_desc); } } @@ -742,7 +1064,7 @@ static void Load_STNN() /* Allocate speclist memory when loading a game */ bst->roadstop_speclist.resize(_num_roadstop_specs); for (uint i = 0; i < bst->roadstop_speclist.size(); i++) { - SlObjectLoadFiltered(&bst->roadstop_speclist[i], _filtered_roadstop_speclist_desc); + SlObjectLoadFiltered(&bst->roadstop_speclist[i], filtered_roadstop_speclist_desc); } } @@ -750,17 +1072,11 @@ static void Load_STNN() /* Allocate custom road stop tile data memory when loading a game */ bst->custom_roadstop_tile_data.resize(_num_roadstop_custom_tiles); for (uint i = 0; i < bst->custom_roadstop_tile_data.size(); i++) { - SlObjectLoadFiltered(&bst->custom_roadstop_tile_data[i], _custom_roadstop_tile_data_desc); // _custom_roadstop_tile_data_desc has no conditionals + SlObjectLoadFiltered(&bst->custom_roadstop_tile_data[i], custom_roadstop_tile_data_desc); } } - if (SlXvIsFeaturePresent(XSLFI_GRF_ROADSTOPS, 1, 1)) { - for (size_t i = 0; i < _custom_road_stop_tiles.size(); i++) { - bst->custom_roadstop_tile_data.push_back({ _custom_road_stop_tiles[i], (uint8_t)GB(_custom_road_stop_data[i], 0, 8), (uint8_t)GB(_custom_road_stop_data[i], 8, 8) }); - } - _custom_road_stop_tiles.clear(); - _custom_road_stop_data.clear(); - } + PostLoadStation_STNN(bst); } } @@ -769,10 +1085,13 @@ static void Ptrs_STNN() /* Don't run when savegame version lower than 123. */ if (IsSavegameVersionBefore(SLV_123)) return; - SetupDescs_STNN(); + std::vector filtered_station_desc = SlFilterNamedSaveLoadTable(_station_desc); + std::vector filtered_waypoint_desc = SlFilterNamedSaveLoadTable(_waypoint_desc); + std::vector filtered_goods_desc = SlFilterNamedSaveLoadTable(GetGoodsDesc()); + std::vector cargolist_desc = SlFilterNamedSaveLoadTable(_cargo_list_desc); if (!IsSavegameVersionBefore(SLV_183)) { - assert(_filtered_goods_desc.size() == 0); + assert(filtered_goods_desc.size() == 0); } uint num_cargo = IsSavegameVersionBefore(SLV_EXTEND_CARGOTYPES) ? 32 : NUM_CARGO; @@ -781,22 +1100,22 @@ static void Ptrs_STNN() GoodsEntry *ge = &st->goods[i]; if (IsSavegameVersionBefore(SLV_183) && SlXvIsFeatureMissing(XSLFI_CHILLPP)) { SwapPackets(ge); - SlObjectPtrOrNullFiltered(ge, _filtered_goods_desc); + SlObjectPtrOrNullFiltered(ge, filtered_goods_desc); SwapPackets(ge); } else { //SlObject(ge, GetGoodsDesc()); if (ge->data != nullptr) { for (StationCargoPacketMap::ConstMapIterator it = ge->data->cargo.Packets()->begin(); it != ge->data->cargo.Packets()->end(); ++it) { - SlObjectPtrOrNullFiltered(const_cast(&(*it)), _cargo_list_desc); // _cargo_list_desc has no conditionals + SlObjectPtrOrNullFiltered(const_cast(&(*it)), cargolist_desc); } } } } - SlObjectPtrOrNullFiltered(st, _filtered_station_desc); + SlObjectPtrOrNullFiltered(st, filtered_station_desc); } for (Waypoint *wp : Waypoint::Iterate()) { - SlObjectPtrOrNullFiltered(wp, _filtered_waypoint_desc); + SlObjectPtrOrNullFiltered(wp, filtered_waypoint_desc); } } @@ -808,7 +1127,7 @@ static void Load_DOCK() static const ChunkHandler station_chunk_handlers[] = { { 'STNS', nullptr, Load_STNS, Ptrs_STNS, nullptr, CH_READONLY }, - { 'STNN', Save_STNN, Load_STNN, Ptrs_STNN, nullptr, CH_ARRAY }, + { 'STNN', Save_STNN, Load_STNN, Ptrs_STNN, nullptr, CH_TABLE }, MakeUpstreamChunkHandler<'ROAD', GeneralUpstreamChunkLoadInfo>(), { 'DOCK', nullptr, Load_DOCK, nullptr, nullptr, CH_READONLY }, };