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/>.
8 /** @file station_sl.cpp Code handling saving and loading of stations. */
10 #include "../stdafx.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"
21 #include "table/strings.h"
23 #include "../safeguards.h"
26 * Update the buoy orders to be waypoint orders.
27 * @param o the order 'list' to check.
29 static void UpdateWaypointOrder(Order
*o
)
31 if (!o
->IsType(OT_GOTO_STATION
)) return;
33 const Station
*st
= Station::Get(o
->GetDestination());
34 if ((st
->had_vehicle_of_type
& HVOT_WAYPOINT
) == 0) return;
36 o
->MakeGoToWaypoint(o
->GetDestination());
40 * Perform all steps to upgrade from the old station buoys to the new version
41 * that uses waypoints. This includes some old saveload mechanics.
43 void MoveBuoysToWaypoints()
45 /* Buoy orders become waypoint orders */
46 for (OrderList
*ol
: OrderList::Iterate()) {
47 VehicleType vt
= ol
->GetFirstSharedVehicle()->type
;
48 if (vt
!= VEH_SHIP
&& vt
!= VEH_TRAIN
) continue;
50 for (Order
*o
= ol
->GetFirstOrder(); o
!= nullptr; o
= o
->next
) UpdateWaypointOrder(o
);
53 for (Vehicle
*v
: Vehicle::Iterate()) {
54 VehicleType vt
= v
->type
;
55 if (vt
!= VEH_SHIP
&& vt
!= VEH_TRAIN
) continue;
57 UpdateWaypointOrder(&v
->current_order
);
60 /* Now make the stations waypoints */
61 for (Station
*st
: Station::Iterate()) {
62 if ((st
->had_vehicle_of_type
& HVOT_WAYPOINT
) == 0) continue;
64 StationID index
= st
->index
;
65 TileIndex xy
= st
->xy
;
66 Town
*town
= st
->town
;
67 StringID string_id
= st
->string_id
;
68 std::string name
= st
->name
;
69 Date build_date
= st
->build_date
;
70 /* TTDPatch could use "buoys with rail station" for rail waypoints */
71 bool train
= st
->train_station
.tile
!= INVALID_TILE
;
72 TileArea train_st
= st
->train_station
;
74 /* Delete the station, so we can make it a real waypoint. */
77 /* Stations and waypoints are in the same pool, so if a station
78 * is deleted there must be place for a Waypoint. */
79 assert(Waypoint::CanAllocateItem());
80 Waypoint
*wp
= new (index
) Waypoint(xy
);
82 wp
->string_id
= train
? STR_SV_STNAME_WAYPOINT
: STR_SV_STNAME_BUOY
;
84 wp
->delete_ctr
= 0; // Just reset delete counter for once.
85 wp
->build_date
= build_date
;
86 wp
->owner
= train
? GetTileOwner(xy
) : OWNER_NONE
;
88 if (IsInsideBS(string_id
, STR_SV_STNAME_BUOY
, 9)) wp
->town_cn
= string_id
- STR_SV_STNAME_BUOY
;
91 /* When we make a rail waypoint of the station, convert the map as well. */
92 for (TileIndex t
: train_st
) {
93 if (!IsTileType(t
, MP_STATION
) || GetStationIndex(t
) != index
) continue;
95 SB(_me
[t
].m6
, 3, 3, STATION_WAYPOINT
);
96 wp
->rect
.BeforeAddTile(t
, StationRect::ADD_FORCE
);
99 wp
->train_station
= train_st
;
100 wp
->facilities
|= FACIL_TRAIN
;
101 } else if (IsBuoyTile(xy
) && GetStationIndex(xy
) == index
) {
102 wp
->rect
.BeforeAddTile(xy
, StationRect::ADD_FORCE
);
103 wp
->facilities
|= FACIL_DOCK
;
108 void AfterLoadStations()
110 /* Update the speclists of all stations to point to the currently loaded custom stations. */
111 for (BaseStation
*st
: BaseStation::Iterate()) {
112 for (uint i
= 0; i
< st
->num_specs
; i
++) {
113 if (st
->speclist
[i
].grfid
== 0) continue;
115 st
->speclist
[i
].spec
= StationClass::GetByGrf(st
->speclist
[i
].grfid
, st
->speclist
[i
].localidx
, nullptr);
118 if (Station::IsExpected(st
)) {
119 Station
*sta
= Station::From(st
);
120 for (const RoadStop
*rs
= sta
->bus_stops
; rs
!= nullptr; rs
= rs
->next
) sta
->bus_station
.Add(rs
->xy
);
121 for (const RoadStop
*rs
= sta
->truck_stops
; rs
!= nullptr; rs
= rs
->next
) sta
->truck_station
.Add(rs
->xy
);
124 StationUpdateCachedTriggers(st
);
129 * (Re)building of road stop caches after loading a savegame.
131 void AfterLoadRoadStops()
133 /* First construct the drive through entries */
134 for (RoadStop
*rs
: RoadStop::Iterate()) {
135 if (IsDriveThroughStopTile(rs
->xy
)) rs
->MakeDriveThrough();
137 /* And then rebuild the data in those entries */
138 for (RoadStop
*rs
: RoadStop::Iterate()) {
139 if (!HasBit(rs
->status
, RoadStop::RSSFB_BASE_ENTRY
)) continue;
141 rs
->GetEntry(DIAGDIR_NE
)->Rebuild(rs
);
142 rs
->GetEntry(DIAGDIR_NW
)->Rebuild(rs
);
146 static const SaveLoad _roadstop_desc
[] = {
147 SLE_VAR(RoadStop
, xy
, SLE_UINT32
),
148 SLE_VAR(RoadStop
, status
, SLE_UINT8
),
149 SLE_REF(RoadStop
, next
, REF_ROADSTOPS
),
152 static uint16 _waiting_acceptance
;
153 static uint32 _old_num_flows
;
154 static uint16 _cargo_source
;
155 static uint32 _cargo_source_xy
;
156 static uint8 _cargo_days
;
157 static Money _cargo_feeder_share
;
159 std::list
<CargoPacket
*> _packets
;
160 uint32 _old_num_dests
;
162 struct FlowSaveLoad
{
163 FlowSaveLoad() : source(0), via(0), share(0), restricted(false) {}
170 typedef std::pair
<const StationID
, std::list
<CargoPacket
*> > StationCargoPair
;
172 static OldPersistentStorage _old_st_persistent_storage
;
175 * Swap the temporary packets with the packets without specific destination in
176 * the given goods entry. Assert that at least one of those is empty.
177 * @param ge Goods entry to swap with.
179 static void SwapPackets(GoodsEntry
*ge
)
181 StationCargoPacketMap
&ge_packets
= const_cast<StationCargoPacketMap
&>(*ge
->cargo
.Packets());
183 if (_packets
.empty()) {
184 std::map
<StationID
, std::list
<CargoPacket
*> >::iterator
it(ge_packets
.find(INVALID_STATION
));
185 if (it
== ge_packets
.end()) {
188 it
->second
.swap(_packets
);
191 assert(ge_packets
[INVALID_STATION
].empty());
192 ge_packets
[INVALID_STATION
].swap(_packets
);
196 class SlStationSpecList
: public DefaultSaveLoadHandler
<SlStationSpecList
, BaseStation
> {
198 inline static const SaveLoad description
[] = {
199 SLE_CONDVAR(StationSpecList
, grfid
, SLE_UINT32
, SLV_27
, SL_MAX_VERSION
),
200 SLE_CONDVAR(StationSpecList
, localidx
, SLE_UINT8
, SLV_27
, SL_MAX_VERSION
),
202 inline const static SaveLoadCompatTable compat_description
= _station_spec_list_sl_compat
;
204 void Save(BaseStation
*bst
) const override
206 SlSetStructListLength(bst
->num_specs
);
207 for (uint i
= 0; i
< bst
->num_specs
; i
++) {
208 SlObject(&bst
->speclist
[i
], this->GetDescription());
212 void Load(BaseStation
*bst
) const override
214 if (!IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH
)) {
215 bst
->num_specs
= (uint8
)SlGetStructListLength(UINT8_MAX
);
218 if (bst
->num_specs
!= 0) {
219 /* Allocate speclist memory when loading a game */
220 bst
->speclist
= CallocT
<StationSpecList
>(bst
->num_specs
);
221 for (uint i
= 0; i
< bst
->num_specs
; i
++) {
222 SlObject(&bst
->speclist
[i
], this->GetLoadDescription());
228 class SlStationCargo
: public DefaultSaveLoadHandler
<SlStationCargo
, GoodsEntry
> {
230 inline static const SaveLoad description
[] = {
231 SLE_VAR(StationCargoPair
, first
, SLE_UINT16
),
232 SLE_REFLIST(StationCargoPair
, second
, REF_CARGO_PACKET
),
234 inline const static SaveLoadCompatTable compat_description
= _station_cargo_sl_compat
;
236 void Save(GoodsEntry
*ge
) const override
238 SlSetStructListLength(ge
->cargo
.Packets()->MapSize());
239 for (StationCargoPacketMap::ConstMapIterator
it(ge
->cargo
.Packets()->begin()); it
!= ge
->cargo
.Packets()->end(); ++it
) {
240 SlObject(const_cast<StationCargoPacketMap::value_type
*>(&(*it
)), this->GetDescription());
244 void Load(GoodsEntry
*ge
) const override
246 size_t num_dests
= IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH
) ? _old_num_dests
: SlGetStructListLength(UINT32_MAX
);
248 StationCargoPair pair
;
249 for (uint j
= 0; j
< num_dests
; ++j
) {
250 SlObject(&pair
, this->GetLoadDescription());
251 const_cast<StationCargoPacketMap
&>(*(ge
->cargo
.Packets()))[pair
.first
].swap(pair
.second
);
252 assert(pair
.second
.empty());
256 void FixPointers(GoodsEntry
*ge
) const override
258 for (StationCargoPacketMap::ConstMapIterator it
= ge
->cargo
.Packets()->begin(); it
!= ge
->cargo
.Packets()->end(); ++it
) {
259 SlObject(const_cast<StationCargoPair
*>(&(*it
)), this->GetDescription());
264 class SlStationFlow
: public DefaultSaveLoadHandler
<SlStationFlow
, GoodsEntry
> {
266 inline static const SaveLoad description
[] = {
267 SLE_VAR(FlowSaveLoad
, source
, SLE_UINT16
),
268 SLE_VAR(FlowSaveLoad
, via
, SLE_UINT16
),
269 SLE_VAR(FlowSaveLoad
, share
, SLE_UINT32
),
270 SLE_CONDVAR(FlowSaveLoad
, restricted
, SLE_BOOL
, SLV_187
, SL_MAX_VERSION
),
272 inline const static SaveLoadCompatTable compat_description
= _station_flow_sl_compat
;
274 void Save(GoodsEntry
*ge
) const override
276 uint32 num_flows
= 0;
277 for (FlowStatMap::const_iterator
it(ge
->flows
.begin()); it
!= ge
->flows
.end(); ++it
) {
278 num_flows
+= (uint32
)it
->second
.GetShares()->size();
280 SlSetStructListLength(num_flows
);
282 for (FlowStatMap::const_iterator
outer_it(ge
->flows
.begin()); outer_it
!= ge
->flows
.end(); ++outer_it
) {
283 const FlowStat::SharesMap
*shares
= outer_it
->second
.GetShares();
284 uint32 sum_shares
= 0;
286 flow
.source
= outer_it
->first
;
287 for (FlowStat::SharesMap::const_iterator
inner_it(shares
->begin()); inner_it
!= shares
->end(); ++inner_it
) {
288 flow
.via
= inner_it
->second
;
289 flow
.share
= inner_it
->first
- sum_shares
;
290 flow
.restricted
= inner_it
->first
> outer_it
->second
.GetUnrestricted();
291 sum_shares
= inner_it
->first
;
292 assert(flow
.share
> 0);
293 SlObject(&flow
, this->GetDescription());
298 void Load(GoodsEntry
*ge
) const override
300 size_t num_flows
= IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH
) ? _old_num_flows
: SlGetStructListLength(UINT32_MAX
);
303 FlowStat
*fs
= nullptr;
304 StationID prev_source
= INVALID_STATION
;
305 for (uint32 j
= 0; j
< num_flows
; ++j
) {
306 SlObject(&flow
, this->GetLoadDescription());
307 if (fs
== nullptr || prev_source
!= flow
.source
) {
308 fs
= &(ge
->flows
.insert(std::make_pair(flow
.source
, FlowStat(flow
.via
, flow
.share
, flow
.restricted
))).first
->second
);
310 fs
->AppendShare(flow
.via
, flow
.share
, flow
.restricted
);
312 prev_source
= flow
.source
;
317 class SlStationGoods
: public DefaultSaveLoadHandler
<SlStationGoods
, BaseStation
> {
319 #if defined(_MSC_VER) && (_MSC_VER == 1915 || _MSC_VER == 1916)
320 /* This table access private members of other classes; they have this
321 * class as friend. For MSVC CL 19.15 and 19.16 this doesn't work for
322 * "inline static const", so we are forced to wrap the table in a
323 * function. CL 19.16 is the latest for VS2017. */
324 inline static const SaveLoad description
[] = {{}};
325 SaveLoadTable
GetDescription() const override
{
329 static const SaveLoad description
[] = {
330 SLEG_CONDVAR("waiting_acceptance", _waiting_acceptance
, SLE_UINT16
, SL_MIN_VERSION
, SLV_68
),
331 SLE_CONDVAR(GoodsEntry
, status
, SLE_UINT8
, SLV_68
, SL_MAX_VERSION
),
332 SLE_VAR(GoodsEntry
, time_since_pickup
, SLE_UINT8
),
333 SLE_VAR(GoodsEntry
, rating
, SLE_UINT8
),
334 SLEG_CONDVAR("cargo_source", _cargo_source
, SLE_FILE_U8
| SLE_VAR_U16
, SL_MIN_VERSION
, SLV_7
),
335 SLEG_CONDVAR("cargo_source", _cargo_source
, SLE_UINT16
, SLV_7
, SLV_68
),
336 SLEG_CONDVAR("cargo_source_xy", _cargo_source_xy
, SLE_UINT32
, SLV_44
, SLV_68
),
337 SLEG_CONDVAR("cargo_days", _cargo_days
, SLE_UINT8
, SL_MIN_VERSION
, SLV_68
),
338 SLE_VAR(GoodsEntry
, last_speed
, SLE_UINT8
),
339 SLE_VAR(GoodsEntry
, last_age
, SLE_UINT8
),
340 SLEG_CONDVAR("cargo_feeder_share", _cargo_feeder_share
, SLE_FILE_U32
| SLE_VAR_I64
, SLV_14
, SLV_65
),
341 SLEG_CONDVAR("cargo_feeder_share", _cargo_feeder_share
, SLE_INT64
, SLV_65
, SLV_68
),
342 SLE_CONDVAR(GoodsEntry
, amount_fract
, SLE_UINT8
, SLV_150
, SL_MAX_VERSION
),
343 SLEG_CONDREFLIST("packets", _packets
, REF_CARGO_PACKET
, SLV_68
, SLV_183
),
344 SLEG_CONDVAR("old_num_dests", _old_num_dests
, SLE_UINT32
, SLV_183
, SLV_SAVELOAD_LIST_LENGTH
),
345 SLE_CONDVAR(GoodsEntry
, cargo
.reserved_count
, SLE_UINT
, SLV_181
, SL_MAX_VERSION
),
346 SLE_CONDVAR(GoodsEntry
, link_graph
, SLE_UINT16
, SLV_183
, SL_MAX_VERSION
),
347 SLE_CONDVAR(GoodsEntry
, node
, SLE_UINT16
, SLV_183
, SL_MAX_VERSION
),
348 SLEG_CONDVAR("old_num_flows", _old_num_flows
, SLE_UINT32
, SLV_183
, SLV_SAVELOAD_LIST_LENGTH
),
349 SLE_CONDVAR(GoodsEntry
, max_waiting_cargo
, SLE_UINT32
, SLV_183
, SL_MAX_VERSION
),
350 SLEG_CONDSTRUCTLIST("flow", SlStationFlow
, SLV_183
, SL_MAX_VERSION
),
351 SLEG_CONDSTRUCTLIST("cargo", SlStationCargo
, SLV_183
, SL_MAX_VERSION
),
353 #if defined(_MSC_VER) && (_MSC_VER == 1915 || _MSC_VER == 1916)
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 (CargoID i
= 0; i
< NUM_CARGO
; i
++) {
379 SlObject(&st
->goods
[i
], 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 memcpy(st
->airport
.psa
->storage
, _old_st_persistent_storage
.storage
, sizeof(_old_st_persistent_storage
.storage
));
395 size_t num_cargo
= this->GetNumCargo();
396 for (CargoID i
= 0; i
< num_cargo
; i
++) {
397 GoodsEntry
*ge
= &st
->goods
[i
];
398 SlObject(ge
, this->GetLoadDescription());
399 if (IsSavegameVersionBefore(SLV_183
)) {
402 if (IsSavegameVersionBefore(SLV_68
)) {
403 SB(ge
->status
, GoodsEntry::GES_ACCEPTANCE
, 1, 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_days
, source
, _cargo_source_xy
, _cargo_source_xy
, _cargo_feeder_share
);
416 ge
->cargo
.Append(cp
, INVALID_STATION
);
417 SB(ge
->status
, GoodsEntry::GES_RATING
, 1, 1);
423 void FixPointers(BaseStation
*bst
) const override
425 Station
*st
= Station::From(bst
);
427 uint num_cargo
= IsSavegameVersionBefore(SLV_55
) ? 12 : IsSavegameVersionBefore(SLV_EXTEND_CARGOTYPES
) ? 32 : NUM_CARGO
;
428 for (CargoID i
= 0; i
< num_cargo
; i
++) {
429 GoodsEntry
*ge
= &st
->goods
[i
];
430 if (IsSavegameVersionBefore(SLV_183
)) {
431 SwapPackets(ge
); // We have to swap back again to be in the format pre-183 expects.
432 SlObject(ge
, this->GetDescription());
435 SlObject(ge
, this->GetDescription());
441 static const SaveLoad _old_station_desc
[] = {
442 SLE_CONDVAR(Station
, xy
, SLE_FILE_U16
| SLE_VAR_U32
, SL_MIN_VERSION
, SLV_6
),
443 SLE_CONDVAR(Station
, xy
, SLE_UINT32
, SLV_6
, SL_MAX_VERSION
),
444 SLE_CONDVAR(Station
, train_station
.tile
, SLE_FILE_U16
| SLE_VAR_U32
, SL_MIN_VERSION
, SLV_6
),
445 SLE_CONDVAR(Station
, train_station
.tile
, SLE_UINT32
, SLV_6
, SL_MAX_VERSION
),
446 SLE_CONDVAR(Station
, airport
.tile
, SLE_FILE_U16
| SLE_VAR_U32
, SL_MIN_VERSION
, SLV_6
),
447 SLE_CONDVAR(Station
, airport
.tile
, SLE_UINT32
, SLV_6
, SL_MAX_VERSION
),
448 SLE_REF(Station
, town
, REF_TOWN
),
449 SLE_VAR(Station
, train_station
.w
, SLE_FILE_U8
| SLE_VAR_U16
),
450 SLE_CONDVAR(Station
, train_station
.h
, SLE_FILE_U8
| SLE_VAR_U16
, SLV_2
, SL_MAX_VERSION
),
452 SLE_VAR(Station
, string_id
, SLE_STRINGID
),
453 SLE_CONDSSTR(Station
, name
, SLE_STR
| SLF_ALLOW_CONTROL
, SLV_84
, SL_MAX_VERSION
),
454 SLE_CONDVAR(Station
, indtype
, SLE_UINT8
, SLV_103
, SL_MAX_VERSION
),
455 SLE_CONDVAR(Station
, had_vehicle_of_type
, SLE_FILE_U16
| SLE_VAR_U8
, SL_MIN_VERSION
, SLV_122
),
456 SLE_CONDVAR(Station
, had_vehicle_of_type
, SLE_UINT8
, SLV_122
, SL_MAX_VERSION
),
458 SLE_VAR(Station
, time_since_load
, SLE_UINT8
),
459 SLE_VAR(Station
, time_since_unload
, SLE_UINT8
),
460 SLE_VAR(Station
, delete_ctr
, SLE_UINT8
),
461 SLE_VAR(Station
, owner
, SLE_UINT8
),
462 SLE_VAR(Station
, facilities
, SLE_UINT8
),
463 SLE_VAR(Station
, airport
.type
, SLE_UINT8
),
464 SLE_CONDVAR(Station
, airport
.flags
, SLE_VAR_U64
| SLE_FILE_U16
, SL_MIN_VERSION
, SLV_3
),
465 SLE_CONDVAR(Station
, airport
.flags
, SLE_VAR_U64
| SLE_FILE_U32
, SLV_3
, SLV_46
),
466 SLE_CONDVAR(Station
, airport
.flags
, SLE_UINT64
, SLV_46
, SL_MAX_VERSION
),
468 SLE_CONDVAR(Station
, last_vehicle_type
, SLE_UINT8
, SLV_26
, SL_MAX_VERSION
),
470 SLE_CONDVAR(Station
, build_date
, SLE_FILE_U16
| SLE_VAR_I32
, SLV_3
, SLV_31
),
471 SLE_CONDVAR(Station
, build_date
, SLE_INT32
, SLV_31
, SL_MAX_VERSION
),
473 SLE_CONDREF(Station
, bus_stops
, REF_ROADSTOPS
, SLV_6
, SL_MAX_VERSION
),
474 SLE_CONDREF(Station
, truck_stops
, REF_ROADSTOPS
, SLV_6
, SL_MAX_VERSION
),
476 /* Used by newstations for graphic variations */
477 SLE_CONDVAR(Station
, random_bits
, SLE_UINT16
, SLV_27
, SL_MAX_VERSION
),
478 SLE_CONDVAR(Station
, waiting_triggers
, SLE_UINT8
, SLV_27
, SL_MAX_VERSION
),
479 SLE_CONDVAR(Station
, num_specs
, SLE_UINT8
, SLV_27
, SL_MAX_VERSION
),
481 SLE_CONDREFLIST(Station
, loading_vehicles
, REF_VEHICLE
, SLV_57
, SL_MAX_VERSION
),
483 SLEG_STRUCTLIST("goods", SlStationGoods
),
484 SLEG_CONDSTRUCTLIST("speclist", SlStationSpecList
, SLV_27
, SL_MAX_VERSION
),
487 struct STNSChunkHandler
: ChunkHandler
{
488 STNSChunkHandler() : ChunkHandler('STNS', CH_READONLY
) {}
490 void Load() const override
492 const std::vector
<SaveLoad
> slt
= SlCompatTableHeader(_old_station_desc
, _old_station_sl_compat
);
494 _cargo_source_xy
= 0;
496 _cargo_feeder_share
= 0;
499 while ((index
= SlIterateArray()) != -1) {
500 Station
*st
= new (index
) Station();
502 _waiting_acceptance
= 0;
507 void FixPointers() const override
509 /* From SLV_123 we store stations in STNN; before that in STNS. So do not
510 * fix pointers when the version is SLV_123 or up, as that would fix
511 * pointers twice: once in STNN chunk and once here. */
512 if (!IsSavegameVersionBefore(SLV_123
)) return;
514 for (Station
*st
: Station::Iterate()) {
515 SlObject(st
, _old_station_desc
);
521 * SaveLoad handler for the BaseStation, which all other stations / waypoints
524 class SlStationBase
: public DefaultSaveLoadHandler
<SlStationBase
, BaseStation
> {
526 inline static const SaveLoad description
[] = {
527 SLE_VAR(BaseStation
, xy
, SLE_UINT32
),
528 SLE_REF(BaseStation
, town
, REF_TOWN
),
529 SLE_VAR(BaseStation
, string_id
, SLE_STRINGID
),
530 SLE_SSTR(BaseStation
, name
, SLE_STR
| SLF_ALLOW_CONTROL
),
531 SLE_VAR(BaseStation
, delete_ctr
, SLE_UINT8
),
532 SLE_VAR(BaseStation
, owner
, SLE_UINT8
),
533 SLE_VAR(BaseStation
, facilities
, SLE_UINT8
),
534 SLE_VAR(BaseStation
, build_date
, SLE_INT32
),
536 /* Used by newstations for graphic variations */
537 SLE_VAR(BaseStation
, random_bits
, SLE_UINT16
),
538 SLE_VAR(BaseStation
, waiting_triggers
, SLE_UINT8
),
539 SLE_CONDVAR(BaseStation
, num_specs
, SLE_UINT8
, SL_MIN_VERSION
, SLV_SAVELOAD_LIST_LENGTH
),
541 inline const static SaveLoadCompatTable compat_description
= _station_base_sl_compat
;
543 void Save(BaseStation
*bst
) const override
545 SlObject(bst
, this->GetDescription());
548 void Load(BaseStation
*bst
) const override
550 SlObject(bst
, this->GetLoadDescription());
553 void FixPointers(BaseStation
*bst
) const override
555 SlObject(bst
, this->GetDescription());
560 * SaveLoad handler for a normal station (read: not a waypoint).
562 class SlStationNormal
: public DefaultSaveLoadHandler
<SlStationNormal
, BaseStation
> {
564 inline static const SaveLoad description
[] = {
565 SLEG_STRUCT("base", SlStationBase
),
566 SLE_VAR(Station
, train_station
.tile
, SLE_UINT32
),
567 SLE_VAR(Station
, train_station
.w
, SLE_FILE_U8
| SLE_VAR_U16
),
568 SLE_VAR(Station
, train_station
.h
, SLE_FILE_U8
| SLE_VAR_U16
),
570 SLE_REF(Station
, bus_stops
, REF_ROADSTOPS
),
571 SLE_REF(Station
, truck_stops
, REF_ROADSTOPS
),
572 SLE_CONDVAR(Station
, ship_station
.tile
, SLE_UINT32
, SLV_MULTITILE_DOCKS
, SL_MAX_VERSION
),
573 SLE_CONDVAR(Station
, ship_station
.w
, SLE_FILE_U8
| SLE_VAR_U16
, SLV_MULTITILE_DOCKS
, SL_MAX_VERSION
),
574 SLE_CONDVAR(Station
, ship_station
.h
, SLE_FILE_U8
| SLE_VAR_U16
, SLV_MULTITILE_DOCKS
, SL_MAX_VERSION
),
575 SLE_CONDVAR(Station
, docking_station
.tile
, SLE_UINT32
, SLV_MULTITILE_DOCKS
, SL_MAX_VERSION
),
576 SLE_CONDVAR(Station
, docking_station
.w
, SLE_FILE_U8
| SLE_VAR_U16
, SLV_MULTITILE_DOCKS
, SL_MAX_VERSION
),
577 SLE_CONDVAR(Station
, docking_station
.h
, SLE_FILE_U8
| SLE_VAR_U16
, SLV_MULTITILE_DOCKS
, SL_MAX_VERSION
),
578 SLE_VAR(Station
, airport
.tile
, SLE_UINT32
),
579 SLE_CONDVAR(Station
, airport
.w
, SLE_FILE_U8
| SLE_VAR_U16
, SLV_140
, SL_MAX_VERSION
),
580 SLE_CONDVAR(Station
, airport
.h
, SLE_FILE_U8
| SLE_VAR_U16
, SLV_140
, SL_MAX_VERSION
),
581 SLE_VAR(Station
, airport
.type
, SLE_UINT8
),
582 SLE_CONDVAR(Station
, airport
.layout
, SLE_UINT8
, SLV_145
, SL_MAX_VERSION
),
583 SLE_VAR(Station
, airport
.flags
, SLE_UINT64
),
584 SLE_CONDVAR(Station
, airport
.rotation
, SLE_UINT8
, SLV_145
, SL_MAX_VERSION
),
585 SLEG_CONDARR("storage", _old_st_persistent_storage
.storage
, SLE_UINT32
, 16, SLV_145
, SLV_161
),
586 SLE_CONDREF(Station
, airport
.psa
, REF_STORAGE
, SLV_161
, SL_MAX_VERSION
),
588 SLE_VAR(Station
, indtype
, SLE_UINT8
),
590 SLE_VAR(Station
, time_since_load
, SLE_UINT8
),
591 SLE_VAR(Station
, time_since_unload
, SLE_UINT8
),
592 SLE_VAR(Station
, last_vehicle_type
, SLE_UINT8
),
593 SLE_VAR(Station
, had_vehicle_of_type
, SLE_UINT8
),
594 SLE_REFLIST(Station
, loading_vehicles
, REF_VEHICLE
),
595 SLE_CONDVAR(Station
, always_accepted
, SLE_FILE_U32
| SLE_VAR_U64
, SLV_127
, SLV_EXTEND_CARGOTYPES
),
596 SLE_CONDVAR(Station
, always_accepted
, SLE_UINT64
, SLV_EXTEND_CARGOTYPES
, SL_MAX_VERSION
),
597 SLEG_STRUCTLIST("goods", SlStationGoods
),
599 inline const static SaveLoadCompatTable compat_description
= _station_normal_sl_compat
;
601 void Save(BaseStation
*bst
) const
603 if ((bst
->facilities
& FACIL_WAYPOINT
) != 0) return;
604 SlObject(bst
, this->GetDescription());
607 void Load(BaseStation
*bst
) const
609 if ((bst
->facilities
& FACIL_WAYPOINT
) != 0) return;
610 SlObject(bst
, this->GetLoadDescription());
613 void FixPointers(BaseStation
*bst
) const
615 if ((bst
->facilities
& FACIL_WAYPOINT
) != 0) return;
616 SlObject(bst
, this->GetDescription());
620 class SlStationWaypoint
: public DefaultSaveLoadHandler
<SlStationWaypoint
, BaseStation
> {
622 inline static const SaveLoad description
[] = {
623 SLEG_STRUCT("base", SlStationBase
),
624 SLE_VAR(Waypoint
, town_cn
, SLE_UINT16
),
626 SLE_CONDVAR(Waypoint
, train_station
.tile
, SLE_UINT32
, SLV_124
, SL_MAX_VERSION
),
627 SLE_CONDVAR(Waypoint
, train_station
.w
, SLE_FILE_U8
| SLE_VAR_U16
, SLV_124
, SL_MAX_VERSION
),
628 SLE_CONDVAR(Waypoint
, train_station
.h
, SLE_FILE_U8
| SLE_VAR_U16
, SLV_124
, SL_MAX_VERSION
),
630 inline const static SaveLoadCompatTable compat_description
= _station_waypoint_sl_compat
;
632 void Save(BaseStation
*bst
) const
634 if ((bst
->facilities
& FACIL_WAYPOINT
) == 0) return;
635 SlObject(bst
, this->GetDescription());
638 void Load(BaseStation
*bst
) const
640 if ((bst
->facilities
& FACIL_WAYPOINT
) == 0) return;
641 SlObject(bst
, this->GetLoadDescription());
644 void FixPointers(BaseStation
*bst
) const
646 if ((bst
->facilities
& FACIL_WAYPOINT
) == 0) return;
647 SlObject(bst
, this->GetDescription());
651 static const SaveLoad _station_desc
[] = {
652 SLE_SAVEBYTE(BaseStation
, facilities
),
653 SLEG_STRUCT("normal", SlStationNormal
),
654 SLEG_STRUCT("waypoint", SlStationWaypoint
),
655 SLEG_CONDSTRUCTLIST("speclist", SlStationSpecList
, SLV_27
, SL_MAX_VERSION
),
658 struct STNNChunkHandler
: ChunkHandler
{
659 STNNChunkHandler() : ChunkHandler('STNN', CH_TABLE
) {}
661 void Save() const override
663 SlTableHeader(_station_desc
);
665 /* Write the stations */
666 for (BaseStation
*st
: BaseStation::Iterate()) {
667 SlSetArrayIndex(st
->index
);
668 SlObject(st
, _station_desc
);
673 void Load() const override
675 const std::vector
<SaveLoad
> slt
= SlCompatTableHeader(_station_desc
, _station_sl_compat
);
680 while ((index
= SlIterateArray()) != -1) {
681 bool waypoint
= (SlReadByte() & FACIL_WAYPOINT
) != 0;
683 BaseStation
*bst
= waypoint
? (BaseStation
*)new (index
) Waypoint() : new (index
) Station();
688 void FixPointers() const override
690 /* From SLV_123 we store stations in STNN; before that in STNS. So do not
691 * fix pointers when the version is below SLV_123, as that would fix
692 * pointers twice: once in STNS chunk and once here. */
693 if (IsSavegameVersionBefore(SLV_123
)) return;
695 for (BaseStation
*bst
: BaseStation::Iterate()) {
696 SlObject(bst
, _station_desc
);
701 struct ROADChunkHandler
: ChunkHandler
{
702 ROADChunkHandler() : ChunkHandler('ROAD', CH_TABLE
) {}
704 void Save() const override
706 SlTableHeader(_roadstop_desc
);
708 for (RoadStop
*rs
: RoadStop::Iterate()) {
709 SlSetArrayIndex(rs
->index
);
710 SlObject(rs
, _roadstop_desc
);
714 void Load() const override
716 const std::vector
<SaveLoad
> slt
= SlCompatTableHeader(_roadstop_desc
, _roadstop_sl_compat
);
720 while ((index
= SlIterateArray()) != -1) {
721 RoadStop
*rs
= new (index
) RoadStop(INVALID_TILE
);
727 void FixPointers() const override
729 for (RoadStop
*rs
: RoadStop::Iterate()) {
730 SlObject(rs
, _roadstop_desc
);
735 static const STNSChunkHandler STNS
;
736 static const STNNChunkHandler STNN
;
737 static const ROADChunkHandler ROAD
;
738 static const ChunkHandlerRef station_chunk_handlers
[] = {
744 extern const ChunkHandlerTable
_station_chunk_handlers(station_chunk_handlers
);