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"
20 #include "../newgrf_roadstop.h"
21 #include "../timer/timer_game_calendar.h"
23 #include "table/strings.h"
25 #include "../safeguards.h"
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());
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. */
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
);
84 wp
->string_id
= train
? STR_SV_STNAME_WAYPOINT
: STR_SV_STNAME_BUOY
;
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
;
93 /* When we make a rail waypoint of the station, convert the map as well. */
94 for (TileIndex t
: train_st
) {
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) {}
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()) {
195 it
->second
.swap(_packets
);
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
> {
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
> {
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
> {
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;
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
);
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
;
323 fs
->AppendShare(flow
.via
, flow
.share
, flow
.restricted
);
325 prev_source
= flow
.source
;
330 class SlStationGoods
: public DefaultSaveLoadHandler
<SlStationGoods
, BaseStation
> {
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
)) {
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());
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;
497 _cargo_feeder_share
= 0;
500 while ((index
= SlIterateArray()) != -1) {
501 Station
*st
= new (index
) Station();
503 _waiting_acceptance
= 0;
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
> {
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
552 class SlStationBase
: public DefaultSaveLoadHandler
<SlStationBase
, BaseStation
> {
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
> {
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
> {
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
);
715 while ((index
= SlIterateArray()) != -1) {
716 bool waypoint
= (SlReadByte() & FACIL_WAYPOINT
) != 0;
718 BaseStation
*bst
= waypoint
? (BaseStation
*)new (index
) Waypoint() : new (index
) Station();
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
);
755 while ((index
= SlIterateArray()) != -1) {
756 RoadStop
*rs
= new (index
) RoadStop(INVALID_TILE
);
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
[] = {
779 extern const ChunkHandlerTable
_station_chunk_handlers(station_chunk_handlers
);