OpenTTD Source 20250312-master-gcdcc6b491d
waypoint_sl.cpp
Go to the documentation of this file.
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 */
7
10#include "../stdafx.h"
11#include "../waypoint_base.h"
12#include "../debug.h"
13#include "../newgrf_station.h"
14#include "../vehicle_base.h"
15#include "../town.h"
16#include "../newgrf.h"
17#include "../timer/timer_game_calendar.h"
18
19#include "table/strings.h"
20
21#include "saveload_internal.h"
22
23#include "../safeguards.h"
24
25using OldWaypointID = uint16_t;
26
29 OldWaypointID index;
30 TileIndex xy;
31 TownID town_index;
32 Town *town;
33 uint16_t town_cn;
34 StringID string_id;
35 std::string name;
36 uint8_t delete_ctr;
37 TimerGameCalendar::Date build_date;
38 uint8_t localidx;
39 uint32_t grfid;
40 const StationSpec *spec;
41 Owner owner;
42
43 StationID new_index;
44};
45
47static std::vector<OldWaypoint> _old_waypoints;
48
54{
55 if (!o->IsType(OT_GOTO_WAYPOINT)) return;
56
57 for (OldWaypoint &wp : _old_waypoints) {
58 if (wp.index != o->GetDestination()) continue;
59
60 o->SetDestination(wp.new_index);
61 return;
62 }
63}
64
70{
71 /* In version 17, ground type is moved from m2 to m4 for depots and
72 * waypoints to make way for storing the index in m2. The custom graphics
73 * id which was stored in m4 is now saved as a grf/id reference in the
74 * waypoint struct. */
76 for (OldWaypoint &wp : _old_waypoints) {
77 if (wp.delete_ctr != 0) continue; // The waypoint was deleted
78
79 /* Waypoint indices were not added to the map prior to this. */
80 Tile tile = wp.xy;
81 tile.m2() = wp.index;
82
83 if (HasBit(tile.m3(), 4)) {
84 wp.spec = StationClass::Get(STAT_CLASS_WAYP)->GetSpec(tile.m4() + 1);
85 }
86 }
87 } else {
88 /* As of version 17, we recalculate the custom graphic ID of waypoints
89 * from the GRF ID / station index. */
90 for (OldWaypoint &wp : _old_waypoints) {
91 const auto specs = StationClass::Get(STAT_CLASS_WAYP)->Specs();
92 auto found = std::ranges::find_if(specs, [&wp](const StationSpec *spec) { return spec != nullptr && spec->grf_prop.grfid == wp.grfid && spec->grf_prop.local_id == wp.localidx; });
93 if (found != std::end(specs)) wp.spec = *found;
94 }
95 }
96
97 if (!Waypoint::CanAllocateItem(_old_waypoints.size())) SlError(STR_ERROR_TOO_MANY_STATIONS_LOADING);
98
99 /* All saveload conversions have been done. Create the new waypoints! */
100 for (OldWaypoint &wp : _old_waypoints) {
101 TileIndex t = wp.xy;
102 /* Sometimes waypoint (sign) locations became disconnected from their actual location in
103 * the map array. If this is the case, try to locate the actual location in the map array */
104 if (!IsTileType(t, MP_RAILWAY) || GetRailTileType(t) != 2 /* RAIL_TILE_WAYPOINT */ || Tile(t).m2() != wp.index) {
105 Debug(sl, 0, "Found waypoint tile {} with invalid position", t);
106 t = INVALID_TILE;
107 for (auto tile : Map::Iterate()) {
108 if (IsTileType(tile, MP_RAILWAY) && GetRailTileType(tile) == 2 /* RAIL_TILE_WAYPOINT */ && tile.m2() == wp.index) {
109 t = TileIndex(tile);
110 Debug(sl, 0, "Found actual waypoint position at {}", TileIndex(tile));
111 break;
112 }
113 }
114 }
115 if (t == INVALID_TILE) {
116 SlErrorCorrupt("Waypoint with invalid tile");
117 }
118
119 Waypoint *new_wp = new Waypoint(t);
120 new_wp->town = wp.town;
121 new_wp->town_cn = wp.town_cn;
122 new_wp->name = wp.name;
123 new_wp->delete_ctr = 0; // Just reset delete counter for once.
124 new_wp->build_date = wp.build_date;
125 new_wp->owner = wp.owner;
126 new_wp->string_id = STR_SV_STNAME_WAYPOINT;
127
128 /* The tile might've been reserved! */
129 Tile tile(t);
130 bool reserved = !IsSavegameVersionBefore(SLV_100) && HasBit(tile.m5(), 4);
131
132 /* The tile really has our waypoint, so reassign the map array */
133 MakeRailWaypoint(tile, GetTileOwner(tile), new_wp->index, (Axis)GB(tile.m5(), 0, 1), 0, GetRailType(tile));
135 new_wp->owner = GetTileOwner(tile);
136
137 SetRailStationReservation(tile, reserved);
138
139 if (wp.spec != nullptr) {
140 SetCustomStationSpecIndex(tile, AllocateSpecToStation(wp.spec, new_wp, true));
141 }
142 new_wp->rect.BeforeAddTile(tile, StationRect::ADD_FORCE);
143
144 wp.new_index = new_wp->index;
145 }
146
147 /* Update the orders of vehicles */
148 for (OrderList *ol : OrderList::Iterate()) {
149 if (ol->GetFirstSharedVehicle()->type != VEH_TRAIN) continue;
150
151 for (Order *o = ol->GetFirstOrder(); o != nullptr; o = o->next) UpdateWaypointOrder(o);
152 }
153
154 for (Vehicle *v : Vehicle::Iterate()) {
155 if (v->type != VEH_TRAIN) continue;
156
157 UpdateWaypointOrder(&v->current_order);
158 }
159
160 ResetOldWaypoints();
161}
162
163void ResetOldWaypoints()
164{
165 _old_waypoints.clear();
166 _old_waypoints.shrink_to_fit();
167}
168
169static const SaveLoad _old_waypoint_desc[] = {
170 SLE_CONDVAR(OldWaypoint, xy, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6),
171 SLE_CONDVAR(OldWaypoint, xy, SLE_UINT32, SLV_6, SL_MAX_VERSION),
172 SLE_CONDVAR(OldWaypoint, town_index, SLE_UINT16, SLV_12, SLV_122),
174 SLE_CONDVAR(OldWaypoint, town_cn, SLE_FILE_U8 | SLE_VAR_U16, SLV_12, SLV_89),
175 SLE_CONDVAR(OldWaypoint, town_cn, SLE_UINT16, SLV_89, SL_MAX_VERSION),
176 SLE_CONDVAR(OldWaypoint, string_id, SLE_STRINGID, SL_MIN_VERSION, SLV_84),
178 SLE_VAR(OldWaypoint, delete_ctr, SLE_UINT8),
179
180 SLE_CONDVAR(OldWaypoint, build_date, SLE_FILE_U16 | SLE_VAR_I32, SLV_3, SLV_31),
181 SLE_CONDVAR(OldWaypoint, build_date, SLE_INT32, SLV_31, SL_MAX_VERSION),
182 SLE_CONDVAR(OldWaypoint, localidx, SLE_UINT8, SLV_3, SL_MAX_VERSION),
183 SLE_CONDVAR(OldWaypoint, grfid, SLE_UINT32, SLV_17, SL_MAX_VERSION),
184 SLE_CONDVAR(OldWaypoint, owner, SLE_UINT8, SLV_101, SL_MAX_VERSION),
185};
186
189
190 void Load() const override
191 {
192 /* Precaution for when loading failed and it didn't get cleared */
193 ResetOldWaypoints();
194
195 int index;
196
197 while ((index = SlIterateArray()) != -1) {
198 OldWaypoint *wp = &_old_waypoints.emplace_back();
199
200 wp->index = static_cast<OldWaypointID>(index);
201 SlObject(wp, _old_waypoint_desc);
202 }
203 }
204
205 void FixPointers() const override
206 {
207 for (OldWaypoint &wp : _old_waypoints) {
208 SlObject(&wp, _old_waypoint_desc);
209
211 wp.town_cn = (wp.string_id & 0xC000) == 0xC000 ? (wp.string_id >> 8) & 0x3F : 0;
212 wp.town = ClosestTownFromTile(wp.xy, UINT_MAX);
213 } else if (IsSavegameVersionBefore(SLV_122)) {
214 /* Only for versions 12 .. 122 */
215 if (!Town::IsValidID(wp.town_index)) {
216 /* Upon a corrupted waypoint we'll likely get here. The next step will be to
217 * loop over all Ptrs procs to nullptr the pointers. However, we don't know
218 * whether we're in the nullptr or "normal" Ptrs proc. So just clear the list
219 * of old waypoints we constructed and then this waypoint (and the other
220 * possibly corrupt ones) will not be queried in the nullptr Ptrs proc run. */
221 _old_waypoints.clear();
222 SlErrorCorrupt("Referencing invalid Town");
223 }
224 wp.town = Town::Get(wp.town_index);
225 }
227 wp.name = CopyFromOldName(wp.string_id);
228 }
229 }
230 }
231};
232
233static const CHKPChunkHandler CHKP;
234static const ChunkHandlerRef waypoint_chunk_handlers[] = {
235 CHKP,
236};
237
238extern const ChunkHandlerTable _waypoint_chunk_handlers(waypoint_chunk_handlers);
debug_inline constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
debug_inline static constexpr uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
constexpr Timpl & Set()
Set all bits.
std::span< Tspec *const > Specs() const
Get read-only span of specs of this class.
static NewGRFClass * Get(Tindex class_index)
Get a particular class.
const Tspec * GetSpec(uint index) const
Get a spec from the class at a given index.
Wrapper class to abstract away the way the tiles are stored.
Definition map_func.h:25
debug_inline uint16_t & m2()
Primarily used for indices to towns, industries and stations.
Definition map_func.h:125
debug_inline uint8_t & m4()
General purpose.
Definition map_func.h:149
debug_inline uint8_t & m3()
General purpose.
Definition map_func.h:137
debug_inline uint8_t & m5()
General purpose.
Definition map_func.h:161
#define Debug(category, level, format_string,...)
Output a line of debugging information.
Definition debug.h:37
Axis
Allow incrementing of DiagDirDiff variables.
int AllocateSpecToStation(const StationSpec *statspec, BaseStation *st, bool exec)
Allocate a StationSpec to a Station.
@ STAT_CLASS_WAYP
Waypoint class.
RailType GetRailType(Tile t)
Gets the rail type of the given tile.
Definition rail_map.h:115
static debug_inline RailTileType GetRailTileType(Tile t)
Returns the RailTileType (normal with or without signals, waypoint or depot).
Definition rail_map.h:36
void SlError(StringID string, const std::string &extra_msg)
Error handler.
Definition saveload.cpp:325
int SlIterateArray()
Iterate through the elements of an array and read the whole thing.
Definition saveload.cpp:663
void SlErrorCorrupt(const std::string &msg)
Error handler for corrupt savegames.
Definition saveload.cpp:355
void SlObject(void *object, const SaveLoadTable &slt)
Main SaveLoad function.
std::reference_wrapper< const ChunkHandler > ChunkHandlerRef
A reference to ChunkHandler.
Definition saveload.h:514
@ REF_TOWN
Load/save a reference to a town.
Definition saveload.h:609
std::span< const ChunkHandlerRef > ChunkHandlerTable
A table of ChunkHandler entries.
Definition saveload.h:517
#define SLE_CONDSSTR(base, variable, type, from, to)
Storage of a std::string in some savegame versions.
Definition saveload.h:938
#define SLE_CONDVAR(base, variable, type, from, to)
Storage of a variable in some savegame versions.
Definition saveload.h:873
bool IsSavegameVersionBefore(SaveLoadVersion major, uint8_t minor=0)
Checks whether the savegame is below major.
Definition saveload.h:1268
@ SLV_89
89 12160
Definition saveload.h:149
@ SLV_84
84 11822
Definition saveload.h:143
@ SLV_17
17.0 3212 17.1 3218
Definition saveload.h:62
@ SLV_100
100 13952
Definition saveload.h:163
@ SLV_6
6.0 1721 6.1 1768
Definition saveload.h:46
@ SLV_122
122 16855
Definition saveload.h:189
@ SLV_12
12.1 2046
Definition saveload.h:55
@ SL_MAX_VERSION
Highest possible saveload version.
Definition saveload.h:404
@ SL_MIN_VERSION
First savegame version.
Definition saveload.h:31
@ SLV_3
3.x lost
Definition saveload.h:36
@ SLV_101
101 14233
Definition saveload.h:164
@ SLV_31
31 5999
Definition saveload.h:80
#define SLE_CONDREF(base, variable, type, from, to)
Storage of a reference in some savegame versions.
Definition saveload.h:894
@ CH_READONLY
Chunk is never saved.
Definition saveload.h:464
#define SLE_VAR(base, variable, type)
Storage of a variable in every version of a savegame.
Definition saveload.h:1007
Declaration of functions used in more save/load files.
std::string CopyFromOldName(StringID id)
Copy and convert old custom names to UTF-8.
void SetCustomStationSpecIndex(Tile t, uint8_t specindex)
Set the custom station spec for this tile.
void SetRailStationReservation(Tile t, bool b)
Set the reservation state of the rail station.
void MakeRailWaypoint(Tile t, Owner o, StationID sid, Axis a, uint8_t section, RailType rt)
Make the given tile a rail waypoint tile.
@ Train
Station with train station.
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
StringID string_id
Default name (town area) of station.
StationFacilities facilities
The facilities that this station has.
Owner owner
The owner of this station.
uint8_t delete_ctr
Delete counter. If greater than 0 then it is decremented until it reaches 0; the waypoint is then is ...
StationRect rect
NOSAVE: Station spread out rectangle maintained by StationRect::xxx() functions.
Town * town
The town this station is associated with.
TimerGameCalendar::Date build_date
Date of construction.
std::string name
Custom name.
void Load() const override
Load the chunk.
void FixPointers() const override
Fix the pointers.
Handlers and description of chunk.
Definition saveload.h:468
uint16_t local_id
id defined by the grf file for this entity
uint32_t grfid
grfid that introduced this entity.
static IterateWrapper Iterate()
Returns an iterable ensemble of all Tiles.
Definition map_func.h:362
Helper structure to convert from the old waypoint system.
Shared order list linking together the linked list of orders and the list of vehicles sharing this or...
Definition order_base.h:258
DestinationID GetDestination() const
Gets the destination of this order.
Definition order_base.h:103
bool IsType(OrderType type) const
Check whether this order is of the given type.
Definition order_base.h:70
Order * next
Pointer to next order. If nullptr, end of list.
Definition order_base.h:59
void SetDestination(DestinationID destination)
Sets the destination of this order.
Definition order_base.h:110
static Pool::IterateWrapper< Titem > Iterate(size_t from=0)
Returns an iterable ensemble of all valid Titem.
static Titem * Get(auto index)
Returns Titem with given index.
Tindex index
Index of this pool item.
static bool CanAllocateItem(size_t n=1)
Helper functions so we can use PoolItem::Function() instead of _poolitem_pool.Function()
static bool IsValidID(auto index)
Tests whether given index can be used to get valid (non-nullptr) Titem.
SaveLoad type struct.
Definition saveload.h:722
Station specification.
VariableGRFFileProps grf_prop
Properties related the the grf file.
Town data structure.
Definition town.h:52
Vehicle data structure.
Representation of a waypoint.
uint16_t town_cn
The N-1th waypoint for this town (consecutive number)
Owner GetTileOwner(Tile tile)
Returns the owner of a tile.
Definition tile_map.h:178
static debug_inline bool IsTileType(Tile tile, TileType type)
Checks if a tile is a given tiletype.
Definition tile_map.h:150
constexpr TileIndex INVALID_TILE
The very nice invalid tile marker.
Definition tile_type.h:95
StrongType::Typedef< uint32_t, struct TileIndexTag, StrongType::Compare, StrongType::Integer, StrongType::Compatible< int32_t >, StrongType::Compatible< int64_t > > TileIndex
The index/ID of a Tile.
Definition tile_type.h:87
@ MP_RAILWAY
A railway.
Definition tile_type.h:49
Town * ClosestTownFromTile(TileIndex tile, uint threshold)
Return the town closest (in distance or ownership) to a given tile, within a given threshold.
@ VEH_TRAIN
Train vehicle type.
static void UpdateWaypointOrder(Order *o)
Update the waypoint orders to get the new waypoint ID.
static std::vector< OldWaypoint > _old_waypoints
Temporary array with old waypoints.
void MoveWaypointsToBaseStations()
Perform all steps to upgrade from the old waypoints to the new version that uses station.