4 * This file is part of OpenTTD.
5 * 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.
6 * 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.
7 * 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/>.
10 /** @file waypoint_sl.cpp Code handling saving and loading of waypoints */
12 #include "../stdafx.h"
13 #include "../waypoint_base.h"
14 #include "../newgrf_station.h"
15 #include "../vehicle_base.h"
17 #include "../newgrf.h"
19 #include "table/strings.h"
21 #include "saveload_internal.h"
23 #include "../safeguards.h"
25 /** Helper structure to convert from the old waypoint system. */
38 const StationSpec
*spec
;
44 /** Temporary array with old waypoints. */
45 static SmallVector
<OldWaypoint
, 16> _old_waypoints
;
48 * Update the waypoint orders to get the new waypoint ID.
49 * @param o the order 'list' to check.
51 static void UpdateWaypointOrder(Order
*o
)
53 if (!o
->IsType(OT_GOTO_WAYPOINT
)) return;
55 for (OldWaypoint
*wp
= _old_waypoints
.Begin(); wp
!= _old_waypoints
.End(); wp
++) {
56 if (wp
->index
!= o
->GetDestination()) continue;
58 o
->SetDestination((DestinationID
)wp
->new_index
);
64 * Perform all steps to upgrade from the old waypoints to the new version
65 * that uses station. This includes some old saveload mechanics.
67 void MoveWaypointsToBaseStations()
69 /* In version 17, ground type is moved from m2 to m4 for depots and
70 * waypoints to make way for storing the index in m2. The custom graphics
71 * id which was stored in m4 is now saved as a grf/id reference in the
73 if (IsSavegameVersionBefore(17)) {
74 for (OldWaypoint
*wp
= _old_waypoints
.Begin(); wp
!= _old_waypoints
.End(); wp
++) {
75 if (wp
->delete_ctr
!= 0) continue; // The waypoint was deleted
77 /* Waypoint indices were not added to the map prior to this. */
78 _m
[wp
->xy
].m2
= (StationID
)wp
->index
;
80 if (HasBit(_m
[wp
->xy
].m3
, 4)) {
81 wp
->spec
= StationClass::Get(STAT_CLASS_WAYP
)->GetSpec(_m
[wp
->xy
].m4
+ 1);
85 /* As of version 17, we recalculate the custom graphic ID of waypoints
86 * from the GRF ID / station index. */
87 for (OldWaypoint
*wp
= _old_waypoints
.Begin(); wp
!= _old_waypoints
.End(); wp
++) {
88 StationClass
* stclass
= StationClass::Get(STAT_CLASS_WAYP
);
89 for (uint i
= 0; i
< stclass
->GetSpecCount(); i
++) {
90 const StationSpec
*statspec
= stclass
->GetSpec(i
);
91 if (statspec
!= NULL
&& statspec
->grf_prop
.grffile
->grfid
== wp
->grfid
&& statspec
->grf_prop
.local_id
== wp
->localidx
) {
99 if (!Waypoint::CanAllocateItem(_old_waypoints
.Length())) SlError(STR_ERROR_TOO_MANY_STATIONS_LOADING
);
101 /* All saveload conversions have been done. Create the new waypoints! */
102 for (OldWaypoint
*wp
= _old_waypoints
.Begin(); wp
!= _old_waypoints
.End(); wp
++) {
103 Waypoint
*new_wp
= new Waypoint(wp
->xy
);
104 new_wp
->town
= wp
->town
;
105 new_wp
->town_cn
= wp
->town_cn
;
106 new_wp
->name
= wp
->name
;
107 new_wp
->delete_ctr
= 0; // Just reset delete counter for once.
108 new_wp
->build_date
= wp
->build_date
;
109 new_wp
->owner
= wp
->owner
;
111 new_wp
->string_id
= STR_SV_STNAME_WAYPOINT
;
113 TileIndex t
= wp
->xy
;
114 if (IsTileType(t
, MP_RAILWAY
) && GetRailTileType(t
) == 2 /* RAIL_TILE_WAYPOINT */ && _m
[t
].m2
== wp
->index
) {
115 /* The tile might've been reserved! */
116 bool reserved
= !IsSavegameVersionBefore(100) && HasBit(_m
[t
].m5
, 4);
118 /* The tile really has our waypoint, so reassign the map array */
119 MakeRailWaypoint(t
, GetTileOwner(t
), new_wp
->index
, (Axis
)GB(_m
[t
].m5
, 0, 1), 0, GetRailType(t
));
120 new_wp
->facilities
|= FACIL_TRAIN
;
121 new_wp
->owner
= GetTileOwner(t
);
123 SetRailStationReservation(t
, reserved
);
125 if (wp
->spec
!= NULL
) {
126 SetCustomStationSpecIndex(t
, AllocateSpecToStation(wp
->spec
, new_wp
, true));
128 new_wp
->rect
.BeforeAddTile(t
, StationRect::ADD_FORCE
);
131 wp
->new_index
= new_wp
->index
;
134 /* Update the orders of vehicles */
136 FOR_ALL_ORDER_LISTS(ol
) {
137 if (ol
->GetFirstSharedVehicle()->type
!= VEH_TRAIN
) continue;
139 for (Order
*o
= ol
->GetFirstOrder(); o
!= NULL
; o
= o
->next
) UpdateWaypointOrder(o
);
143 FOR_ALL_VEHICLES(v
) {
144 if (v
->type
!= VEH_TRAIN
) continue;
146 UpdateWaypointOrder(&v
->current_order
);
149 _old_waypoints
.Reset();
152 static const SaveLoad _old_waypoint_desc
[] = {
153 SLE_CONDVAR(OldWaypoint
, xy
, SLE_FILE_U16
| SLE_VAR_U32
, 0, 5),
154 SLE_CONDVAR(OldWaypoint
, xy
, SLE_UINT32
, 6, SL_MAX_VERSION
),
155 SLE_CONDVAR(OldWaypoint
, town_index
, SLE_UINT16
, 12, 121),
156 SLE_CONDREF(OldWaypoint
, town
, REF_TOWN
, 122, SL_MAX_VERSION
),
157 SLE_CONDVAR(OldWaypoint
, town_cn
, SLE_FILE_U8
| SLE_VAR_U16
, 12, 88),
158 SLE_CONDVAR(OldWaypoint
, town_cn
, SLE_UINT16
, 89, SL_MAX_VERSION
),
159 SLE_CONDVAR(OldWaypoint
, string_id
, SLE_STRINGID
, 0, 83),
160 SLE_CONDSTR(OldWaypoint
, name
, SLE_STR
, 0, 84, SL_MAX_VERSION
),
161 SLE_VAR(OldWaypoint
, delete_ctr
, SLE_UINT8
),
163 SLE_CONDVAR(OldWaypoint
, build_date
, SLE_FILE_U16
| SLE_VAR_I32
, 3, 30),
164 SLE_CONDVAR(OldWaypoint
, build_date
, SLE_INT32
, 31, SL_MAX_VERSION
),
165 SLE_CONDVAR(OldWaypoint
, localidx
, SLE_UINT8
, 3, SL_MAX_VERSION
),
166 SLE_CONDVAR(OldWaypoint
, grfid
, SLE_UINT32
, 17, SL_MAX_VERSION
),
167 SLE_CONDVAR(OldWaypoint
, owner
, SLE_UINT8
, 101, SL_MAX_VERSION
),
172 static void Load_WAYP()
174 /* Precaution for when loading failed and it didn't get cleared */
175 _old_waypoints
.Clear();
179 while ((index
= SlIterateArray()) != -1) {
180 OldWaypoint
*wp
= _old_waypoints
.Append();
181 memset(wp
, 0, sizeof(*wp
));
184 SlObject(wp
, _old_waypoint_desc
);
188 static void Ptrs_WAYP()
190 for (OldWaypoint
*wp
= _old_waypoints
.Begin(); wp
!= _old_waypoints
.End(); wp
++) {
191 SlObject(wp
, _old_waypoint_desc
);
193 if (IsSavegameVersionBefore(12)) {
194 wp
->town_cn
= (wp
->string_id
& 0xC000) == 0xC000 ? (wp
->string_id
>> 8) & 0x3F : 0;
195 wp
->town
= ClosestTownFromTile(wp
->xy
, UINT_MAX
);
196 } else if (IsSavegameVersionBefore(122)) {
197 /* Only for versions 12 .. 122 */
198 if (!Town::IsValidID(wp
->town_index
)) {
199 /* Upon a corrupted waypoint we'll likely get here. The next step will be to
200 * loop over all Ptrs procs to NULL the pointers. However, we don't know
201 * whether we're in the NULL or "normal" Ptrs proc. So just clear the list
202 * of old waypoints we constructed and then this waypoint (and the other
203 * possibly corrupt ones) will not be queried in the NULL Ptrs proc run. */
204 _old_waypoints
.Clear();
205 SlErrorCorrupt("Referencing invalid Town");
207 wp
->town
= Town::Get(wp
->town_index
);
209 if (IsSavegameVersionBefore(84)) {
210 wp
->name
= CopyFromOldName(wp
->string_id
);
215 extern const ChunkHandler _waypoint_chunk_handlers
[] = {
216 { 'CHKP', NULL
, Load_WAYP
, Ptrs_WAYP
, NULL
, CH_ARRAY
| CH_LAST
},