Update: Translations from eints
[openttd-github.git] / src / saveload / station_sl.cpp
blobf1c288af565074d93d7bb9dfe4e88b68461a4033
1 /*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6 */
8 /** @file station_sl.cpp Code handling saving and loading of stations. */
10 #include "../stdafx.h"
12 #include "saveload.h"
13 #include "compat/station_sl_compat.h"
15 #include "../station_base.h"
16 #include "../waypoint_base.h"
17 #include "../roadstop_base.h"
18 #include "../vehicle_base.h"
19 #include "../newgrf_station.h"
20 #include "../newgrf_roadstop.h"
21 #include "../timer/timer_game_calendar.h"
23 #include "table/strings.h"
25 #include "../safeguards.h"
27 /**
28 * Update the buoy orders to be waypoint orders.
29 * @param o the order 'list' to check.
31 static void UpdateWaypointOrder(Order *o)
33 if (!o->IsType(OT_GOTO_STATION)) return;
35 const Station *st = Station::Get(o->GetDestination());
36 if ((st->had_vehicle_of_type & HVOT_WAYPOINT) == 0) return;
38 o->MakeGoToWaypoint(o->GetDestination());
41 /**
42 * Perform all steps to upgrade from the old station buoys to the new version
43 * that uses waypoints. This includes some old saveload mechanics.
45 void MoveBuoysToWaypoints()
47 /* Buoy orders become waypoint orders */
48 for (OrderList *ol : OrderList::Iterate()) {
49 VehicleType vt = ol->GetFirstSharedVehicle()->type;
50 if (vt != VEH_SHIP && vt != VEH_TRAIN) continue;
52 for (Order *o = ol->GetFirstOrder(); o != nullptr; o = o->next) UpdateWaypointOrder(o);
55 for (Vehicle *v : Vehicle::Iterate()) {
56 VehicleType vt = v->type;
57 if (vt != VEH_SHIP && vt != VEH_TRAIN) continue;
59 UpdateWaypointOrder(&v->current_order);
62 /* Now make the stations waypoints */
63 for (Station *st : Station::Iterate()) {
64 if ((st->had_vehicle_of_type & HVOT_WAYPOINT) == 0) continue;
66 StationID index = st->index;
67 TileIndex xy = st->xy;
68 Town *town = st->town;
69 StringID string_id = st->string_id;
70 std::string name = st->name;
71 TimerGameCalendar::Date build_date = st->build_date;
72 /* TTDPatch could use "buoys with rail station" for rail waypoints */
73 bool train = st->train_station.tile != INVALID_TILE;
74 TileArea train_st = st->train_station;
76 /* Delete the station, so we can make it a real waypoint. */
77 delete st;
79 /* Stations and waypoints are in the same pool, so if a station
80 * is deleted there must be place for a Waypoint. */
81 assert(Waypoint::CanAllocateItem());
82 Waypoint *wp = new (index) Waypoint(xy);
83 wp->town = town;
84 wp->string_id = train ? STR_SV_STNAME_WAYPOINT : STR_SV_STNAME_BUOY;
85 wp->name = name;
86 wp->delete_ctr = 0; // Just reset delete counter for once.
87 wp->build_date = build_date;
88 wp->owner = train ? GetTileOwner(xy) : OWNER_NONE;
90 if (IsInsideBS(string_id, STR_SV_STNAME_BUOY, 9)) wp->town_cn = string_id - STR_SV_STNAME_BUOY;
92 if (train) {
93 /* When we make a rail waypoint of the station, convert the map as well. */
94 for (TileIndex t : train_st) {
95 Tile tile(t);
96 if (!IsTileType(tile, MP_STATION) || GetStationIndex(tile) != index) continue;
98 SB(tile.m6(), 3, 3, STATION_WAYPOINT);
99 wp->rect.BeforeAddTile(t, StationRect::ADD_FORCE);
102 wp->train_station = train_st;
103 wp->facilities |= FACIL_TRAIN;
104 } else if (IsBuoyTile(xy) && GetStationIndex(xy) == index) {
105 wp->rect.BeforeAddTile(xy, StationRect::ADD_FORCE);
106 wp->facilities |= FACIL_DOCK;
111 void AfterLoadStations()
113 /* Update the speclists of all stations to point to the currently loaded custom stations. */
114 for (BaseStation *st : BaseStation::Iterate()) {
115 for (auto &sm : GetStationSpecList<StationSpec>(st)) {
116 if (sm.grfid == 0) continue;
117 sm.spec = StationClass::GetByGrf(sm.grfid, sm.localidx);
119 for (auto &sm : GetStationSpecList<RoadStopSpec>(st)) {
120 if (sm.grfid == 0) continue;
121 sm.spec = RoadStopClass::GetByGrf(sm.grfid, sm.localidx);
124 if (Station::IsExpected(st)) {
125 Station *sta = Station::From(st);
126 for (const RoadStop *rs = sta->bus_stops; rs != nullptr; rs = rs->next) sta->bus_station.Add(rs->xy);
127 for (const RoadStop *rs = sta->truck_stops; rs != nullptr; rs = rs->next) sta->truck_station.Add(rs->xy);
130 StationUpdateCachedTriggers(st);
131 RoadStopUpdateCachedTriggers(st);
136 * (Re)building of road stop caches after loading a savegame.
138 void AfterLoadRoadStops()
140 /* First construct the drive through entries */
141 for (RoadStop *rs : RoadStop::Iterate()) {
142 if (IsDriveThroughStopTile(rs->xy)) rs->MakeDriveThrough();
144 /* And then rebuild the data in those entries */
145 for (RoadStop *rs : RoadStop::Iterate()) {
146 if (!HasBit(rs->status, RoadStop::RSSFB_BASE_ENTRY)) continue;
148 rs->GetEntry(DIAGDIR_NE)->Rebuild(rs);
149 rs->GetEntry(DIAGDIR_NW)->Rebuild(rs);
153 static const SaveLoad _roadstop_desc[] = {
154 SLE_VAR(RoadStop, xy, SLE_UINT32),
155 SLE_VAR(RoadStop, status, SLE_UINT8),
156 SLE_REF(RoadStop, next, REF_ROADSTOPS),
159 static uint16_t _waiting_acceptance;
160 static uint32_t _old_num_flows;
161 static uint16_t _cargo_source;
162 static uint32_t _cargo_source_xy;
163 static uint8_t _cargo_periods;
164 static Money _cargo_feeder_share;
166 std::list<CargoPacket *> _packets;
167 uint32_t _old_num_dests;
169 struct FlowSaveLoad {
170 FlowSaveLoad() : source(0), via(0), share(0), restricted(false) {}
171 StationID source;
172 StationID via;
173 uint32_t share;
174 bool restricted;
177 typedef std::pair<const StationID, std::list<CargoPacket *> > StationCargoPair;
179 static OldPersistentStorage _old_st_persistent_storage;
182 * Swap the temporary packets with the packets without specific destination in
183 * the given goods entry. Assert that at least one of those is empty.
184 * @param ge Goods entry to swap with.
186 static void SwapPackets(GoodsEntry *ge)
188 StationCargoPacketMap &ge_packets = const_cast<StationCargoPacketMap &>(*ge->cargo.Packets());
190 if (_packets.empty()) {
191 std::map<StationID, std::list<CargoPacket *> >::iterator it(ge_packets.find(INVALID_STATION));
192 if (it == ge_packets.end()) {
193 return;
194 } else {
195 it->second.swap(_packets);
197 } else {
198 assert(ge_packets[INVALID_STATION].empty());
199 ge_packets[INVALID_STATION].swap(_packets);
203 template <typename T>
204 class SlStationSpecList : public DefaultSaveLoadHandler<SlStationSpecList<T>, BaseStation> {
205 public:
206 inline static const SaveLoad description[] = {
207 SLE_CONDVAR(SpecMapping<T>, grfid, SLE_UINT32, SLV_27, SL_MAX_VERSION),
208 SLE_CONDVAR(SpecMapping<T>, localidx, SLE_FILE_U8 | SLE_VAR_U16, SLV_27, SLV_EXTEND_ENTITY_MAPPING),
209 SLE_CONDVAR(SpecMapping<T>, localidx, SLE_UINT16, SLV_EXTEND_ENTITY_MAPPING, SL_MAX_VERSION),
211 inline const static SaveLoadCompatTable compat_description = _station_spec_list_sl_compat;
213 static inline uint8_t last_num_specs; ///< Number of specs of the last loaded station.
215 void Save(BaseStation *bst) const override
217 auto &speclist = GetStationSpecList<T>(bst);
218 SlSetStructListLength(speclist.size());
219 for (auto &sm : speclist) {
220 SlObject(&sm, this->GetDescription());
224 void Load(BaseStation *bst) const override
226 size_t num_specs = IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) ? last_num_specs : SlGetStructListLength(UINT8_MAX);
228 auto &speclist = GetStationSpecList<T>(bst);
229 speclist.reserve(num_specs);
230 for (size_t index = 0; index < num_specs; ++index) {
231 auto &sm = speclist.emplace_back();
232 SlObject(&sm, this->GetLoadDescription());
237 /* Instantiate SlStationSpecList classes. */
238 template class SlStationSpecList<StationSpec>;
239 template class SlStationSpecList<RoadStopSpec>;
241 class SlStationCargo : public DefaultSaveLoadHandler<SlStationCargo, GoodsEntry> {
242 public:
243 inline static const SaveLoad description[] = {
244 SLE_VAR(StationCargoPair, first, SLE_UINT16),
245 SLE_REFLIST(StationCargoPair, second, REF_CARGO_PACKET),
247 inline const static SaveLoadCompatTable compat_description = _station_cargo_sl_compat;
249 void Save(GoodsEntry *ge) const override
251 SlSetStructListLength(ge->cargo.Packets()->MapSize());
252 for (StationCargoPacketMap::ConstMapIterator it(ge->cargo.Packets()->begin()); it != ge->cargo.Packets()->end(); ++it) {
253 SlObject(const_cast<StationCargoPacketMap::value_type *>(&(*it)), this->GetDescription());
257 void Load(GoodsEntry *ge) const override
259 size_t num_dests = IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) ? _old_num_dests : SlGetStructListLength(UINT32_MAX);
261 StationCargoPair pair;
262 for (uint j = 0; j < num_dests; ++j) {
263 SlObject(&pair, this->GetLoadDescription());
264 const_cast<StationCargoPacketMap &>(*(ge->cargo.Packets()))[pair.first].swap(pair.second);
265 assert(pair.second.empty());
269 void FixPointers(GoodsEntry *ge) const override
271 for (StationCargoPacketMap::ConstMapIterator it = ge->cargo.Packets()->begin(); it != ge->cargo.Packets()->end(); ++it) {
272 SlObject(const_cast<StationCargoPair *>(&(*it)), this->GetDescription());
277 class SlStationFlow : public DefaultSaveLoadHandler<SlStationFlow, GoodsEntry> {
278 public:
279 inline static const SaveLoad description[] = {
280 SLE_VAR(FlowSaveLoad, source, SLE_UINT16),
281 SLE_VAR(FlowSaveLoad, via, SLE_UINT16),
282 SLE_VAR(FlowSaveLoad, share, SLE_UINT32),
283 SLE_CONDVAR(FlowSaveLoad, restricted, SLE_BOOL, SLV_187, SL_MAX_VERSION),
285 inline const static SaveLoadCompatTable compat_description = _station_flow_sl_compat;
287 void Save(GoodsEntry *ge) const override
289 size_t num_flows = 0;
290 for (const auto &it : ge->flows) {
291 num_flows += it.second.GetShares()->size();
293 SlSetStructListLength(num_flows);
295 for (const auto &outer_it : ge->flows) {
296 const FlowStat::SharesMap *shares = outer_it.second.GetShares();
297 uint32_t sum_shares = 0;
298 FlowSaveLoad flow;
299 flow.source = outer_it.first;
300 for (auto &inner_it : *shares) {
301 flow.via = inner_it.second;
302 flow.share = inner_it.first - sum_shares;
303 flow.restricted = inner_it.first > outer_it.second.GetUnrestricted();
304 sum_shares = inner_it.first;
305 assert(flow.share > 0);
306 SlObject(&flow, this->GetDescription());
311 void Load(GoodsEntry *ge) const override
313 size_t num_flows = IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) ? _old_num_flows : SlGetStructListLength(UINT32_MAX);
315 FlowSaveLoad flow;
316 FlowStat *fs = nullptr;
317 StationID prev_source = INVALID_STATION;
318 for (uint32_t j = 0; j < num_flows; ++j) {
319 SlObject(&flow, this->GetLoadDescription());
320 if (fs == nullptr || prev_source != flow.source) {
321 fs = &(ge->flows.emplace(flow.source, FlowStat(flow.via, flow.share, flow.restricted))).first->second;
322 } else {
323 fs->AppendShare(flow.via, flow.share, flow.restricted);
325 prev_source = flow.source;
330 class SlStationGoods : public DefaultSaveLoadHandler<SlStationGoods, BaseStation> {
331 public:
332 inline static const SaveLoad description[] = {
333 SLEG_CONDVAR("waiting_acceptance", _waiting_acceptance, SLE_UINT16, SL_MIN_VERSION, SLV_68),
334 SLE_CONDVAR(GoodsEntry, status, SLE_UINT8, SLV_68, SL_MAX_VERSION),
335 SLE_VAR(GoodsEntry, time_since_pickup, SLE_UINT8),
336 SLE_VAR(GoodsEntry, rating, SLE_UINT8),
337 SLEG_CONDVAR("cargo_source", _cargo_source, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_7),
338 SLEG_CONDVAR("cargo_source", _cargo_source, SLE_UINT16, SLV_7, SLV_68),
339 SLEG_CONDVAR("cargo_source_xy", _cargo_source_xy, SLE_UINT32, SLV_44, SLV_68),
340 SLEG_CONDVAR("cargo_days", _cargo_periods, SLE_UINT8, SL_MIN_VERSION, SLV_68),
341 SLE_VAR(GoodsEntry, last_speed, SLE_UINT8),
342 SLE_VAR(GoodsEntry, last_age, SLE_UINT8),
343 SLEG_CONDVAR("cargo_feeder_share", _cargo_feeder_share, SLE_FILE_U32 | SLE_VAR_I64, SLV_14, SLV_65),
344 SLEG_CONDVAR("cargo_feeder_share", _cargo_feeder_share, SLE_INT64, SLV_65, SLV_68),
345 SLE_CONDVAR(GoodsEntry, amount_fract, SLE_UINT8, SLV_150, SL_MAX_VERSION),
346 SLEG_CONDREFLIST("packets", _packets, REF_CARGO_PACKET, SLV_68, SLV_183),
347 SLEG_CONDVAR("old_num_dests", _old_num_dests, SLE_UINT32, SLV_183, SLV_SAVELOAD_LIST_LENGTH),
348 SLE_CONDVAR(GoodsEntry, cargo.reserved_count, SLE_UINT, SLV_181, SL_MAX_VERSION),
349 SLE_CONDVAR(GoodsEntry, link_graph, SLE_UINT16, SLV_183, SL_MAX_VERSION),
350 SLE_CONDVAR(GoodsEntry, node, SLE_UINT16, SLV_183, SL_MAX_VERSION),
351 SLEG_CONDVAR("old_num_flows", _old_num_flows, SLE_UINT32, SLV_183, SLV_SAVELOAD_LIST_LENGTH),
352 SLE_CONDVAR(GoodsEntry, max_waiting_cargo, SLE_UINT32, SLV_183, SL_MAX_VERSION),
353 SLEG_CONDSTRUCTLIST("flow", SlStationFlow, SLV_183, SL_MAX_VERSION),
354 SLEG_CONDSTRUCTLIST("cargo", SlStationCargo, SLV_183, SL_MAX_VERSION),
357 inline const static SaveLoadCompatTable compat_description = _station_goods_sl_compat;
360 * Get the number of cargoes used by this savegame version.
361 * @return The number of cargoes used by this savegame version.
363 size_t GetNumCargo() const
365 if (IsSavegameVersionBefore(SLV_55)) return 12;
366 if (IsSavegameVersionBefore(SLV_EXTEND_CARGOTYPES)) return 32;
367 if (IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH)) return NUM_CARGO;
368 /* Read from the savegame how long the list is. */
369 return SlGetStructListLength(NUM_CARGO);
372 void Save(BaseStation *bst) const override
374 Station *st = Station::From(bst);
376 SlSetStructListLength(NUM_CARGO);
378 for (GoodsEntry &ge : st->goods) {
379 SlObject(&ge, this->GetDescription());
383 void Load(BaseStation *bst) const override
385 Station *st = Station::From(bst);
387 /* Before savegame version 161, persistent storages were not stored in a pool. */
388 if (IsSavegameVersionBefore(SLV_161) && !IsSavegameVersionBefore(SLV_145) && st->facilities & FACIL_AIRPORT) {
389 /* Store the old persistent storage. The GRFID will be added later. */
390 assert(PersistentStorage::CanAllocateItem());
391 st->airport.psa = new PersistentStorage(0, 0, 0);
392 std::copy(std::begin(_old_st_persistent_storage.storage), std::end(_old_st_persistent_storage.storage), std::begin(st->airport.psa->storage));
395 auto end = std::next(std::begin(st->goods), std::min(this->GetNumCargo(), std::size(st->goods)));
396 for (auto it = std::begin(st->goods); it != end; ++it) {
397 GoodsEntry &ge = *it;
398 SlObject(&ge, this->GetLoadDescription());
399 if (IsSavegameVersionBefore(SLV_183)) {
400 SwapPackets(&ge);
402 if (IsSavegameVersionBefore(SLV_68)) {
403 AssignBit(ge.status, GoodsEntry::GES_ACCEPTANCE, HasBit(_waiting_acceptance, 15));
404 if (GB(_waiting_acceptance, 0, 12) != 0) {
405 /* In old versions, enroute_from used 0xFF as INVALID_STATION */
406 StationID source = (IsSavegameVersionBefore(SLV_7) && _cargo_source == 0xFF) ? INVALID_STATION : _cargo_source;
408 /* Make sure we can allocate the CargoPacket. This is safe
409 * as there can only be ~64k stations and 32 cargoes in these
410 * savegame versions. As the CargoPacketPool has more than
411 * 16 million entries; it fits by an order of magnitude. */
412 assert(CargoPacket::CanAllocateItem());
414 /* Don't construct the packet with station here, because that'll fail with old savegames */
415 CargoPacket *cp = new CargoPacket(GB(_waiting_acceptance, 0, 12), _cargo_periods, source, _cargo_source_xy, _cargo_feeder_share);
416 ge.cargo.Append(cp, INVALID_STATION);
417 SetBit(ge.status, GoodsEntry::GES_RATING);
423 void FixPointers(BaseStation *bst) const override
425 Station *st = Station::From(bst);
427 size_t num_cargo = IsSavegameVersionBefore(SLV_55) ? 12 : IsSavegameVersionBefore(SLV_EXTEND_CARGOTYPES) ? 32 : NUM_CARGO;
428 auto end = std::next(std::begin(st->goods), std::min(num_cargo, std::size(st->goods)));
429 for (auto it = std::begin(st->goods); it != end; ++it) {
430 GoodsEntry &ge = *it;
431 if (IsSavegameVersionBefore(SLV_183)) {
432 SwapPackets(&ge); // We have to swap back again to be in the format pre-183 expects.
433 SlObject(&ge, this->GetDescription());
434 SwapPackets(&ge);
435 } else {
436 SlObject(&ge, this->GetDescription());
442 static const SaveLoad _old_station_desc[] = {
443 SLE_CONDVAR(Station, xy, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6),
444 SLE_CONDVAR(Station, xy, SLE_UINT32, SLV_6, SL_MAX_VERSION),
445 SLE_CONDVAR(Station, train_station.tile, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6),
446 SLE_CONDVAR(Station, train_station.tile, SLE_UINT32, SLV_6, SL_MAX_VERSION),
447 SLE_CONDVAR(Station, airport.tile, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6),
448 SLE_CONDVAR(Station, airport.tile, SLE_UINT32, SLV_6, SL_MAX_VERSION),
449 SLE_REF(Station, town, REF_TOWN),
450 SLE_VAR(Station, train_station.w, SLE_FILE_U8 | SLE_VAR_U16),
451 SLE_CONDVAR(Station, train_station.h, SLE_FILE_U8 | SLE_VAR_U16, SLV_2, SL_MAX_VERSION),
453 SLE_VAR(Station, string_id, SLE_STRINGID),
454 SLE_CONDSSTR(Station, name, SLE_STR | SLF_ALLOW_CONTROL, SLV_84, SL_MAX_VERSION),
455 SLE_CONDVAR(Station, indtype, SLE_UINT8, SLV_103, SL_MAX_VERSION),
456 SLE_CONDVAR(Station, had_vehicle_of_type, SLE_FILE_U16 | SLE_VAR_U8, SL_MIN_VERSION, SLV_122),
457 SLE_CONDVAR(Station, had_vehicle_of_type, SLE_UINT8, SLV_122, SL_MAX_VERSION),
459 SLE_VAR(Station, time_since_load, SLE_UINT8),
460 SLE_VAR(Station, time_since_unload, SLE_UINT8),
461 SLE_VAR(Station, delete_ctr, SLE_UINT8),
462 SLE_VAR(Station, owner, SLE_UINT8),
463 SLE_VAR(Station, facilities, SLE_UINT8),
464 SLE_VAR(Station, airport.type, SLE_UINT8),
465 SLE_CONDVAR(Station, airport.flags, SLE_VAR_U64 | SLE_FILE_U16, SL_MIN_VERSION, SLV_3),
466 SLE_CONDVAR(Station, airport.flags, SLE_VAR_U64 | SLE_FILE_U32, SLV_3, SLV_46),
467 SLE_CONDVAR(Station, airport.flags, SLE_UINT64, SLV_46, SL_MAX_VERSION),
469 SLE_CONDVAR(Station, last_vehicle_type, SLE_UINT8, SLV_26, SL_MAX_VERSION),
471 SLE_CONDVAR(Station, build_date, SLE_FILE_U16 | SLE_VAR_I32, SLV_3, SLV_31),
472 SLE_CONDVAR(Station, build_date, SLE_INT32, SLV_31, SL_MAX_VERSION),
474 SLE_CONDREF(Station, bus_stops, REF_ROADSTOPS, SLV_6, SL_MAX_VERSION),
475 SLE_CONDREF(Station, truck_stops, REF_ROADSTOPS, SLV_6, SL_MAX_VERSION),
477 /* Used by newstations for graphic variations */
478 SLE_CONDVAR(Station, random_bits, SLE_UINT16, SLV_27, SL_MAX_VERSION),
479 SLE_CONDVAR(Station, waiting_triggers, SLE_UINT8, SLV_27, SL_MAX_VERSION),
480 SLEG_CONDVAR("num_specs", SlStationSpecList<StationSpec>::last_num_specs, SLE_UINT8, SLV_27, SL_MAX_VERSION),
482 SLE_CONDREFLIST(Station, loading_vehicles, REF_VEHICLE, SLV_57, SL_MAX_VERSION),
484 SLEG_STRUCTLIST("goods", SlStationGoods),
485 SLEG_CONDSTRUCTLIST("speclist", SlStationSpecList<StationSpec>, SLV_27, SL_MAX_VERSION),
488 struct STNSChunkHandler : ChunkHandler {
489 STNSChunkHandler() : ChunkHandler('STNS', CH_READONLY) {}
491 void Load() const override
493 const std::vector<SaveLoad> slt = SlCompatTableHeader(_old_station_desc, _old_station_sl_compat);
495 _cargo_source_xy = 0;
496 _cargo_periods = 0;
497 _cargo_feeder_share = 0;
499 int index;
500 while ((index = SlIterateArray()) != -1) {
501 Station *st = new (index) Station();
503 _waiting_acceptance = 0;
504 SlObject(st, slt);
508 void FixPointers() const override
510 /* From SLV_123 we store stations in STNN; before that in STNS. So do not
511 * fix pointers when the version is SLV_123 or up, as that would fix
512 * pointers twice: once in STNN chunk and once here. */
513 if (!IsSavegameVersionBefore(SLV_123)) return;
515 for (Station *st : Station::Iterate()) {
516 SlObject(st, _old_station_desc);
521 class SlRoadStopTileData : public DefaultSaveLoadHandler<SlRoadStopTileData, BaseStation> {
522 public:
523 inline static const SaveLoad description[] = {
524 SLE_VAR(RoadStopTileData, tile, SLE_UINT32),
525 SLE_VAR(RoadStopTileData, random_bits, SLE_UINT8),
526 SLE_VAR(RoadStopTileData, animation_frame, SLE_UINT8),
528 inline const static SaveLoadCompatTable compat_description = {};
530 void Save(BaseStation *bst) const override
532 SlSetStructListLength(bst->custom_roadstop_tile_data.size());
533 for (uint i = 0; i < bst->custom_roadstop_tile_data.size(); i++) {
534 SlObject(&bst->custom_roadstop_tile_data[i], this->GetDescription());
538 void Load(BaseStation *bst) const override
540 uint32_t num_tiles = (uint32_t)SlGetStructListLength(UINT32_MAX);
541 bst->custom_roadstop_tile_data.resize(num_tiles);
542 for (uint i = 0; i < num_tiles; i++) {
543 SlObject(&bst->custom_roadstop_tile_data[i], this->GetLoadDescription());
549 * SaveLoad handler for the BaseStation, which all other stations / waypoints
550 * make use of.
552 class SlStationBase : public DefaultSaveLoadHandler<SlStationBase, BaseStation> {
553 public:
554 inline static const SaveLoad description[] = {
555 SLE_VAR(BaseStation, xy, SLE_UINT32),
556 SLE_REF(BaseStation, town, REF_TOWN),
557 SLE_VAR(BaseStation, string_id, SLE_STRINGID),
558 SLE_SSTR(BaseStation, name, SLE_STR | SLF_ALLOW_CONTROL),
559 SLE_VAR(BaseStation, delete_ctr, SLE_UINT8),
560 SLE_VAR(BaseStation, owner, SLE_UINT8),
561 SLE_VAR(BaseStation, facilities, SLE_UINT8),
562 SLE_VAR(BaseStation, build_date, SLE_INT32),
564 /* Used by newstations for graphic variations */
565 SLE_VAR(BaseStation, random_bits, SLE_UINT16),
566 SLE_VAR(BaseStation, waiting_triggers, SLE_UINT8),
567 SLEG_CONDVAR("num_specs", SlStationSpecList<StationSpec>::last_num_specs, SLE_UINT8, SL_MIN_VERSION, SLV_SAVELOAD_LIST_LENGTH),
569 inline const static SaveLoadCompatTable compat_description = _station_base_sl_compat;
571 void Save(BaseStation *bst) const override
573 SlObject(bst, this->GetDescription());
576 void Load(BaseStation *bst) const override
578 SlObject(bst, this->GetLoadDescription());
581 void FixPointers(BaseStation *bst) const override
583 SlObject(bst, this->GetDescription());
588 * SaveLoad handler for a normal station (read: not a waypoint).
590 class SlStationNormal : public DefaultSaveLoadHandler<SlStationNormal, BaseStation> {
591 public:
592 inline static const SaveLoad description[] = {
593 SLEG_STRUCT("base", SlStationBase),
594 SLE_VAR(Station, train_station.tile, SLE_UINT32),
595 SLE_VAR(Station, train_station.w, SLE_FILE_U8 | SLE_VAR_U16),
596 SLE_VAR(Station, train_station.h, SLE_FILE_U8 | SLE_VAR_U16),
598 SLE_REF(Station, bus_stops, REF_ROADSTOPS),
599 SLE_REF(Station, truck_stops, REF_ROADSTOPS),
600 SLE_CONDVAR(Station, ship_station.tile, SLE_UINT32, SLV_MULTITILE_DOCKS, SL_MAX_VERSION),
601 SLE_CONDVAR(Station, ship_station.w, SLE_FILE_U8 | SLE_VAR_U16, SLV_MULTITILE_DOCKS, SL_MAX_VERSION),
602 SLE_CONDVAR(Station, ship_station.h, SLE_FILE_U8 | SLE_VAR_U16, SLV_MULTITILE_DOCKS, SL_MAX_VERSION),
603 SLE_CONDVAR(Station, docking_station.tile, SLE_UINT32, SLV_MULTITILE_DOCKS, SL_MAX_VERSION),
604 SLE_CONDVAR(Station, docking_station.w, SLE_FILE_U8 | SLE_VAR_U16, SLV_MULTITILE_DOCKS, SL_MAX_VERSION),
605 SLE_CONDVAR(Station, docking_station.h, SLE_FILE_U8 | SLE_VAR_U16, SLV_MULTITILE_DOCKS, SL_MAX_VERSION),
606 SLE_VAR(Station, airport.tile, SLE_UINT32),
607 SLE_CONDVAR(Station, airport.w, SLE_FILE_U8 | SLE_VAR_U16, SLV_140, SL_MAX_VERSION),
608 SLE_CONDVAR(Station, airport.h, SLE_FILE_U8 | SLE_VAR_U16, SLV_140, SL_MAX_VERSION),
609 SLE_VAR(Station, airport.type, SLE_UINT8),
610 SLE_CONDVAR(Station, airport.layout, SLE_UINT8, SLV_145, SL_MAX_VERSION),
611 SLE_VAR(Station, airport.flags, SLE_UINT64),
612 SLE_CONDVAR(Station, airport.rotation, SLE_UINT8, SLV_145, SL_MAX_VERSION),
613 SLEG_CONDARR("storage", _old_st_persistent_storage.storage, SLE_UINT32, 16, SLV_145, SLV_161),
614 SLE_CONDREF(Station, airport.psa, REF_STORAGE, SLV_161, SL_MAX_VERSION),
616 SLE_VAR(Station, indtype, SLE_UINT8),
618 SLE_VAR(Station, time_since_load, SLE_UINT8),
619 SLE_VAR(Station, time_since_unload, SLE_UINT8),
620 SLE_VAR(Station, last_vehicle_type, SLE_UINT8),
621 SLE_VAR(Station, had_vehicle_of_type, SLE_UINT8),
622 SLE_REFLIST(Station, loading_vehicles, REF_VEHICLE),
623 SLE_CONDVAR(Station, always_accepted, SLE_FILE_U32 | SLE_VAR_U64, SLV_127, SLV_EXTEND_CARGOTYPES),
624 SLE_CONDVAR(Station, always_accepted, SLE_UINT64, SLV_EXTEND_CARGOTYPES, SL_MAX_VERSION),
625 SLEG_CONDSTRUCTLIST("speclist", SlRoadStopTileData, SLV_NEWGRF_ROAD_STOPS, SLV_ROAD_STOP_TILE_DATA),
626 SLEG_STRUCTLIST("goods", SlStationGoods),
628 inline const static SaveLoadCompatTable compat_description = _station_normal_sl_compat;
630 void Save(BaseStation *bst) const override
632 if ((bst->facilities & FACIL_WAYPOINT) != 0) return;
633 SlObject(bst, this->GetDescription());
636 void Load(BaseStation *bst) const override
638 if ((bst->facilities & FACIL_WAYPOINT) != 0) return;
639 SlObject(bst, this->GetLoadDescription());
642 void FixPointers(BaseStation *bst) const override
644 if ((bst->facilities & FACIL_WAYPOINT) != 0) return;
645 SlObject(bst, this->GetDescription());
649 class SlStationWaypoint : public DefaultSaveLoadHandler<SlStationWaypoint, BaseStation> {
650 public:
651 inline static const SaveLoad description[] = {
652 SLEG_STRUCT("base", SlStationBase),
653 SLE_VAR(Waypoint, town_cn, SLE_UINT16),
655 SLE_CONDVAR(Waypoint, train_station.tile, SLE_UINT32, SLV_124, SL_MAX_VERSION),
656 SLE_CONDVAR(Waypoint, train_station.w, SLE_FILE_U8 | SLE_VAR_U16, SLV_124, SL_MAX_VERSION),
657 SLE_CONDVAR(Waypoint, train_station.h, SLE_FILE_U8 | SLE_VAR_U16, SLV_124, SL_MAX_VERSION),
658 SLE_CONDVAR(Waypoint, waypoint_flags, SLE_UINT16, SLV_ROAD_WAYPOINTS, SL_MAX_VERSION),
659 SLE_CONDVAR(Waypoint, road_waypoint_area.tile, SLE_UINT32, SLV_ROAD_WAYPOINTS, SL_MAX_VERSION),
660 SLE_CONDVAR(Waypoint, road_waypoint_area.w, SLE_FILE_U8 | SLE_VAR_U16, SLV_ROAD_WAYPOINTS, SL_MAX_VERSION),
661 SLE_CONDVAR(Waypoint, road_waypoint_area.h, SLE_FILE_U8 | SLE_VAR_U16, SLV_ROAD_WAYPOINTS, SL_MAX_VERSION),
663 inline const static SaveLoadCompatTable compat_description = _station_waypoint_sl_compat;
665 void Save(BaseStation *bst) const override
667 if ((bst->facilities & FACIL_WAYPOINT) == 0) return;
668 SlObject(bst, this->GetDescription());
671 void Load(BaseStation *bst) const override
673 if ((bst->facilities & FACIL_WAYPOINT) == 0) return;
674 SlObject(bst, this->GetLoadDescription());
677 void FixPointers(BaseStation *bst) const override
679 if ((bst->facilities & FACIL_WAYPOINT) == 0) return;
680 SlObject(bst, this->GetDescription());
684 static const SaveLoad _station_desc[] = {
685 SLE_SAVEBYTE(BaseStation, facilities),
686 SLEG_STRUCT("normal", SlStationNormal),
687 SLEG_STRUCT("waypoint", SlStationWaypoint),
688 SLEG_CONDSTRUCTLIST("speclist", SlStationSpecList<StationSpec>, SLV_27, SL_MAX_VERSION),
689 SLEG_CONDSTRUCTLIST("roadstopspeclist", SlStationSpecList<RoadStopSpec>, SLV_NEWGRF_ROAD_STOPS, SL_MAX_VERSION),
690 SLEG_CONDSTRUCTLIST("roadstoptiledata", SlRoadStopTileData, SLV_ROAD_STOP_TILE_DATA, SL_MAX_VERSION),
693 struct STNNChunkHandler : ChunkHandler {
694 STNNChunkHandler() : ChunkHandler('STNN', CH_TABLE) {}
696 void Save() const override
698 SlTableHeader(_station_desc);
700 /* Write the stations */
701 for (BaseStation *st : BaseStation::Iterate()) {
702 SlSetArrayIndex(st->index);
703 SlObject(st, _station_desc);
708 void Load() const override
710 const std::vector<SaveLoad> slt = SlCompatTableHeader(_station_desc, _station_sl_compat);
712 _old_num_flows = 0;
714 int index;
715 while ((index = SlIterateArray()) != -1) {
716 bool waypoint = (SlReadByte() & FACIL_WAYPOINT) != 0;
718 BaseStation *bst = waypoint ? (BaseStation *)new (index) Waypoint() : new (index) Station();
719 SlObject(bst, slt);
723 void FixPointers() const override
725 /* From SLV_123 we store stations in STNN; before that in STNS. So do not
726 * fix pointers when the version is below SLV_123, as that would fix
727 * pointers twice: once in STNS chunk and once here. */
728 if (IsSavegameVersionBefore(SLV_123)) return;
730 for (BaseStation *bst : BaseStation::Iterate()) {
731 SlObject(bst, _station_desc);
736 struct ROADChunkHandler : ChunkHandler {
737 ROADChunkHandler() : ChunkHandler('ROAD', CH_TABLE) {}
739 void Save() const override
741 SlTableHeader(_roadstop_desc);
743 for (RoadStop *rs : RoadStop::Iterate()) {
744 SlSetArrayIndex(rs->index);
745 SlObject(rs, _roadstop_desc);
749 void Load() const override
751 const std::vector<SaveLoad> slt = SlCompatTableHeader(_roadstop_desc, _roadstop_sl_compat);
753 int index;
755 while ((index = SlIterateArray()) != -1) {
756 RoadStop *rs = new (index) RoadStop(INVALID_TILE);
758 SlObject(rs, slt);
762 void FixPointers() const override
764 for (RoadStop *rs : RoadStop::Iterate()) {
765 SlObject(rs, _roadstop_desc);
770 static const STNSChunkHandler STNS;
771 static const STNNChunkHandler STNN;
772 static const ROADChunkHandler ROAD;
773 static const ChunkHandlerRef station_chunk_handlers[] = {
774 STNS,
775 STNN,
776 ROAD,
779 extern const ChunkHandlerTable _station_chunk_handlers(station_chunk_handlers);