summaryrefslogtreecommitdiff
path: root/src/saveload
diff options
context:
space:
mode:
Diffstat (limited to 'src/saveload')
-rw-r--r--src/saveload/afterload.cpp1704
-rw-r--r--src/saveload/ai_sl.cpp79
-rw-r--r--src/saveload/animated_tile_sl.cpp56
-rw-r--r--src/saveload/autoreplace_sl.cpp51
-rw-r--r--src/saveload/cargopacket_sl.cpp45
-rw-r--r--src/saveload/cheat_sl.cpp37
-rw-r--r--src/saveload/company_sl.cpp240
-rw-r--r--src/saveload/depot_sl.cpp39
-rw-r--r--src/saveload/economy_sl.cpp58
-rw-r--r--src/saveload/engine_sl.cpp118
-rw-r--r--src/saveload/gamelog_sl.cpp161
-rw-r--r--src/saveload/group_sl.cpp43
-rw-r--r--src/saveload/industry_sl.cpp155
-rw-r--r--src/saveload/map_sl.cpp249
-rw-r--r--src/saveload/misc_sl.cpp105
-rw-r--r--src/saveload/newgrf_sl.cpp52
-rw-r--r--src/saveload/oldloader.cpp1736
-rw-r--r--src/saveload/order_sl.cpp203
-rw-r--r--src/saveload/saveload.cpp1899
-rw-r--r--src/saveload/saveload.h332
-rw-r--r--src/saveload/saveload_internal.h40
-rw-r--r--src/saveload/signs_sl.cpp49
-rw-r--r--src/saveload/station_sl.cpp227
-rw-r--r--src/saveload/strings_sl.cpp126
-rw-r--r--src/saveload/subsidy_sl.cpp43
-rw-r--r--src/saveload/town_sl.cpp210
-rw-r--r--src/saveload/vehicle_sl.cpp670
-rw-r--r--src/saveload/waypoint_sl.cpp96
28 files changed, 8823 insertions, 0 deletions
diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp
new file mode 100644
index 000000000..c4ecb664a
--- /dev/null
+++ b/src/saveload/afterload.cpp
@@ -0,0 +1,1704 @@
+/* $Id$ */
+
+/** @file afterload.cpp Code updating data after game load */
+
+#include "../stdafx.h"
+#include "../strings_type.h"
+#include "../tile_type.h"
+#include "../tile_map.h"
+#include "../map_type.h"
+#include "../road_map.h"
+#include "../town.h"
+#include "../void_map.h"
+#include "../signs_base.h"
+#include "../window_func.h"
+#include "../station_func.h"
+#include "../company_base.h"
+#include "../fios.h"
+#include "../date_func.h"
+#include "../engine_func.h"
+#include "../train.h"
+#include "../string_func.h"
+#include "../newgrf_config.h"
+#include "../gamelog.h"
+#include "../waypoint.h"
+#include "../station_map.h"
+#include "../station_base.h"
+#include "../tunnelbridge_map.h"
+#include "../debug.h"
+#include "../network/network.h"
+#include "../openttd.h"
+#include "../gfxinit.h"
+#include "../gfx_func.h"
+#include "../functions.h"
+#include "../industry_map.h"
+#include "../town_map.h"
+#include "../clear_map.h"
+#include "../engine_base.h"
+#include "../landscape.h"
+#include "../vehicle_func.h"
+#include "../newgrf_station.h"
+#include "../yapf/yapf.hpp"
+#include "../elrail_func.h"
+#include "../signs_func.h"
+#include "../newgrf_house.h"
+#include "../aircraft.h"
+#include "../unmovable_map.h"
+#include "../tree_map.h"
+#include "../company_func.h"
+#include "../command_func.h"
+#include "../road_cmd.h"
+
+#include "table/strings.h"
+
+#include "saveload.h"
+#include "saveload_internal.h"
+
+#include <signal.h>
+
+extern StringID _switch_mode_errorstr;
+extern Company *DoStartupNewCompany(bool is_ai);
+extern void InitializeRailGUI();
+
+/**
+ * Makes a tile canal or water depending on the surroundings.
+ *
+ * Must only be used for converting old savegames. Use WaterClass now.
+ *
+ * This as for example docks and shipdepots do not store
+ * whether the tile used to be canal or 'normal' water.
+ * @param t the tile to change.
+ * @param o the owner of the new tile.
+ * @param include_invalid_water_class Also consider WATER_CLASS_INVALID, i.e. industry tiles on land
+ */
+void SetWaterClassDependingOnSurroundings(TileIndex t, bool include_invalid_water_class)
+{
+ /* If the slope is not flat, we always assume 'land' (if allowed). Also for one-corner-raised-shores.
+ * Note: Wrt. autosloping under industry tiles this is the most fool-proof behaviour. */
+ if (GetTileSlope(t, NULL) != SLOPE_FLAT) {
+ if (include_invalid_water_class) {
+ SetWaterClass(t, WATER_CLASS_INVALID);
+ return;
+ } else {
+ NOT_REACHED();
+ }
+ }
+
+ /* Mark tile dirty in all cases */
+ MarkTileDirtyByTile(t);
+
+ if (TileX(t) == 0 || TileY(t) == 0 || TileX(t) == MapMaxX() - 1 || TileY(t) == MapMaxY() - 1) {
+ /* tiles at map borders are always WATER_CLASS_SEA */
+ SetWaterClass(t, WATER_CLASS_SEA);
+ return;
+ }
+
+ bool has_water = false;
+ bool has_canal = false;
+ bool has_river = false;
+
+ for (DiagDirection dir = DIAGDIR_BEGIN; dir < DIAGDIR_END; dir++) {
+ TileIndex neighbour = TileAddByDiagDir(t, dir);
+ switch (GetTileType(neighbour)) {
+ case MP_WATER:
+ /* clear water and shipdepots have already a WaterClass associated */
+ if (IsCoast(neighbour)) {
+ has_water = true;
+ } else if (!IsLock(neighbour)) {
+ switch (GetWaterClass(neighbour)) {
+ case WATER_CLASS_SEA: has_water = true; break;
+ case WATER_CLASS_CANAL: has_canal = true; break;
+ case WATER_CLASS_RIVER: has_river = true; break;
+ default: NOT_REACHED();
+ }
+ }
+ break;
+
+ case MP_RAILWAY:
+ /* Shore or flooded halftile */
+ has_water |= (GetRailGroundType(neighbour) == RAIL_GROUND_WATER);
+ break;
+
+ case MP_TREES:
+ /* trees on shore */
+ has_water |= (GetTreeGround(neighbour) == TREE_GROUND_SHORE);
+ break;
+
+ default: break;
+ }
+ }
+
+ if (!has_water && !has_canal && !has_river && include_invalid_water_class) {
+ SetWaterClass(t, WATER_CLASS_INVALID);
+ return;
+ }
+
+ if (has_river && !has_canal) {
+ SetWaterClass(t, WATER_CLASS_RIVER);
+ } else if (has_canal || !has_water) {
+ SetWaterClass(t, WATER_CLASS_CANAL);
+ } else {
+ SetWaterClass(t, WATER_CLASS_SEA);
+ }
+}
+
+static void ConvertTownOwner()
+{
+ for (TileIndex tile = 0; tile != MapSize(); tile++) {
+ switch (GetTileType(tile)) {
+ case MP_ROAD:
+ if (GB(_m[tile].m5, 4, 2) == ROAD_TILE_CROSSING && HasBit(_m[tile].m3, 7)) {
+ _m[tile].m3 = OWNER_TOWN;
+ }
+ /* FALLTHROUGH */
+
+ case MP_TUNNELBRIDGE:
+ if (GetTileOwner(tile) & 0x80) SetTileOwner(tile, OWNER_TOWN);
+ break;
+
+ default: break;
+ }
+ }
+}
+
+/* since savegame version 4.1, exclusive transport rights are stored at towns */
+static void UpdateExclusiveRights()
+{
+ Town *t;
+
+ FOR_ALL_TOWNS(t) {
+ t->exclusivity = INVALID_COMPANY;
+ }
+
+ /* FIXME old exclusive rights status is not being imported (stored in s->blocked_months_obsolete)
+ * could be implemented this way:
+ * 1.) Go through all stations
+ * Build an array town_blocked[ town_id ][ company_id ]
+ * that stores if at least one station in that town is blocked for a company
+ * 2.) Go through that array, if you find a town that is not blocked for
+ * one company, but for all others, then give him exclusivity.
+ */
+}
+
+static const byte convert_currency[] = {
+ 0, 1, 12, 8, 3,
+ 10, 14, 19, 4, 5,
+ 9, 11, 13, 6, 17,
+ 16, 22, 21, 7, 15,
+ 18, 2, 20,
+};
+
+/* since savegame version 4.2 the currencies are arranged differently */
+static void UpdateCurrencies()
+{
+ _settings_game.locale.currency = convert_currency[_settings_game.locale.currency];
+}
+
+/* Up to revision 1413 the invisible tiles at the southern border have not been
+ * MP_VOID, even though they should have. This is fixed by this function
+ */
+static void UpdateVoidTiles()
+{
+ uint i;
+
+ for (i = 0; i < MapMaxY(); ++i) MakeVoid(i * MapSizeX() + MapMaxX());
+ for (i = 0; i < MapSizeX(); ++i) MakeVoid(MapSizeX() * MapMaxY() + i);
+}
+
+/* since savegame version 6.0 each sign has an "owner", signs without owner (from old games are set to 255) */
+static void UpdateSignOwner()
+{
+ Sign *si;
+
+ FOR_ALL_SIGNS(si) si->owner = OWNER_NONE;
+}
+
+static inline RailType UpdateRailType(RailType rt, RailType min)
+{
+ return rt >= min ? (RailType)(rt + 1): rt;
+}
+
+/**
+ * Initialization of the windows and several kinds of caches.
+ * This is not done directly in AfterLoadGame because these
+ * functions require that all saveload conversions have been
+ * done. As people tend to add savegame conversion stuff after
+ * the intialization of the windows and caches quite some bugs
+ * had been made.
+ * Moving this out of there is both cleaner and less bug-prone.
+ *
+ * @return true if everything went according to plan, otherwise false.
+ */
+static bool InitializeWindowsAndCaches()
+{
+ /* Initialize windows */
+ ResetWindowSystem();
+ SetupColorsAndInitialWindow();
+
+ ResetViewportAfterLoadGame();
+
+ /* Update coordinates of the signs. */
+ UpdateAllStationVirtCoord();
+ UpdateAllSignVirtCoords();
+ UpdateAllTownVirtCoords();
+ UpdateAllWaypointSigns();
+
+ Company *c;
+ FOR_ALL_COMPANIES(c) {
+ /* For each company, verify (while loading a scenario) that the inauguration date is the current year and set it
+ * accordingly if it is not the case. No need to set it on companies that are not been used already,
+ * thus the MIN_YEAR (which is really nothing more than Zero, initialized value) test */
+ if (_file_to_saveload.filetype == FT_SCENARIO && c->inaugurated_year != MIN_YEAR) {
+ c->inaugurated_year = _cur_year;
+ }
+ }
+
+ SetCachedEngineCounts();
+
+ /* Towns have a noise controlled number of airports system
+ * So each airport's noise value must be added to the town->noise_reached value
+ * Reset each town's noise_reached value to '0' before. */
+ UpdateAirportsNoise();
+
+ CheckTrainsLengths();
+
+ return true;
+}
+
+/**
+ * Signal handler used to give a user a more useful report for crashes during
+ * the savegame loading process; especially when there's problems with the
+ * NewGRFs that are required by the savegame.
+ * @param unused well... unused
+ */
+void CDECL HandleSavegameLoadCrash(int unused)
+{
+ char buffer[8192];
+ char *p = buffer;
+ p += seprintf(p, lastof(buffer),
+ "Loading your savegame caused OpenTTD to crash.\n"
+ "This is most likely caused by a missing NewGRF or a NewGRF that has been\n"
+ "loaded as replacement for a missing NewGRF. OpenTTD cannot easily\n"
+ "determine whether a replacement NewGRF is of a newer or older version.\n"
+ "It will load a NewGRF with the same GRF ID as the missing NewGRF. This\n"
+ "means that if the author makes incompatible NewGRFs with the same GRF ID\n"
+ "OpenTTD cannot magically do the right thing. In most cases OpenTTD will\n"
+ "load the savegame and not crash, but this is an exception.\n"
+ "Please load the savegame with the appropriate NewGRFs. When loading a\n"
+ "savegame still crashes when all NewGRFs are found you should file a\n"
+ "bug report. The missing NewGRFs are:\n");
+
+ for (GRFConfig *c = _grfconfig; c != NULL; c = c->next) {
+ if (HasBit(c->flags, GCF_COMPATIBLE)) {
+ char buf[40];
+ md5sumToString(buf, lastof(buf), c->md5sum);
+ p += seprintf(p, lastof(buffer), "NewGRF %08X (%s) not found; checksum %s. Tried another NewGRF with same GRF ID\n", BSWAP32(c->grfid), c->filename, buf);
+ }
+ if (c->status == GCS_NOT_FOUND) {
+ char buf[40];
+ md5sumToString(buf, lastof(buf), c->md5sum);
+ p += seprintf(p, lastof(buffer), "NewGRF %08X (%s) not found; checksum %s\n", BSWAP32(c->grfid), c->filename, buf);
+ }
+ }
+
+ ShowInfo(buffer);
+}
+
+
+bool AfterLoadGame()
+{
+ typedef void (CDECL *SignalHandlerPointer)(int);
+ SignalHandlerPointer prev_segfault = signal(SIGSEGV, HandleSavegameLoadCrash);
+ SignalHandlerPointer prev_abort = signal(SIGABRT, HandleSavegameLoadCrash);
+
+ TileIndex map_size = MapSize();
+ Company *c;
+
+ if (CheckSavegameVersion(98)) GamelogOldver();
+
+ GamelogTestRevision();
+ GamelogTestMode();
+
+ if (CheckSavegameVersion(98)) GamelogGRFAddList(_grfconfig);
+
+ /* in very old versions, size of train stations was stored differently */
+ if (CheckSavegameVersion(2)) {
+ Station *st;
+ FOR_ALL_STATIONS(st) {
+ if (st->train_tile != 0 && st->trainst_h == 0) {
+ extern SavegameType _savegame_type;
+ uint n = _savegame_type == SGT_OTTD ? 4 : 3; // OTTD uses 4 bits per dimensions, TTD 3 bits
+ uint w = GB(st->trainst_w, n, n);
+ uint h = GB(st->trainst_w, 0, n);
+
+ if (GetRailStationAxis(st->train_tile) != AXIS_X) Swap(w, h);
+
+ st->trainst_w = w;
+ st->trainst_h = h;
+
+ assert(GetStationIndex(st->train_tile + TileDiffXY(w - 1, h - 1)) == st->index);
+ }
+ }
+ }
+
+ /* in version 2.1 of the savegame, town owner was unified. */
+ if (CheckSavegameVersionOldStyle(2, 1)) ConvertTownOwner();
+
+ /* from version 4.1 of the savegame, exclusive rights are stored at towns */
+ if (CheckSavegameVersionOldStyle(4, 1)) UpdateExclusiveRights();
+
+ /* from version 4.2 of the savegame, currencies are in a different order */
+ if (CheckSavegameVersionOldStyle(4, 2)) UpdateCurrencies();
+
+ /* from version 6.1 of the savegame, signs have an "owner" */
+ if (CheckSavegameVersionOldStyle(6, 1)) UpdateSignOwner();
+
+ /* In old version there seems to be a problem that water is owned by
+ * OWNER_NONE, not OWNER_WATER.. I can't replicate it for the current
+ * (4.3) version, so I just check when versions are older, and then
+ * walk through the whole map.. */
+ if (CheckSavegameVersionOldStyle(4, 3)) {
+ for (TileIndex t = 0; t < map_size; t++) {
+ if (IsTileType(t, MP_WATER) && GetTileOwner(t) >= MAX_COMPANIES) {
+ SetTileOwner(t, OWNER_WATER);
+ }
+ }
+ }
+
+ if (CheckSavegameVersion(84)) {
+ FOR_ALL_COMPANIES(c) {
+ c->name = CopyFromOldName(c->name_1);
+ if (c->name != NULL) c->name_1 = STR_SV_UNNAMED;
+ c->president_name = CopyFromOldName(c->president_name_1);
+ if (c->president_name != NULL) c->president_name_1 = SPECSTR_PRESIDENT_NAME;
+ }
+
+ Station *st;
+ FOR_ALL_STATIONS(st) {
+ st->name = CopyFromOldName(st->string_id);
+ /* generating new name would be too much work for little effect, use the station name fallback */
+ if (st->name != NULL) st->string_id = STR_SV_STNAME_FALLBACK;
+ }
+
+ Town *t;
+ FOR_ALL_TOWNS(t) {
+ t->name = CopyFromOldName(t->townnametype);
+ if (t->name != NULL) t->townnametype = SPECSTR_TOWNNAME_START + _settings_game.game_creation.town_name;
+ }
+
+ Waypoint *wp;
+ FOR_ALL_WAYPOINTS(wp) {
+ wp->name = CopyFromOldName(wp->string);
+ wp->string = STR_EMPTY;
+ }
+
+ for (uint i = 0; i < GetSignPoolSize(); i++) {
+ /* invalid signs are determined by si->ower == INVALID_COMPANY now */
+ Sign *si = GetSign(i);
+ if (!si->IsValid() && si->name != NULL) {
+ si->owner = OWNER_NONE;
+ }
+ }
+ }
+
+ /* From this point the old names array is cleared. */
+ ResetOldNames();
+
+ if (CheckSavegameVersion(106)) {
+ /* no station is determined by 'tile == INVALID_TILE' now (instead of '0') */
+ Station *st;
+ FOR_ALL_STATIONS(st) {
+ if (st->airport_tile == 0) st->airport_tile = INVALID_TILE;
+ if (st->dock_tile == 0) st->dock_tile = INVALID_TILE;
+ if (st->train_tile == 0) st->train_tile = INVALID_TILE;
+ }
+
+ /* the same applies to Company::location_of_HQ */
+ Company *c;
+ FOR_ALL_COMPANIES(c) {
+ if (c->location_of_HQ == 0 || (CheckSavegameVersion(4) && c->location_of_HQ == 0xFFFF)) {
+ c->location_of_HQ = INVALID_TILE;
+ }
+ }
+ }
+
+ /* convert road side to my format. */
+ if (_settings_game.vehicle.road_side) _settings_game.vehicle.road_side = 1;
+
+ /* Check if all NewGRFs are present, we are very strict in MP mode */
+ GRFListCompatibility gcf_res = IsGoodGRFConfigList();
+ if (_networking && gcf_res != GLC_ALL_GOOD) {
+ SetSaveLoadError(STR_NETWORK_ERR_CLIENT_NEWGRF_MISMATCH);
+ /* Restore the signals */
+ signal(SIGSEGV, prev_segfault);
+ signal(SIGABRT, prev_abort);
+ return false;
+ }
+
+ switch (gcf_res) {
+ case GLC_COMPATIBLE: _switch_mode_errorstr = STR_NEWGRF_COMPATIBLE_LOAD_WARNING; break;
+ case GLC_NOT_FOUND: _switch_mode_errorstr = STR_NEWGRF_DISABLED_WARNING; _pause_game = -1; break;
+ default: break;
+ }
+
+ /* Update current year
+ * must be done before loading sprites as some newgrfs check it */
+ SetDate(_date);
+
+ /* Force dynamic engines off when loading older savegames */
+ if (CheckSavegameVersion(95)) _settings_game.vehicle.dynamic_engines = 0;
+
+ /* Load the sprites */
+ GfxLoadSprites();
+ LoadStringWidthTable();
+
+ /* Copy temporary data to Engine pool */
+ CopyTempEngineData();
+
+ /* Connect front and rear engines of multiheaded trains and converts
+ * subtype to the new format */
+ if (CheckSavegameVersionOldStyle(17, 1)) ConvertOldMultiheadToNew();
+
+ /* Connect front and rear engines of multiheaded trains */
+ ConnectMultiheadedTrains();
+
+ /* reinit the landscape variables (landscape might have changed) */
+ InitializeLandscapeVariables(true);
+
+ /* Update all vehicles */
+ AfterLoadVehicles(true);
+
+ /* Update all waypoints */
+ if (CheckSavegameVersion(12)) FixOldWaypoints();
+
+ /* in version 2.2 of the savegame, we have new airports */
+ if (CheckSavegameVersionOldStyle(2, 2)) UpdateOldAircraft();
+
+ AfterLoadTown();
+
+ /* make sure there is a town in the game */
+ if (_game_mode == GM_NORMAL && !ClosestTownFromTile(0, UINT_MAX)) {
+ SetSaveLoadError(STR_NO_TOWN_IN_SCENARIO);
+ /* Restore the signals */
+ signal(SIGSEGV, prev_segfault);
+ signal(SIGABRT, prev_abort);
+ return false;
+ }
+
+ /* The void tiles on the southern border used to belong to a wrong class (pre 4.3).
+ * This problem appears in savegame version 21 too, see r3455. But after loading the
+ * savegame and saving again, the buggy map array could be converted to new savegame
+ * version. It didn't show up before r12070. */
+ if (CheckSavegameVersion(87)) UpdateVoidTiles();
+
+ /* If Load Scenario / New (Scenario) Game is used,
+ * a company does not exist yet. So create one here.
+ * 1 exeption: network-games. Those can have 0 companies
+ * But this exeption is not true for non dedicated network_servers! */
+ if (!IsValidCompanyID(COMPANY_FIRST) && (!_networking || (_networking && _network_server && !_network_dedicated)))
+ DoStartupNewCompany(false);
+
+ if (CheckSavegameVersion(72)) {
+ /* Locks/shiplifts in very old savegames had OWNER_WATER as owner */
+ for (TileIndex t = 0; t < MapSize(); t++) {
+ switch (GetTileType(t)) {
+ default: break;
+
+ case MP_WATER:
+ if (GetWaterTileType(t) == WATER_TILE_LOCK && GetTileOwner(t) == OWNER_WATER) SetTileOwner(t, OWNER_NONE);
+ break;
+
+ case MP_STATION: {
+ if (HasBit(_m[t].m6, 3)) SetBit(_m[t].m6, 2);
+ StationGfx gfx = GetStationGfx(t);
+ StationType st;
+ if ( IsInsideMM(gfx, 0, 8)) { // Railway station
+ st = STATION_RAIL;
+ SetStationGfx(t, gfx - 0);
+ } else if (IsInsideMM(gfx, 8, 67)) { // Airport
+ st = STATION_AIRPORT;
+ SetStationGfx(t, gfx - 8);
+ } else if (IsInsideMM(gfx, 67, 71)) { // Truck
+ st = STATION_TRUCK;
+ SetStationGfx(t, gfx - 67);
+ } else if (IsInsideMM(gfx, 71, 75)) { // Bus
+ st = STATION_BUS;
+ SetStationGfx(t, gfx - 71);
+ } else if (gfx == 75) { // Oil rig
+ st = STATION_OILRIG;
+ SetStationGfx(t, gfx - 75);
+ } else if (IsInsideMM(gfx, 76, 82)) { // Dock
+ st = STATION_DOCK;
+ SetStationGfx(t, gfx - 76);
+ } else if (gfx == 82) { // Buoy
+ st = STATION_BUOY;
+ SetStationGfx(t, gfx - 82);
+ } else if (IsInsideMM(gfx, 83, 168)) { // Extended airport
+ st = STATION_AIRPORT;
+ SetStationGfx(t, gfx - 83 + 67 - 8);
+ } else if (IsInsideMM(gfx, 168, 170)) { // Drive through truck
+ st = STATION_TRUCK;
+ SetStationGfx(t, gfx - 168 + GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET);
+ } else if (IsInsideMM(gfx, 170, 172)) { // Drive through bus
+ st = STATION_BUS;
+ SetStationGfx(t, gfx - 170 + GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET);
+ } else {
+ /* Restore the signals */
+ signal(SIGSEGV, prev_segfault);
+ signal(SIGABRT, prev_abort);
+ return false;
+ }
+ SB(_m[t].m6, 3, 3, st);
+ } break;
+ }
+ }
+ }
+
+ for (TileIndex t = 0; t < map_size; t++) {
+ switch (GetTileType(t)) {
+ case MP_STATION: {
+ Station *st = GetStationByTile(t);
+
+ /* Set up station spread; buoys do not have one */
+ if (!IsBuoy(t)) st->rect.BeforeAddTile(t, StationRect::ADD_FORCE);
+
+ switch (GetStationType(t)) {
+ case STATION_TRUCK:
+ case STATION_BUS:
+ if (CheckSavegameVersion(6)) {
+ /* From this version on there can be multiple road stops of the
+ * same type per station. Convert the existing stops to the new
+ * internal data structure. */
+ RoadStop *rs = new RoadStop(t);
+ if (rs == NULL) error("Too many road stops in savegame");
+
+ RoadStop **head =
+ IsTruckStop(t) ? &st->truck_stops : &st->bus_stops;
+ *head = rs;
+ }
+ break;
+
+ case STATION_OILRIG: {
+ /* Very old savegames sometimes have phantom oil rigs, i.e.
+ * an oil rig which got shut down, but not completly removed from
+ * the map
+ */
+ TileIndex t1 = TILE_ADDXY(t, 0, 1);
+ if (IsTileType(t1, MP_INDUSTRY) &&
+ GetIndustryGfx(t1) == GFX_OILRIG_1) {
+ /* The internal encoding of oil rigs was changed twice.
+ * It was 3 (till 2.2) and later 5 (till 5.1).
+ * Setting it unconditionally does not hurt.
+ */
+ GetStationByTile(t)->airport_type = AT_OILRIG;
+ } else {
+ DeleteOilRig(t);
+ }
+ break;
+ }
+
+ default: break;
+ }
+ break;
+ }
+
+ default: break;
+ }
+ }
+
+ /* In version 6.1 we put the town index in the map-array. To do this, we need
+ * to use m2 (16bit big), so we need to clean m2, and that is where this is
+ * all about ;) */
+ if (CheckSavegameVersionOldStyle(6, 1)) {
+ for (TileIndex t = 0; t < map_size; t++) {
+ switch (GetTileType(t)) {
+ case MP_HOUSE:
+ _m[t].m4 = _m[t].m2;
+ SetTownIndex(t, CalcClosestTownFromTile(t, UINT_MAX)->index);
+ break;
+
+ case MP_ROAD:
+ _m[t].m4 |= (_m[t].m2 << 4);
+ if ((GB(_m[t].m5, 4, 2) == ROAD_TILE_CROSSING ? (Owner)_m[t].m3 : GetTileOwner(t)) == OWNER_TOWN) {
+ SetTownIndex(t, CalcClosestTownFromTile(t, UINT_MAX)->index);
+ } else {
+ SetTownIndex(t, 0);
+ }
+ break;
+
+ default: break;
+ }
+ }
+ }
+
+ /* From version 9.0, we update the max passengers of a town (was sometimes negative
+ * before that. */
+ if (CheckSavegameVersion(9)) {
+ Town *t;
+ FOR_ALL_TOWNS(t) UpdateTownMaxPass(t);
+ }
+
+ /* From version 16.0, we included autorenew on engines, which are now saved, but
+ * of course, we do need to initialize them for older savegames. */
+ if (CheckSavegameVersion(16)) {
+ FOR_ALL_COMPANIES(c) {
+ c->engine_renew_list = NULL;
+ c->engine_renew = false;
+ c->engine_renew_months = -6;
+ c->engine_renew_money = 100000;
+ }
+
+ /* When loading a game, _local_company is not yet set to the correct value.
+ * However, in a dedicated server we are a spectator, so nothing needs to
+ * happen. In case we are not a dedicated server, the local company always
+ * becomes company 0, unless we are in the scenario editor where all the
+ * companies are 'invalid'.
+ */
+ if (!_network_dedicated && IsValidCompanyID(COMPANY_FIRST)) {
+ c = GetCompany(COMPANY_FIRST);
+ c->engine_renew = _settings_client.gui.autorenew;
+ c->engine_renew_months = _settings_client.gui.autorenew_months;
+ c->engine_renew_money = _settings_client.gui.autorenew_money;
+ }
+ }
+
+ if (CheckSavegameVersion(48)) {
+ for (TileIndex t = 0; t < map_size; t++) {
+ switch (GetTileType(t)) {
+ case MP_RAILWAY:
+ if (IsPlainRailTile(t)) {
+ /* Swap ground type and signal type for plain rail tiles, so the
+ * ground type uses the same bits as for depots and waypoints. */
+ uint tmp = GB(_m[t].m4, 0, 4);
+ SB(_m[t].m4, 0, 4, GB(_m[t].m2, 0, 4));
+ SB(_m[t].m2, 0, 4, tmp);
+ } else if (HasBit(_m[t].m5, 2)) {
+ /* Split waypoint and depot rail type and remove the subtype. */
+ ClrBit(_m[t].m5, 2);
+ ClrBit(_m[t].m5, 6);
+ }
+ break;
+
+ case MP_ROAD:
+ /* Swap m3 and m4, so the track type for rail crossings is the
+ * same as for normal rail. */
+ Swap(_m[t].m3, _m[t].m4);
+ break;
+
+ default: break;
+ }
+ }
+ }
+
+ if (CheckSavegameVersion(61)) {
+ /* Added the RoadType */
+ bool old_bridge = CheckSavegameVersion(42);
+ for (TileIndex t = 0; t < map_size; t++) {
+ switch(GetTileType(t)) {
+ case MP_ROAD:
+ SB(_m[t].m5, 6, 2, GB(_m[t].m5, 4, 2));
+ switch (GetRoadTileType(t)) {
+ default: NOT_REACHED();
+ case ROAD_TILE_NORMAL:
+ SB(_m[t].m4, 0, 4, GB(_m[t].m5, 0, 4));
+ SB(_m[t].m4, 4, 4, 0);
+ SB(_m[t].m6, 2, 4, 0);
+ break;
+ case ROAD_TILE_CROSSING:
+ SB(_m[t].m4, 5, 2, GB(_m[t].m5, 2, 2));
+ break;
+ case ROAD_TILE_DEPOT: break;
+ }
+ SetRoadTypes(t, ROADTYPES_ROAD);
+ break;
+
+ case MP_STATION:
+ if (IsRoadStop(t)) SetRoadTypes(t, ROADTYPES_ROAD);
+ break;
+
+ case MP_TUNNELBRIDGE:
+ /* Middle part of "old" bridges */
+ if (old_bridge && IsBridge(t) && HasBit(_m[t].m5, 6)) break;
+ if (((old_bridge && IsBridge(t)) ? (TransportType)GB(_m[t].m5, 1, 2) : GetTunnelBridgeTransportType(t)) == TRANSPORT_ROAD) {
+ SetRoadTypes(t, ROADTYPES_ROAD);
+ }
+ break;
+
+ default: break;
+ }
+ }
+ }
+
+ if (CheckSavegameVersion(42)) {
+ Vehicle* v;
+
+ for (TileIndex t = 0; t < map_size; t++) {
+ if (MayHaveBridgeAbove(t)) ClearBridgeMiddle(t);
+ if (IsBridgeTile(t)) {
+ if (HasBit(_m[t].m5, 6)) { // middle part
+ Axis axis = (Axis)GB(_m[t].m5, 0, 1);
+
+ if (HasBit(_m[t].m5, 5)) { // transport route under bridge?
+ if (GB(_m[t].m5, 3, 2) == TRANSPORT_RAIL) {
+ MakeRailNormal(
+ t,
+ GetTileOwner(t),
+ axis == AXIS_X ? TRACK_BIT_Y : TRACK_BIT_X,
+ GetRailType(t)
+ );
+ } else {
+ TownID town = IsTileOwner(t, OWNER_TOWN) ? ClosestTownFromTile(t, UINT_MAX)->index : 0;
+
+ MakeRoadNormal(
+ t,
+ axis == AXIS_X ? ROAD_Y : ROAD_X,
+ ROADTYPES_ROAD,
+ town,
+ GetTileOwner(t), OWNER_NONE, OWNER_NONE
+ );
+ }
+ } else {
+ if (GB(_m[t].m5, 3, 2) == 0) {
+ MakeClear(t, CLEAR_GRASS, 3);
+ } else {
+ if (GetTileSlope(t, NULL) != SLOPE_FLAT) {
+ MakeShore(t);
+ } else {
+ if (GetTileOwner(t) == OWNER_WATER) {
+ MakeWater(t);
+ } else {
+ MakeCanal(t, GetTileOwner(t), Random());
+ }
+ }
+ }
+ }
+ SetBridgeMiddle(t, axis);
+ } else { // ramp
+ Axis axis = (Axis)GB(_m[t].m5, 0, 1);
+ uint north_south = GB(_m[t].m5, 5, 1);
+ DiagDirection dir = ReverseDiagDir(XYNSToDiagDir(axis, north_south));
+ TransportType type = (TransportType)GB(_m[t].m5, 1, 2);
+
+ _m[t].m5 = 1 << 7 | type << 2 | dir;
+ }
+ }
+ }
+
+ FOR_ALL_VEHICLES(v) {
+ if (v->type != VEH_TRAIN && v->type != VEH_ROAD) continue;
+ if (IsBridgeTile(v->tile)) {
+ DiagDirection dir = GetTunnelBridgeDirection(v->tile);
+
+ if (dir != DirToDiagDir(v->direction)) continue;
+ switch (dir) {
+ default: NOT_REACHED();
+ case DIAGDIR_NE: if ((v->x_pos & 0xF) != 0) continue; break;
+ case DIAGDIR_SE: if ((v->y_pos & 0xF) != TILE_SIZE - 1) continue; break;
+ case DIAGDIR_SW: if ((v->x_pos & 0xF) != TILE_SIZE - 1) continue; break;
+ case DIAGDIR_NW: if ((v->y_pos & 0xF) != 0) continue; break;
+ }
+ } else if (v->z_pos > GetSlopeZ(v->x_pos, v->y_pos)) {
+ v->tile = GetNorthernBridgeEnd(v->tile);
+ } else {
+ continue;
+ }
+ if (v->type == VEH_TRAIN) {
+ v->u.rail.track = TRACK_BIT_WORMHOLE;
+ } else {
+ v->u.road.state = RVSB_WORMHOLE;
+ }
+ }
+ }
+
+ /* Elrails got added in rev 24 */
+ if (CheckSavegameVersion(24)) {
+ Vehicle *v;
+ RailType min_rail = RAILTYPE_ELECTRIC;
+
+ FOR_ALL_VEHICLES(v) {
+ if (v->type == VEH_TRAIN) {
+ RailType rt = RailVehInfo(v->engine_type)->railtype;
+
+ v->u.rail.railtype = rt;
+ if (rt == RAILTYPE_ELECTRIC) min_rail = RAILTYPE_RAIL;
+ }
+ }
+
+ /* .. so we convert the entire map from normal to elrail (so maintain "fairness") */
+ for (TileIndex t = 0; t < map_size; t++) {
+ switch (GetTileType(t)) {
+ case MP_RAILWAY:
+ SetRailType(t, UpdateRailType(GetRailType(t), min_rail));
+ break;
+
+ case MP_ROAD:
+ if (IsLevelCrossing(t)) {
+ SetRailType(t, UpdateRailType(GetRailType(t), min_rail));
+ }
+ break;
+
+ case MP_STATION:
+ if (IsRailwayStation(t)) {
+ SetRailType(t, UpdateRailType(GetRailType(t), min_rail));
+ }
+ break;
+
+ case MP_TUNNELBRIDGE:
+ if (GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL) {
+ SetRailType(t, UpdateRailType(GetRailType(t), min_rail));
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ FOR_ALL_VEHICLES(v) {
+ if (v->type == VEH_TRAIN && (IsFrontEngine(v) || IsFreeWagon(v))) TrainConsistChanged(v, true);
+ }
+
+ }
+
+ /* In version 16.1 of the savegame a company can decide if trains, which get
+ * replaced, shall keep their old length. In all prior versions, just default
+ * to false */
+ if (CheckSavegameVersionOldStyle(16, 1)) {
+ FOR_ALL_COMPANIES(c) c->renew_keep_length = false;
+ }
+
+ /* In version 17, ground type is moved from m2 to m4 for depots and
+ * waypoints to make way for storing the index in m2. The custom graphics
+ * id which was stored in m4 is now saved as a grf/id reference in the
+ * waypoint struct. */
+ if (CheckSavegameVersion(17)) {
+ Waypoint *wp;
+
+ FOR_ALL_WAYPOINTS(wp) {
+ if (wp->deleted == 0) {
+ const StationSpec *statspec = NULL;
+
+ if (HasBit(_m[wp->xy].m3, 4))
+ statspec = GetCustomStationSpec(STAT_CLASS_WAYP, _m[wp->xy].m4 + 1);
+
+ if (statspec != NULL) {
+ wp->stat_id = _m[wp->xy].m4 + 1;
+ wp->grfid = statspec->grffile->grfid;
+ wp->localidx = statspec->localidx;
+ } else {
+ /* No custom graphics set, so set to default. */
+ wp->stat_id = 0;
+ wp->grfid = 0;
+ wp->localidx = 0;
+ }
+
+ /* Move ground type bits from m2 to m4. */
+ _m[wp->xy].m4 = GB(_m[wp->xy].m2, 0, 4);
+ /* Store waypoint index in the tile. */
+ _m[wp->xy].m2 = wp->index;
+ }
+ }
+ } else {
+ /* As of version 17, we recalculate the custom graphic ID of waypoints
+ * from the GRF ID / station index. */
+ AfterLoadWaypoints();
+ }
+
+ /* From version 15, we moved a semaphore bit from bit 2 to bit 3 in m4, making
+ * room for PBS. Now in version 21 move it back :P. */
+ if (CheckSavegameVersion(21) && !CheckSavegameVersion(15)) {
+ for (TileIndex t = 0; t < map_size; t++) {
+ switch (GetTileType(t)) {
+ case MP_RAILWAY:
+ if (HasSignals(t)) {
+ /* convert PBS signals to combo-signals */
+ if (HasBit(_m[t].m2, 2)) SetSignalType(t, TRACK_X, SIGTYPE_COMBO);
+
+ /* move the signal variant back */
+ SetSignalVariant(t, TRACK_X, HasBit(_m[t].m2, 3) ? SIG_SEMAPHORE : SIG_ELECTRIC);
+ ClrBit(_m[t].m2, 3);
+ }
+
+ /* Clear PBS reservation on track */
+ if (!IsRailDepotTile(t)) {
+ SB(_m[t].m4, 4, 4, 0);
+ } else {
+ ClrBit(_m[t].m3, 6);
+ }
+ break;
+
+ case MP_ROAD: /* Clear PBS reservation on crossing */
+ if (IsLevelCrossing(t)) ClrBit(_m[t].m5, 0);
+ break;
+
+ case MP_STATION: /* Clear PBS reservation on station */
+ ClrBit(_m[t].m3, 6);
+ break;
+
+ default: break;
+ }
+ }
+ }
+
+ if (CheckSavegameVersion(25)) {
+ Vehicle *v;
+ FOR_ALL_VEHICLES(v) {
+ if (v->type == VEH_ROAD) {
+ v->vehstatus &= ~0x40;
+ v->u.road.slot = NULL;
+ v->u.road.slot_age = 0;
+ }
+ }
+ } else {
+ Vehicle *v;
+ FOR_ALL_VEHICLES(v) {
+ if (v->type == VEH_ROAD && v->u.road.slot != NULL) v->u.road.slot->num_vehicles++;
+ }
+ }
+
+ if (CheckSavegameVersion(26)) {
+ Station *st;
+ FOR_ALL_STATIONS(st) {
+ st->last_vehicle_type = VEH_INVALID;
+ }
+ }
+
+ YapfNotifyTrackLayoutChange(INVALID_TILE, INVALID_TRACK);
+
+ if (CheckSavegameVersion(34)) FOR_ALL_COMPANIES(c) ResetCompanyLivery(c);
+
+ FOR_ALL_COMPANIES(c) {
+ c->avail_railtypes = GetCompanyRailtypes(c->index);
+ c->avail_roadtypes = GetCompanyRoadtypes(c->index);
+ }
+
+ if (!CheckSavegameVersion(27)) AfterLoadStations();
+
+ /* Time starts at 0 instead of 1920.
+ * Account for this in older games by adding an offset */
+ if (CheckSavegameVersion(31)) {
+ Station *st;
+ Waypoint *wp;
+ Engine *e;
+ Industry *i;
+ Vehicle *v;
+
+ _date += DAYS_TILL_ORIGINAL_BASE_YEAR;
+ _cur_year += ORIGINAL_BASE_YEAR;
+
+ FOR_ALL_STATIONS(st) st->build_date += DAYS_TILL_ORIGINAL_BASE_YEAR;
+ FOR_ALL_WAYPOINTS(wp) wp->build_date += DAYS_TILL_ORIGINAL_BASE_YEAR;
+ FOR_ALL_ENGINES(e) e->intro_date += DAYS_TILL_ORIGINAL_BASE_YEAR;
+ FOR_ALL_COMPANIES(c) c->inaugurated_year += ORIGINAL_BASE_YEAR;
+ FOR_ALL_INDUSTRIES(i) i->last_prod_year += ORIGINAL_BASE_YEAR;
+
+ FOR_ALL_VEHICLES(v) {
+ v->date_of_last_service += DAYS_TILL_ORIGINAL_BASE_YEAR;
+ v->build_year += ORIGINAL_BASE_YEAR;
+ }
+ }
+
+ /* From 32 on we save the industry who made the farmland.
+ * To give this prettyness to old savegames, we remove all farmfields and
+ * plant new ones. */
+ if (CheckSavegameVersion(32)) {
+ Industry *i;
+
+ for (TileIndex t = 0; t < map_size; t++) {
+ if (IsTileType(t, MP_CLEAR) && IsClearGround(t, CLEAR_FIELDS)) {
+ /* remove fields */
+ MakeClear(t, CLEAR_GRASS, 3);
+ } else if (IsTileType(t, MP_CLEAR) || IsTileType(t, MP_TREES)) {
+ /* remove fences around fields */
+ SetFenceSE(t, 0);
+ SetFenceSW(t, 0);
+ }
+ }
+
+ FOR_ALL_INDUSTRIES(i) {
+ uint j;
+
+ if (GetIndustrySpec(i->type)->behaviour & INDUSTRYBEH_PLANT_ON_BUILT) {
+ for (j = 0; j != 50; j++) PlantRandomFarmField(i);
+ }
+ }
+ }
+
+ /* Setting no refit flags to all orders in savegames from before refit in orders were added */
+ if (CheckSavegameVersion(36)) {
+ Order *order;
+ Vehicle *v;
+
+ FOR_ALL_ORDERS(order) {
+ order->SetRefit(CT_NO_REFIT);
+ }
+
+ FOR_ALL_VEHICLES(v) {
+ v->current_order.SetRefit(CT_NO_REFIT);
+ }
+ }
+
+ /* from version 38 we have optional elrails, since we cannot know the
+ * preference of a user, let elrails enabled; it can be disabled manually */
+ if (CheckSavegameVersion(38)) _settings_game.vehicle.disable_elrails = false;
+ /* do the same as when elrails were enabled/disabled manually just now */
+ SettingsDisableElrail(_settings_game.vehicle.disable_elrails);
+ InitializeRailGUI();
+
+ /* From version 53, the map array was changed for house tiles to allow
+ * space for newhouses grf features. A new byte, m7, was also added. */
+ if (CheckSavegameVersion(53)) {
+ for (TileIndex t = 0; t < map_size; t++) {
+ if (IsTileType(t, MP_HOUSE)) {
+ if (GB(_m[t].m3, 6, 2) != TOWN_HOUSE_COMPLETED) {
+ /* Move the construction stage from m3[7..6] to m5[5..4].
+ * The construction counter does not have to move. */
+ SB(_m[t].m5, 3, 2, GB(_m[t].m3, 6, 2));
+ SB(_m[t].m3, 6, 2, 0);
+
+ /* The "house is completed" bit is now in m6[2]. */
+ SetHouseCompleted(t, false);
+ } else {
+ /* The "lift has destination" bit has been moved from
+ * m5[7] to m7[0]. */
+ SB(_me[t].m7, 0, 1, HasBit(_m[t].m5, 7));
+ ClrBit(_m[t].m5, 7);
+
+ /* The "lift is moving" bit has been removed, as it does
+ * the same job as the "lift has destination" bit. */
+ ClrBit(_m[t].m1, 7);
+
+ /* The position of the lift goes from m1[7..0] to m6[7..2],
+ * making m1 totally free, now. The lift position does not
+ * have to be a full byte since the maximum value is 36. */
+ SetLiftPosition(t, GB(_m[t].m1, 0, 6 ));
+
+ _m[t].m1 = 0;
+ _m[t].m3 = 0;
+ SetHouseCompleted(t, true);
+ }
+ }
+ }
+ }
+
+ /* Check and update house and town values */
+ UpdateHousesAndTowns();
+
+ if (CheckSavegameVersion(43)) {
+ for (TileIndex t = 0; t < map_size; t++) {
+ if (IsTileType(t, MP_INDUSTRY)) {
+ switch (GetIndustryGfx(t)) {
+ case GFX_POWERPLANT_SPARKS:
+ SetIndustryAnimationState(t, GB(_m[t].m1, 2, 5));
+ break;
+
+ case GFX_OILWELL_ANIMATED_1:
+ case GFX_OILWELL_ANIMATED_2:
+ case GFX_OILWELL_ANIMATED_3:
+ SetIndustryAnimationState(t, GB(_m[t].m1, 0, 2));
+ break;
+
+ case GFX_COAL_MINE_TOWER_ANIMATED:
+ case GFX_COPPER_MINE_TOWER_ANIMATED:
+ case GFX_GOLD_MINE_TOWER_ANIMATED:
+ SetIndustryAnimationState(t, _m[t].m1);
+ break;
+
+ default: /* No animation states to change */
+ break;
+ }
+ }
+ }
+ }
+
+ if (CheckSavegameVersion(44)) {
+ Vehicle *v;
+ /* If we remove a station while cargo from it is still enroute, payment calculation will assume
+ * 0, 0 to be the source of the cargo, resulting in very high payments usually. v->source_xy
+ * stores the coordinates, preserving them even if the station is removed. However, if a game is loaded
+ * where this situation exists, the cargo-source information is lost. in this case, we set the source
+ * to the current tile of the vehicle to prevent excessive profits
+ */
+ FOR_ALL_VEHICLES(v) {
+ const CargoList::List *packets = v->cargo.Packets();
+ for (CargoList::List::const_iterator it = packets->begin(); it != packets->end(); it++) {
+ CargoPacket *cp = *it;
+ cp->source_xy = IsValidStationID(cp->source) ? GetStation(cp->source)->xy : v->tile;
+ cp->loaded_at_xy = cp->source_xy;
+ }
+ v->cargo.InvalidateCache();
+ }
+
+ /* Store position of the station where the goods come from, so there
+ * are no very high payments when stations get removed. However, if the
+ * station where the goods came from is already removed, the source
+ * information is lost. In that case we set it to the position of this
+ * station */
+ Station *st;
+ FOR_ALL_STATIONS(st) {
+ for (CargoID c = 0; c < NUM_CARGO; c++) {
+ GoodsEntry *ge = &st->goods[c];
+
+ const CargoList::List *packets = ge->cargo.Packets();
+ for (CargoList::List::const_iterator it = packets->begin(); it != packets->end(); it++) {
+ CargoPacket *cp = *it;
+ cp->source_xy = IsValidStationID(cp->source) ? GetStation(cp->source)->xy : st->xy;
+ cp->loaded_at_xy = cp->source_xy;
+ }
+ }
+ }
+ }
+
+ if (CheckSavegameVersion(45)) {
+ Vehicle *v;
+ /* Originally just the fact that some cargo had been paid for was
+ * stored to stop people cheating and cashing in several times. This
+ * wasn't enough though as it was cleared when the vehicle started
+ * loading again, even if it didn't actually load anything, so now the
+ * amount of cargo that has been paid for is stored. */
+ FOR_ALL_VEHICLES(v) {
+ const CargoList::List *packets = v->cargo.Packets();
+ for (CargoList::List::const_iterator it = packets->begin(); it != packets->end(); it++) {
+ CargoPacket *cp = *it;
+ cp->paid_for = HasBit(v->vehicle_flags, 2);
+ }
+ ClrBit(v->vehicle_flags, 2);
+ v->cargo.InvalidateCache();
+ }
+ }
+
+ /* Buoys do now store the owner of the previous water tile, which can never
+ * be OWNER_NONE. So replace OWNER_NONE with OWNER_WATER. */
+ if (CheckSavegameVersion(46)) {
+ Station *st;
+ FOR_ALL_STATIONS(st) {
+ if (st->IsBuoy() && IsTileOwner(st->xy, OWNER_NONE) && TileHeight(st->xy) == 0) SetTileOwner(st->xy, OWNER_WATER);
+ }
+ }
+
+ if (CheckSavegameVersion(50)) {
+ Vehicle *v;
+ /* Aircraft units changed from 8 mph to 1 km/h */
+ FOR_ALL_VEHICLES(v) {
+ if (v->type == VEH_AIRCRAFT && v->subtype <= AIR_AIRCRAFT) {
+ const AircraftVehicleInfo *avi = AircraftVehInfo(v->engine_type);
+ v->cur_speed *= 129;
+ v->cur_speed /= 10;
+ v->max_speed = avi->max_speed;
+ v->acceleration = avi->acceleration;
+ }
+ }
+ }
+
+ if (CheckSavegameVersion(49)) FOR_ALL_COMPANIES(c) c->face = ConvertFromOldCompanyManagerFace(c->face);
+
+ if (CheckSavegameVersion(52)) {
+ for (TileIndex t = 0; t < map_size; t++) {
+ if (IsStatueTile(t)) {
+ _m[t].m2 = CalcClosestTownFromTile(t, UINT_MAX)->index;
+ }
+ }
+ }
+
+ /* A patch option containing the proportion of towns that grow twice as
+ * fast was added in version 54. From version 56 this is now saved in the
+ * town as cities can be built specifically in the scenario editor. */
+ if (CheckSavegameVersion(56)) {
+ Town *t;
+
+ FOR_ALL_TOWNS(t) {
+ if (_settings_game.economy.larger_towns != 0 && (t->index % _settings_game.economy.larger_towns) == 0) {
+ t->larger_town = true;
+ }
+ }
+ }
+
+ if (CheckSavegameVersion(57)) {
+ Vehicle *v;
+ /* Added a FIFO queue of vehicles loading at stations */
+ FOR_ALL_VEHICLES(v) {
+ if ((v->type != VEH_TRAIN || IsFrontEngine(v)) && // for all locs
+ !(v->vehstatus & (VS_STOPPED | VS_CRASHED)) && // not stopped or crashed
+ v->current_order.IsType(OT_LOADING)) { // loading
+ GetStation(v->last_station_visited)->loading_vehicles.push_back(v);
+
+ /* The loading finished flag is *only* set when actually completely
+ * finished. Because the vehicle is loading, it is not finished. */
+ ClrBit(v->vehicle_flags, VF_LOADING_FINISHED);
+ }
+ }
+ } else if (CheckSavegameVersion(59)) {
+ /* For some reason non-loading vehicles could be in the station's loading vehicle list */
+
+ Station *st;
+ FOR_ALL_STATIONS(st) {
+ std::list<Vehicle *>::iterator iter;
+ for (iter = st->loading_vehicles.begin(); iter != st->loading_vehicles.end();) {
+ Vehicle *v = *iter;
+ iter++;
+ if (!v->current_order.IsType(OT_LOADING)) st->loading_vehicles.remove(v);
+ }
+ }
+ }
+
+ if (CheckSavegameVersion(58)) {
+ /* patch difficulty number_industries other than zero get bumped to +1
+ * since a new option (very low at position1) has been added */
+ if (_settings_game.difficulty.number_industries > 0) {
+ _settings_game.difficulty.number_industries++;
+ }
+
+ /* Same goes for number of towns, although no test is needed, just an increment */
+ _settings_game.difficulty.number_towns++;
+ }
+
+ if (CheckSavegameVersion(64)) {
+ /* copy the signal type/variant and move signal states bits */
+ for (TileIndex t = 0; t < map_size; t++) {
+ if (IsTileType(t, MP_RAILWAY) && HasSignals(t)) {
+ SetSignalStates(t, GB(_m[t].m2, 4, 4));
+ SetSignalVariant(t, INVALID_TRACK, GetSignalVariant(t, TRACK_X));
+ SetSignalType(t, INVALID_TRACK, GetSignalType(t, TRACK_X));
+ ClrBit(_m[t].m2, 7);
+ }
+ }
+ }
+
+ if (CheckSavegameVersion(69)) {
+ /* In some old savegames a bit was cleared when it should not be cleared */
+ Vehicle *v;
+ FOR_ALL_VEHICLES(v) {
+ if (v->type == VEH_ROAD && (v->u.road.state == 250 || v->u.road.state == 251)) {
+ SetBit(v->u.road.state, RVS_IS_STOPPING);
+ }
+ }
+ }
+
+ if (CheckSavegameVersion(70)) {
+ /* Added variables to support newindustries */
+ Industry *i;
+ FOR_ALL_INDUSTRIES(i) i->founder = OWNER_NONE;
+ }
+
+ /* From version 82, old style canals (above sealevel (0), WATER owner) are no longer supported.
+ Replace the owner for those by OWNER_NONE. */
+ if (CheckSavegameVersion(82)) {
+ for (TileIndex t = 0; t < map_size; t++) {
+ if (IsTileType(t, MP_WATER) &&
+ GetWaterTileType(t) == WATER_TILE_CLEAR &&
+ GetTileOwner(t) == OWNER_WATER &&
+ TileHeight(t) != 0) {
+ SetTileOwner(t, OWNER_NONE);
+ }
+ }
+ }
+
+ /*
+ * Add the 'previous' owner to the ship depots so we can reset it with
+ * the correct values when it gets destroyed. This prevents that
+ * someone can remove canals owned by somebody else and it prevents
+ * making floods using the removal of ship depots.
+ */
+ if (CheckSavegameVersion(83)) {
+ for (TileIndex t = 0; t < map_size; t++) {
+ if (IsTileType(t, MP_WATER) && IsShipDepot(t)) {
+ _m[t].m4 = (TileHeight(t) == 0) ? OWNER_WATER : OWNER_NONE;
+ }
+ }
+ }
+
+ if (CheckSavegameVersion(74)) {
+ Station *st;
+ FOR_ALL_STATIONS(st) {
+ for (CargoID c = 0; c < NUM_CARGO; c++) {
+ st->goods[c].last_speed = 0;
+ if (st->goods[c].cargo.Count() != 0) SetBit(st->goods[c].acceptance_pickup, GoodsEntry::PICKUP);
+ }
+ }
+ }
+
+ if (CheckSavegameVersion(78)) {
+ Industry *i;
+ uint j;
+ FOR_ALL_INDUSTRIES(i) {
+ const IndustrySpec *indsp = GetIndustrySpec(i->type);
+ for (j = 0; j < lengthof(i->produced_cargo); j++) {
+ i->produced_cargo[j] = indsp->produced_cargo[j];
+ }
+ for (j = 0; j < lengthof(i->accepts_cargo); j++) {
+ i->accepts_cargo[j] = indsp->accepts_cargo[j];
+ }
+ }
+ }
+
+ /* Before version 81, the density of grass was always stored as zero, and
+ * grassy trees were always drawn fully grassy. Furthermore, trees on rough
+ * land used to have zero density, now they have full density. Therefore,
+ * make all grassy/rough land trees have a density of 3. */
+ if (CheckSavegameVersion(81)) {
+ for (TileIndex t = 0; t < map_size; t++) {
+ if (GetTileType(t) == MP_TREES) {
+ TreeGround groundType = GetTreeGround(t);
+ if (groundType != TREE_GROUND_SNOW_DESERT) SetTreeGroundDensity(t, groundType, 3);
+ }
+ }
+ }
+
+
+ if (CheckSavegameVersion(93)) {
+ /* Rework of orders. */
+ Order *order;
+ FOR_ALL_ORDERS(order) order->ConvertFromOldSavegame();
+
+ Vehicle *v;
+ FOR_ALL_VEHICLES(v) {
+ if (v->orders.list != NULL && v->orders.list->GetFirstOrder() != NULL && !v->orders.list->GetFirstOrder()->IsValid()) {
+ v->orders.list->FreeChain();
+ v->orders.list = NULL;
+ }
+
+ v->current_order.ConvertFromOldSavegame();
+ if (v->type == VEH_ROAD && v->IsPrimaryVehicle() && v->FirstShared() == v) {
+ FOR_VEHICLE_ORDERS(v, order) order->SetNonStopType(ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS);
+ }
+ }
+ } else if (CheckSavegameVersion(94)) {
+ /* Unload and transfer are now mutual exclusive. */
+ Order *order;
+ FOR_ALL_ORDERS(order) {
+ if ((order->GetUnloadType() & (OUFB_UNLOAD | OUFB_TRANSFER)) == (OUFB_UNLOAD | OUFB_TRANSFER)) {
+ order->SetUnloadType(OUFB_TRANSFER);
+ order->SetLoadType(OLFB_NO_LOAD);
+ }
+ }
+
+ Vehicle *v;
+ FOR_ALL_VEHICLES(v) {
+ if ((v->current_order.GetUnloadType() & (OUFB_UNLOAD | OUFB_TRANSFER)) == (OUFB_UNLOAD | OUFB_TRANSFER)) {
+ v->current_order.SetUnloadType(OUFB_TRANSFER);
+ v->current_order.SetLoadType(OLFB_NO_LOAD);
+ }
+ }
+ }
+
+ if (CheckSavegameVersion(84)) {
+ /* Update go to buoy orders because they are just waypoints */
+ Order *order;
+ FOR_ALL_ORDERS(order) {
+ if (order->IsType(OT_GOTO_STATION) && GetStation(order->GetDestination())->IsBuoy()) {
+ order->SetLoadType(OLF_LOAD_IF_POSSIBLE);
+ order->SetUnloadType(OUF_UNLOAD_IF_POSSIBLE);
+ }
+ }
+
+ /* Set all share owners to INVALID_COMPANY for
+ * 1) all inactive companies
+ * (when inactive companies were stored in the savegame - TTD, TTDP and some
+ * *really* old revisions of OTTD; else it is already set in InitializeCompanies())
+ * 2) shares that are owned by inactive companies or self
+ * (caused by cheating clients in earlier revisions) */
+ FOR_ALL_COMPANIES(c) {
+ for (uint i = 0; i < 4; i++) {
+ CompanyID company = c->share_owners[i];
+ if (company == INVALID_COMPANY) continue;
+ if (!IsValidCompanyID(company) || company == c->index) c->share_owners[i] = INVALID_COMPANY;
+ }
+ }
+ }
+
+ if (CheckSavegameVersion(86)) {
+ for (TileIndex t = 0; t < map_size; t++) {
+ /* Move river flag and update canals to use water class */
+ if (IsTileType(t, MP_WATER)) {
+ if (GetWaterClass(t) != WATER_CLASS_RIVER) {
+ if (IsWater(t)) {
+ Owner o = GetTileOwner(t);
+ if (o == OWNER_WATER) {
+ MakeWater(t);
+ } else {
+ MakeCanal(t, o, Random());
+ }
+ } else if (IsShipDepot(t)) {
+ Owner o = (Owner)_m[t].m4; // Original water owner
+ SetWaterClass(t, o == OWNER_WATER ? WATER_CLASS_SEA : WATER_CLASS_CANAL);
+ }
+ }
+ }
+ }
+
+ /* Update locks, depots, docks and buoys to have a water class based
+ * on its neighbouring tiles. Done after river and canal updates to
+ * ensure neighbours are correct. */
+ for (TileIndex t = 0; t < map_size; t++) {
+ if (GetTileSlope(t, NULL) != SLOPE_FLAT) continue;
+
+ if (IsTileType(t, MP_WATER) && IsLock(t)) SetWaterClassDependingOnSurroundings(t, false);
+ if (IsTileType(t, MP_STATION) && (IsDock(t) || IsBuoy(t))) SetWaterClassDependingOnSurroundings(t, false);
+ }
+ }
+
+ if (CheckSavegameVersion(87)) {
+ for (TileIndex t = 0; t < map_size; t++) {
+ /* skip oil rigs at borders! */
+ if ((IsTileType(t, MP_WATER) || IsBuoyTile(t)) &&
+ (TileX(t) == 0 || TileY(t) == 0 || TileX(t) == MapMaxX() - 1 || TileY(t) == MapMaxY() - 1)) {
+ /* Some version 86 savegames have wrong water class at map borders (under buoy, or after removing buoy).
+ * This conversion has to be done before buoys with invalid owner are removed. */
+ SetWaterClass(t, WATER_CLASS_SEA);
+ }
+
+ if (IsBuoyTile(t) || IsDriveThroughStopTile(t) || IsTileType(t, MP_WATER)) {
+ Owner o = GetTileOwner(t);
+ if (o < MAX_COMPANIES && !IsValidCompanyID(o)) {
+ _current_company = o;
+ ChangeTileOwner(t, o, INVALID_OWNER);
+ }
+ if (IsBuoyTile(t)) {
+ /* reset buoy owner to OWNER_NONE in the station struct
+ * (even if it is owned by active company) */
+ GetStationByTile(t)->owner = OWNER_NONE;
+ }
+ } else if (IsTileType(t, MP_ROAD)) {
+ /* works for all RoadTileType */
+ for (RoadType rt = ROADTYPE_ROAD; rt < ROADTYPE_END; rt++) {
+ /* update even non-existing road types to update tile owner too */
+ Owner o = GetRoadOwner(t, rt);
+ if (o < MAX_COMPANIES && !IsValidCompanyID(o)) SetRoadOwner(t, rt, OWNER_NONE);
+ }
+ if (IsLevelCrossing(t)) {
+ Owner o = GetTileOwner(t);
+ if (!IsValidCompanyID(o)) {
+ /* remove leftover rail piece from crossing (from very old savegames) */
+ _current_company = o;
+ DoCommand(t, 0, GetCrossingRailTrack(t), DC_EXEC | DC_BANKRUPT, CMD_REMOVE_SINGLE_RAIL);
+ }
+ }
+ }
+ }
+
+ /* Convert old PF settings to new */
+ if (_settings_game.pf.yapf.rail_use_yapf || CheckSavegameVersion(28)) {
+ _settings_game.pf.pathfinder_for_trains = VPF_YAPF;
+ } else {
+ _settings_game.pf.pathfinder_for_trains = (_settings_game.pf.new_pathfinding_all ? VPF_NPF : VPF_NTP);
+ }
+
+ if (_settings_game.pf.yapf.road_use_yapf || CheckSavegameVersion(28)) {
+ _settings_game.pf.pathfinder_for_roadvehs = VPF_YAPF;
+ } else {
+ _settings_game.pf.pathfinder_for_roadvehs = (_settings_game.pf.new_pathfinding_all ? VPF_NPF : VPF_OPF);
+ }
+
+ if (_settings_game.pf.yapf.ship_use_yapf) {
+ _settings_game.pf.pathfinder_for_ships = VPF_YAPF;
+ } else {
+ _settings_game.pf.pathfinder_for_ships = (_settings_game.pf.new_pathfinding_all ? VPF_NPF : VPF_OPF);
+ }
+ }
+
+ if (CheckSavegameVersion(88)) {
+ /* Profits are now with 8 bit fract */
+ Vehicle *v;
+ FOR_ALL_VEHICLES(v) {
+ v->profit_this_year <<= 8;
+ v->profit_last_year <<= 8;
+ v->running_ticks = 0;
+ }
+ }
+
+ if (CheckSavegameVersion(91)) {
+ /* Increase HouseAnimationFrame from 5 to 7 bits */
+ for (TileIndex t = 0; t < map_size; t++) {
+ if (IsTileType(t, MP_HOUSE) && GetHouseType(t) >= NEW_HOUSE_OFFSET) {
+ SetHouseAnimationFrame(t, GB(_m[t].m6, 3, 5));
+ }
+ }
+ }
+
+ if (CheckSavegameVersion(62)) {
+ /* Remove all trams from savegames without tram support.
+ * There would be trams without tram track under causing crashes sooner or later. */
+ Vehicle *v;
+ FOR_ALL_VEHICLES(v) {
+ if (v->type == VEH_ROAD && v->First() == v &&
+ HasBit(EngInfo(v->engine_type)->misc_flags, EF_ROAD_TRAM)) {
+ if (_switch_mode_errorstr == INVALID_STRING_ID || _switch_mode_errorstr == STR_NEWGRF_COMPATIBLE_LOAD_WARNING) {
+ _switch_mode_errorstr = STR_LOADGAME_REMOVED_TRAMS;
+ }
+ delete v;
+ }
+ }
+ }
+
+ if (CheckSavegameVersion(99)) {
+ for (TileIndex t = 0; t < map_size; t++) {
+ /* Set newly introduced WaterClass of industry tiles */
+ if (IsTileType(t, MP_STATION) && IsOilRig(t)) {
+ SetWaterClassDependingOnSurroundings(t, true);
+ }
+ if (IsTileType(t, MP_INDUSTRY)) {
+ if ((GetIndustrySpec(GetIndustryType(t))->behaviour & INDUSTRYBEH_BUILT_ONWATER) != 0) {
+ SetWaterClassDependingOnSurroundings(t, true);
+ } else {
+ SetWaterClass(t, WATER_CLASS_INVALID);
+ }
+ }
+
+ /* Replace "house construction year" with "house age" */
+ if (IsTileType(t, MP_HOUSE) && IsHouseCompleted(t)) {
+ _m[t].m5 = Clamp(_cur_year - (_m[t].m5 + ORIGINAL_BASE_YEAR), 0, 0xFF);
+ }
+ }
+ }
+
+ /* Move the signal variant back up one bit for PBS. We don't convert the old PBS
+ * format here, as an old layout wouldn't work properly anyway. To be safe, we
+ * clear any possible PBS reservations as well. */
+ if (CheckSavegameVersion(100)) {
+ for (TileIndex t = 0; t < map_size; t++) {
+ switch (GetTileType(t)) {
+ case MP_RAILWAY:
+ if (HasSignals(t)) {
+ /* move the signal variant */
+ SetSignalVariant(t, TRACK_UPPER, HasBit(_m[t].m2, 2) ? SIG_SEMAPHORE : SIG_ELECTRIC);
+ SetSignalVariant(t, TRACK_LOWER, HasBit(_m[t].m2, 6) ? SIG_SEMAPHORE : SIG_ELECTRIC);
+ ClrBit(_m[t].m2, 2);
+ ClrBit(_m[t].m2, 6);
+ }
+
+ /* Clear PBS reservation on track */
+ if (IsRailDepot(t) ||IsRailWaypoint(t)) {
+ SetDepotWaypointReservation(t, false);
+ } else {
+ SetTrackReservation(t, TRACK_BIT_NONE);
+ }
+ break;
+
+ case MP_ROAD: /* Clear PBS reservation on crossing */
+ if (IsLevelCrossing(t)) SetCrossingReservation(t, false);
+ break;
+
+ case MP_STATION: /* Clear PBS reservation on station */
+ if (IsRailwayStation(t)) SetRailwayStationReservation(t, false);
+ break;
+
+ case MP_TUNNELBRIDGE: /* Clear PBS reservation on tunnels/birdges */
+ if (GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL) SetTunnelBridgeReservation(t, false);
+ break;
+
+ default: break;
+ }
+ }
+ }
+
+ /* Reserve all tracks trains are currently on. */
+ if (CheckSavegameVersion(101)) {
+ Vehicle *v;
+ FOR_ALL_VEHICLES(v) {
+ if (v->type == VEH_TRAIN) {
+ if ((v->u.rail.track & TRACK_BIT_WORMHOLE) == TRACK_BIT_WORMHOLE) {
+ TryReserveRailTrack(v->tile, DiagDirToDiagTrack(GetTunnelBridgeDirection(v->tile)));
+ } else if ((v->u.rail.track & TRACK_BIT_MASK) != TRACK_BIT_NONE) {
+ TryReserveRailTrack(v->tile, TrackBitsToTrack(v->u.rail.track));
+ }
+ }
+ }
+
+ /* Give owners to waypoints, based on rail tracks it is sitting on.
+ * If none is available, specify OWNER_NONE */
+ Waypoint *wp;
+ FOR_ALL_WAYPOINTS(wp) {
+ Owner owner = (IsRailWaypointTile(wp->xy) ? GetTileOwner(wp->xy) : OWNER_NONE);
+ wp->owner = IsValidCompanyID(owner) ? owner : OWNER_NONE;
+ }
+ }
+
+ if (CheckSavegameVersion(102)) {
+ for (TileIndex t = 0; t < map_size; t++) {
+ /* Now all crossings should be in correct state */
+ if (IsLevelCrossingTile(t)) UpdateLevelCrossing(t, false);
+ }
+ }
+
+ if (CheckSavegameVersion(103)) {
+ /* Non-town-owned roads now store the closest town */
+ UpdateNearestTownForRoadTiles(false);
+
+ /* signs with invalid owner left from older savegames */
+ Sign *si;
+ FOR_ALL_SIGNS(si) {
+ if (si->owner != OWNER_NONE && !IsValidCompanyID(si->owner)) si->owner = OWNER_NONE;
+ }
+
+ /* Station can get named based on an industry type, but the current ones
+ * are not, so mark them as if they are not named by an industry. */
+ Station *st;
+ FOR_ALL_STATIONS(st) {
+ st->indtype = IT_INVALID;
+ }
+ }
+
+ if (CheckSavegameVersion(104)) {
+ Vehicle *v;
+ FOR_ALL_VEHICLES(v) {
+ /* Set engine_type of shadow and rotor */
+ if (v->type == VEH_AIRCRAFT && !IsNormalAircraft(v)) {
+ v->engine_type = v->First()->engine_type;
+ }
+ }
+
+ /* More companies ... */
+ Company *c;
+ FOR_ALL_COMPANIES(c) {
+ if (c->bankrupt_asked == 0xFF) c->bankrupt_asked = 0xFFFF;
+ }
+
+ Engine *e;
+ FOR_ALL_ENGINES(e) {
+ if (e->company_avail == 0xFF) e->company_avail = 0xFFFF;
+ }
+
+ Town *t;
+ FOR_ALL_TOWNS(t) {
+ if (t->have_ratings == 0xFF) t->have_ratings = 0xFFFF;
+ for (uint i = 8; i != MAX_COMPANIES; i++) t->ratings[i] = RATING_INITIAL;
+ }
+ }
+
+ GamelogPrintDebug(1);
+
+ bool ret = InitializeWindowsAndCaches();
+ /* Restore the signals */
+ signal(SIGSEGV, prev_segfault);
+ signal(SIGABRT, prev_abort);
+ return ret;
+}
+
+/** Reload all NewGRF files during a running game. This is a cut-down
+ * version of AfterLoadGame().
+ * XXX - We need to reset the vehicle position hash because with a non-empty
+ * hash AfterLoadVehicles() will loop infinitely. We need AfterLoadVehicles()
+ * to recalculate vehicle data as some NewGRF vehicle sets could have been
+ * removed or added and changed statistics */
+void ReloadNewGRFData()
+{
+ /* reload grf data */
+ GfxLoadSprites();
+ LoadStringWidthTable();
+ ResetEconomy();
+ /* reload vehicles */
+ ResetVehiclePosHash();
+ AfterLoadVehicles(false);
+ StartupEngines();
+ SetCachedEngineCounts();
+ /* update station and waypoint graphics */
+ AfterLoadWaypoints();
+ AfterLoadStations();
+ /* Check and update house and town values */
+ UpdateHousesAndTowns();
+ /* Update livery selection windows */
+ for (CompanyID i = COMPANY_FIRST; i < MAX_COMPANIES; i++) InvalidateWindowData(WC_COMPANY_COLOR, i, _loaded_newgrf_features.has_2CC);
+ /* redraw the whole screen */
+ MarkWholeScreenDirty();
+ CheckTrainsLengths();
+}
diff --git a/src/saveload/ai_sl.cpp b/src/saveload/ai_sl.cpp
new file mode 100644
index 000000000..b4d16a6b5
--- /dev/null
+++ b/src/saveload/ai_sl.cpp
@@ -0,0 +1,79 @@
+/* $Id$ */
+
+/** @file ai_sl.cpp Code handling saving and loading of old AI + new AI initialisation after game load */
+
+#include "../stdafx.h"
+#include "../ai/ai.h"
+#include "../ai/default/default.h"
+
+#include "saveload.h"
+
+static const SaveLoad _company_ai_desc[] = {
+ SLE_VAR(CompanyAI, state, SLE_UINT8),
+ SLE_VAR(CompanyAI, tick, SLE_UINT8),
+ SLE_CONDVAR(CompanyAI, state_counter, SLE_FILE_U16 | SLE_VAR_U32, 0, 12),
+ SLE_CONDVAR(CompanyAI, state_counter, SLE_UINT32, 13, SL_MAX_VERSION),
+ SLE_VAR(CompanyAI, timeout_counter, SLE_UINT16),
+
+ SLE_VAR(CompanyAI, state_mode, SLE_UINT8),
+ SLE_VAR(CompanyAI, banned_tile_count, SLE_UINT8),
+ SLE_VAR(CompanyAI, railtype_to_use, SLE_UINT8),
+
+ SLE_VAR(CompanyAI, cargo_type, SLE_UINT8),
+ SLE_VAR(CompanyAI, num_wagons, SLE_UINT8),
+ SLE_VAR(CompanyAI, build_kind, SLE_UINT8),
+ SLE_VAR(CompanyAI, num_build_rec, SLE_UINT8),
+ SLE_VAR(CompanyAI, num_loco_to_build, SLE_UINT8),
+ SLE_VAR(CompanyAI, num_want_fullload, SLE_UINT8),
+
+ SLE_VAR(CompanyAI, route_type_mask, SLE_UINT8),
+
+ SLE_CONDVAR(CompanyAI, start_tile_a, SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
+ SLE_CONDVAR(CompanyAI, start_tile_a, SLE_UINT32, 6, SL_MAX_VERSION),
+ SLE_CONDVAR(CompanyAI, cur_tile_a, SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
+ SLE_CONDVAR(CompanyAI, cur_tile_a, SLE_UINT32, 6, SL_MAX_VERSION),
+ SLE_VAR(CompanyAI, start_dir_a, SLE_UINT8),
+ SLE_VAR(CompanyAI, cur_dir_a, SLE_UINT8),
+
+ SLE_CONDVAR(CompanyAI, start_tile_b, SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
+ SLE_CONDVAR(CompanyAI, start_tile_b, SLE_UINT32, 6, SL_MAX_VERSION),
+ SLE_CONDVAR(CompanyAI, cur_tile_b, SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
+ SLE_CONDVAR(CompanyAI, cur_tile_b, SLE_UINT32, 6, SL_MAX_VERSION),
+ SLE_VAR(CompanyAI, start_dir_b, SLE_UINT8),
+ SLE_VAR(CompanyAI, cur_dir_b, SLE_UINT8),
+
+ SLE_REF(CompanyAI, cur_veh, REF_VEHICLE),
+
+ SLE_ARR(CompanyAI, wagon_list, SLE_UINT16, 9),
+ SLE_ARR(CompanyAI, order_list_blocks, SLE_UINT8, 20),
+ SLE_ARR(CompanyAI, banned_tiles, SLE_UINT16, 16),
+
+ SLE_CONDNULL(64, 2, SL_MAX_VERSION),
+ SLE_END()
+};
+
+static const SaveLoad _company_ai_build_rec_desc[] = {
+ SLE_CONDVAR(AiBuildRec, spec_tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
+ SLE_CONDVAR(AiBuildRec, spec_tile, SLE_UINT32, 6, SL_MAX_VERSION),
+ SLE_CONDVAR(AiBuildRec, use_tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
+ SLE_CONDVAR(AiBuildRec, use_tile, SLE_UINT32, 6, SL_MAX_VERSION),
+ SLE_VAR(AiBuildRec, rand_rng, SLE_UINT8),
+ SLE_VAR(AiBuildRec, cur_building_rule, SLE_UINT8),
+ SLE_VAR(AiBuildRec, unk6, SLE_UINT8),
+ SLE_VAR(AiBuildRec, unk7, SLE_UINT8),
+ SLE_VAR(AiBuildRec, buildcmd_a, SLE_UINT8),
+ SLE_VAR(AiBuildRec, buildcmd_b, SLE_UINT8),
+ SLE_VAR(AiBuildRec, direction, SLE_UINT8),
+ SLE_VAR(AiBuildRec, cargo, SLE_UINT8),
+ SLE_END()
+};
+
+
+void SaveLoad_AI(CompanyID company)
+{
+ CompanyAI *cai = &_companies_ai[company];
+ SlObject(cai, _company_ai_desc);
+ for (int i = 0; i != cai->num_build_rec; i++) {
+ SlObject(&cai->src + i, _company_ai_build_rec_desc);
+ }
+}
diff --git a/src/saveload/animated_tile_sl.cpp b/src/saveload/animated_tile_sl.cpp
new file mode 100644
index 000000000..ba3f519db
--- /dev/null
+++ b/src/saveload/animated_tile_sl.cpp
@@ -0,0 +1,56 @@
+/* $Id$ */
+
+/** @file animated_tile_sl.cpp Code handling saving and loading of animated tiles */
+
+#include "../stdafx.h"
+#include "../tile_type.h"
+#include "../core/alloc_func.hpp"
+
+#include "saveload.h"
+
+extern TileIndex *_animated_tile_list;
+extern uint _animated_tile_count;
+extern uint _animated_tile_allocated;
+
+/**
+ * Save the ANIT chunk.
+ */
+static void Save_ANIT()
+{
+ SlSetLength(_animated_tile_count * sizeof(*_animated_tile_list));
+ SlArray(_animated_tile_list, _animated_tile_count, SLE_UINT32);
+}
+
+/**
+ * Load the ANIT chunk; the chunk containing the animated tiles.
+ */
+static void Load_ANIT()
+{
+ /* Before version 80 we did NOT have a variable length animated tile table */
+ if (CheckSavegameVersion(80)) {
+ /* In pre version 6, we has 16bit per tile, now we have 32bit per tile, convert it ;) */
+ SlArray(_animated_tile_list, 256, CheckSavegameVersion(6) ? (SLE_FILE_U16 | SLE_VAR_U32) : SLE_UINT32);
+
+ for (_animated_tile_count = 0; _animated_tile_count < 256; _animated_tile_count++) {
+ if (_animated_tile_list[_animated_tile_count] == 0) break;
+ }
+ return;
+ }
+
+ _animated_tile_count = (uint)SlGetFieldLength() / sizeof(*_animated_tile_list);
+
+ /* Determine a nice rounded size for the amount of allocated tiles */
+ _animated_tile_allocated = 256;
+ while (_animated_tile_allocated < _animated_tile_count) _animated_tile_allocated *= 2;
+
+ _animated_tile_list = ReallocT<TileIndex>(_animated_tile_list, _animated_tile_allocated);
+ SlArray(_animated_tile_list, _animated_tile_count, SLE_UINT32);
+}
+
+/**
+ * "Definition" imported by the saveload code to be able to load and save
+ * the animated tile table.
+ */
+extern const ChunkHandler _animated_tile_chunk_handlers[] = {
+ { 'ANIT', Save_ANIT, Load_ANIT, CH_RIFF | CH_LAST},
+};
diff --git a/src/saveload/autoreplace_sl.cpp b/src/saveload/autoreplace_sl.cpp
new file mode 100644
index 000000000..d24c290a8
--- /dev/null
+++ b/src/saveload/autoreplace_sl.cpp
@@ -0,0 +1,51 @@
+/* $Id$ */
+
+/** @file autoreplace_sl.cpp Code handling saving and loading of autoreplace rules */
+
+#include "../stdafx.h"
+#include "../autoreplace_type.h"
+#include "../engine_type.h"
+#include "../group_type.h"
+#include "../autoreplace_base.h"
+
+#include "saveload.h"
+
+static const SaveLoad _engine_renew_desc[] = {
+ SLE_VAR(EngineRenew, from, SLE_UINT16),
+ SLE_VAR(EngineRenew, to, SLE_UINT16),
+
+ SLE_REF(EngineRenew, next, REF_ENGINE_RENEWS),
+ SLE_CONDVAR(EngineRenew, group_id, SLE_UINT16, 60, SL_MAX_VERSION),
+ SLE_END()
+};
+
+static void Save_ERNW()
+{
+ EngineRenew *er;
+
+ FOR_ALL_ENGINE_RENEWS(er) {
+ SlSetArrayIndex(er->index);
+ SlObject(er, _engine_renew_desc);
+ }
+}
+
+static void Load_ERNW()
+{
+ int index;
+
+ while ((index = SlIterateArray()) != -1) {
+ EngineRenew *er = new (index) EngineRenew();
+ SlObject(er, _engine_renew_desc);
+
+ /* Advanced vehicle lists, ungrouped vehicles got added */
+ if (CheckSavegameVersion(60)) {
+ er->group_id = ALL_GROUP;
+ } else if (CheckSavegameVersion(71)) {
+ if (er->group_id == DEFAULT_GROUP) er->group_id = ALL_GROUP;
+ }
+ }
+}
+
+extern const ChunkHandler _autoreplace_chunk_handlers[] = {
+ { 'ERNW', Save_ERNW, Load_ERNW, CH_ARRAY | CH_LAST},
+};
diff --git a/src/saveload/cargopacket_sl.cpp b/src/saveload/cargopacket_sl.cpp
new file mode 100644
index 000000000..d38e4e22e
--- /dev/null
+++ b/src/saveload/cargopacket_sl.cpp
@@ -0,0 +1,45 @@
+/* $Id$ */
+
+/** @file cargopacket_sl.cpp Code handling saving and loading of cargo packets */
+
+#include "../stdafx.h"
+#include "../openttd.h"
+#include "../cargopacket.h"
+
+#include "saveload.h"
+
+static const SaveLoad _cargopacket_desc[] = {
+ SLE_VAR(CargoPacket, source, SLE_UINT16),
+ SLE_VAR(CargoPacket, source_xy, SLE_UINT32),
+ SLE_VAR(CargoPacket, loaded_at_xy, SLE_UINT32),
+ SLE_VAR(CargoPacket, count, SLE_UINT16),
+ SLE_VAR(CargoPacket, days_in_transit, SLE_UINT8),
+ SLE_VAR(CargoPacket, feeder_share, SLE_INT64),
+ SLE_VAR(CargoPacket, paid_for, SLE_BOOL),
+
+ SLE_END()
+};
+
+static void Save_CAPA()
+{
+ CargoPacket *cp;
+
+ FOR_ALL_CARGOPACKETS(cp) {
+ SlSetArrayIndex(cp->index);
+ SlObject(cp, _cargopacket_desc);
+ }
+}
+
+static void Load_CAPA()
+{
+ int index;
+
+ while ((index = SlIterateArray()) != -1) {
+ CargoPacket *cp = new (index) CargoPacket();
+ SlObject(cp, _cargopacket_desc);
+ }
+}
+
+extern const ChunkHandler _cargopacket_chunk_handlers[] = {
+ { 'CAPA', Save_CAPA, Load_CAPA, CH_ARRAY | CH_LAST},
+};
diff --git a/src/saveload/cheat_sl.cpp b/src/saveload/cheat_sl.cpp
new file mode 100644
index 000000000..a8c939fed
--- /dev/null
+++ b/src/saveload/cheat_sl.cpp
@@ -0,0 +1,37 @@
+/* $Id$ */
+
+/** @file cheat_sl.cpp Code handling saving and loading of cheats */
+
+#include "../stdafx.h"
+#include "../cheat_type.h"
+
+#include "saveload.h"
+
+static void Save_CHTS()
+{
+ /* Cannot use lengthof because _cheats is of type Cheats, not Cheat */
+ byte count = sizeof(_cheats) / sizeof(Cheat);
+ Cheat *cht = (Cheat*) &_cheats;
+ Cheat *cht_last = &cht[count];
+
+ SlSetLength(count * 2);
+ for (; cht != cht_last; cht++) {
+ SlWriteByte(cht->been_used);
+ SlWriteByte(cht->value);
+ }
+}
+
+static void Load_CHTS()
+{
+ Cheat *cht = (Cheat*)&_cheats;
+ size_t count = SlGetFieldLength() / 2;
+
+ for (uint i = 0; i < count; i++) {
+ cht[i].been_used = (SlReadByte() != 0);
+ cht[i].value = (SlReadByte() != 0);
+ }
+}
+
+extern const ChunkHandler _cheat_chunk_handlers[] = {
+ { 'CHTS', Save_CHTS, Load_CHTS, CH_RIFF | CH_LAST}
+};
diff --git a/src/saveload/company_sl.cpp b/src/saveload/company_sl.cpp
new file mode 100644
index 000000000..933cc0bde
--- /dev/null
+++ b/src/saveload/company_sl.cpp
@@ -0,0 +1,240 @@
+/* $Id$ */
+
+/** @file company_sl.cpp Code handling saving and loading of company data */
+
+#include "../stdafx.h"
+#include "../company_base.h"
+#include "../company_func.h"
+#include "../network/network.h"
+#include "../ai/ai.h"
+#include "../ai/trolly/trolly.h"
+#include "../company_manager_face.h"
+
+#include "saveload.h"
+
+/**
+ * Converts an old company manager's face format to the new company manager's face format
+ *
+ * Meaning of the bits in the old face (some bits are used in several times):
+ * - 4 and 5: chin
+ * - 6 to 9: eyebrows
+ * - 10 to 13: nose
+ * - 13 to 15: lips (also moustache for males)
+ * - 16 to 19: hair
+ * - 20 to 22: eye color
+ * - 20 to 27: tie, ear rings etc.
+ * - 28 to 30: glasses
+ * - 19, 26 and 27: race (bit 27 set and bit 19 equal to bit 26 = black, otherwise white)
+ * - 31: gender (0 = male, 1 = female)
+ *
+ * @param face the face in the old format
+ * @return the face in the new format
+ */
+CompanyManagerFace ConvertFromOldCompanyManagerFace(uint32 face)
+{
+ CompanyManagerFace cmf = 0;
+ GenderEthnicity ge = GE_WM;
+
+ if (HasBit(face, 31)) SetBit(ge, GENDER_FEMALE);
+ if (HasBit(face, 27) && (HasBit(face, 26) == HasBit(face, 19))) SetBit(ge, ETHNICITY_BLACK);
+
+ SetCompanyManagerFaceBits(cmf, CMFV_GEN_ETHN, ge, ge);
+ SetCompanyManagerFaceBits(cmf, CMFV_HAS_GLASSES, ge, GB(face, 28, 3) <= 1);
+ SetCompanyManagerFaceBits(cmf, CMFV_EYE_COLOUR, ge, HasBit(ge, ETHNICITY_BLACK) ? 0 : ClampU(GB(face, 20, 3), 5, 7) - 5);
+ SetCompanyManagerFaceBits(cmf, CMFV_CHIN, ge, ScaleCompanyManagerFaceValue(CMFV_CHIN, ge, GB(face, 4, 2)));
+ SetCompanyManagerFaceBits(cmf, CMFV_EYEBROWS, ge, ScaleCompanyManagerFaceValue(CMFV_EYEBROWS, ge, GB(face, 6, 4)));
+ SetCompanyManagerFaceBits(cmf, CMFV_HAIR, ge, ScaleCompanyManagerFaceValue(CMFV_HAIR, ge, GB(face, 16, 4)));
+ SetCompanyManagerFaceBits(cmf, CMFV_JACKET, ge, ScaleCompanyManagerFaceValue(CMFV_JACKET, ge, GB(face, 20, 2)));
+ SetCompanyManagerFaceBits(cmf, CMFV_COLLAR, ge, ScaleCompanyManagerFaceValue(CMFV_COLLAR, ge, GB(face, 22, 2)));
+ SetCompanyManagerFaceBits(cmf, CMFV_GLASSES, ge, GB(face, 28, 1));
+
+ uint lips = GB(face, 10, 4);
+ if (!HasBit(ge, GENDER_FEMALE) && lips < 4) {
+ SetCompanyManagerFaceBits(cmf, CMFV_HAS_MOUSTACHE, ge, true);
+ SetCompanyManagerFaceBits(cmf, CMFV_MOUSTACHE, ge, max(lips, 1U) - 1);
+ } else {
+ if (!HasBit(ge, GENDER_FEMALE)) {
+ lips = lips * 15 / 16;
+ lips -= 3;
+ if (HasBit(ge, ETHNICITY_BLACK) && lips > 8) lips = 0;
+ } else {
+ lips = ScaleCompanyManagerFaceValue(CMFV_LIPS, ge, lips);
+ }
+ SetCompanyManagerFaceBits(cmf, CMFV_LIPS, ge, lips);
+
+ uint nose = GB(face, 13, 3);
+ if (ge == GE_WF) {
+ nose = (nose * 3 >> 3) * 3 >> 2; // There is 'hole' in the nose sprites for females
+ } else {
+ nose = ScaleCompanyManagerFaceValue(CMFV_NOSE, ge, nose);
+ }
+ SetCompanyManagerFaceBits(cmf, CMFV_NOSE, ge, nose);
+ }
+
+ uint tie_earring = GB(face, 24, 4);
+ if (!HasBit(ge, GENDER_FEMALE) || tie_earring < 3) { // Not all females have an earring
+ if (HasBit(ge, GENDER_FEMALE)) SetCompanyManagerFaceBits(cmf, CMFV_HAS_TIE_EARRING, ge, true);
+ SetCompanyManagerFaceBits(cmf, CMFV_TIE_EARRING, ge, HasBit(ge, GENDER_FEMALE) ? tie_earring : ScaleCompanyManagerFaceValue(CMFV_TIE_EARRING, ge, tie_earring / 2));
+ }
+
+ return cmf;
+}
+
+
+
+/* Save/load of companies */
+static const SaveLoad _company_desc[] = {
+ SLE_VAR(Company, name_2, SLE_UINT32),
+ SLE_VAR(Company, name_1, SLE_STRINGID),
+ SLE_CONDSTR(Company, name, SLE_STR, 0, 84, SL_MAX_VERSION),
+
+ SLE_VAR(Company, president_name_1, SLE_UINT16),
+ SLE_VAR(Company, president_name_2, SLE_UINT32),
+ SLE_CONDSTR(Company, president_name, SLE_STR, 0, 84, SL_MAX_VERSION),
+
+ SLE_VAR(Company, face, SLE_UINT32),
+
+ /* money was changed to a 64 bit field in savegame version 1. */
+ SLE_CONDVAR(Company, money, SLE_VAR_I64 | SLE_FILE_I32, 0, 0),
+ SLE_CONDVAR(Company, money, SLE_INT64, 1, SL_MAX_VERSION),
+
+ SLE_CONDVAR(Company, current_loan, SLE_VAR_I64 | SLE_FILE_I32, 0, 64),
+ SLE_CONDVAR(Company, current_loan, SLE_INT64, 65, SL_MAX_VERSION),
+
+ SLE_VAR(Company, colour, SLE_UINT8),
+ SLE_VAR(Company, money_fraction, SLE_UINT8),
+ SLE_CONDVAR(Company, avail_railtypes, SLE_UINT8, 0, 57),
+ SLE_VAR(Company, block_preview, SLE_UINT8),
+
+ SLE_CONDVAR(Company, cargo_types, SLE_FILE_U16 | SLE_VAR_U32, 0, 93),
+ SLE_CONDVAR(Company, cargo_types, SLE_UINT32, 94, SL_MAX_VERSION),
+ SLE_CONDVAR(Company, location_of_HQ, SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
+ SLE_CONDVAR(Company, location_of_HQ, SLE_UINT32, 6, SL_MAX_VERSION),
+ SLE_CONDVAR(Company, last_build_coordinate, SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
+ SLE_CONDVAR(Company, last_build_coordinate, SLE_UINT32, 6, SL_MAX_VERSION),
+ SLE_CONDVAR(Company, inaugurated_year, SLE_FILE_U8 | SLE_VAR_I32, 0, 30),
+ SLE_CONDVAR(Company, inaugurated_year, SLE_INT32, 31, SL_MAX_VERSION),
+
+ SLE_ARR(Company, share_owners, SLE_UINT8, 4),
+
+ SLE_VAR(Company, num_valid_stat_ent, SLE_UINT8),
+
+ SLE_VAR(Company, quarters_of_bankrupcy, SLE_UINT8),
+ SLE_CONDVAR(Company, bankrupt_asked, SLE_FILE_U8 | SLE_VAR_U16, 0, 103),
+ SLE_CONDVAR(Company, bankrupt_asked, SLE_UINT16, 104, SL_MAX_VERSION),
+ SLE_VAR(Company, bankrupt_timeout, SLE_INT16),
+ SLE_CONDVAR(Company, bankrupt_value, SLE_VAR_I64 | SLE_FILE_I32, 0, 64),
+ SLE_CONDVAR(Company, bankrupt_value, SLE_INT64, 65, SL_MAX_VERSION),
+
+ /* yearly expenses was changed to 64-bit in savegame version 2. */
+ SLE_CONDARR(Company, yearly_expenses, SLE_FILE_I32 | SLE_VAR_I64, 3 * 13, 0, 1),
+ SLE_CONDARR(Company, yearly_expenses, SLE_INT64, 3 * 13, 2, SL_MAX_VERSION),
+
+ SLE_CONDVAR(Company, is_ai, SLE_BOOL, 2, SL_MAX_VERSION),
+ SLE_CONDNULL(1, 4, 99),
+
+ /* Engine renewal settings */
+ SLE_CONDNULL(512, 16, 18),
+ SLE_CONDREF(Company, engine_renew_list, REF_ENGINE_RENEWS, 19, SL_MAX_VERSION),
+ SLE_CONDVAR(Company, engine_renew, SLE_BOOL, 16, SL_MAX_VERSION),
+ SLE_CONDVAR(Company, engine_renew_months, SLE_INT16, 16, SL_MAX_VERSION),
+ SLE_CONDVAR(Company, engine_renew_money, SLE_UINT32, 16, SL_MAX_VERSION),
+ SLE_CONDVAR(Company, renew_keep_length, SLE_BOOL, 2, SL_MAX_VERSION), // added with 16.1, but was blank since 2
+
+ /* reserve extra space in savegame here. (currently 63 bytes) */
+ SLE_CONDNULL(63, 2, SL_MAX_VERSION),
+
+ SLE_END()
+};
+
+static const SaveLoad _company_economy_desc[] = {
+ /* these were changed to 64-bit in savegame format 2 */
+ SLE_CONDVAR(CompanyEconomyEntry, income, SLE_FILE_I32 | SLE_VAR_I64, 0, 1),
+ SLE_CONDVAR(CompanyEconomyEntry, income, SLE_INT64, 2, SL_MAX_VERSION),
+ SLE_CONDVAR(CompanyEconomyEntry, expenses, SLE_FILE_I32 | SLE_VAR_I64, 0, 1),
+ SLE_CONDVAR(CompanyEconomyEntry, expenses, SLE_INT64, 2, SL_MAX_VERSION),
+ SLE_CONDVAR(CompanyEconomyEntry, company_value, SLE_FILE_I32 | SLE_VAR_I64, 0, 1),
+ SLE_CONDVAR(CompanyEconomyEntry, company_value, SLE_INT64, 2, SL_MAX_VERSION),
+
+ SLE_VAR(CompanyEconomyEntry, delivered_cargo, SLE_INT32),
+ SLE_VAR(CompanyEconomyEntry, performance_history, SLE_INT32),
+
+ SLE_END()
+};
+
+static const SaveLoad _company_livery_desc[] = {
+ SLE_CONDVAR(Livery, in_use, SLE_BOOL, 34, SL_MAX_VERSION),
+ SLE_CONDVAR(Livery, colour1, SLE_UINT8, 34, SL_MAX_VERSION),
+ SLE_CONDVAR(Livery, colour2, SLE_UINT8, 34, SL_MAX_VERSION),
+ SLE_END()
+};
+
+static void SaveLoad_PLYR(Company *c)
+{
+ int i;
+
+ SlObject(c, _company_desc);
+
+ /* Write AI? */
+ if (!IsHumanCompany(c->index)) {
+ extern void SaveLoad_AI(CompanyID company);
+ SaveLoad_AI(c->index);
+ }
+
+ /* Write economy */
+ SlObject(&c->cur_economy, _company_economy_desc);
+
+ /* Write old economy entries. */
+ for (i = 0; i < c->num_valid_stat_ent; i++) {
+ SlObject(&c->old_economy[i], _company_economy_desc);
+ }
+
+ /* Write each livery entry. */
+ int num_liveries = CheckSavegameVersion(63) ? LS_END - 4 : (CheckSavegameVersion(85) ? LS_END - 2: LS_END);
+ for (i = 0; i < num_liveries; i++) {
+ SlObject(&c->livery[i], _company_livery_desc);
+ }
+
+ if (num_liveries < LS_END) {
+ /* We want to insert some liveries somewhere in between. This means some have to be moved. */
+ memmove(&c->livery[LS_FREIGHT_WAGON], &c->livery[LS_PASSENGER_WAGON_MONORAIL], (LS_END - LS_FREIGHT_WAGON) * sizeof(c->livery[0]));
+ c->livery[LS_PASSENGER_WAGON_MONORAIL] = c->livery[LS_MONORAIL];
+ c->livery[LS_PASSENGER_WAGON_MAGLEV] = c->livery[LS_MAGLEV];
+ }
+
+ if (num_liveries == LS_END - 4) {
+ /* Copy bus/truck liveries over to trams */
+ c->livery[LS_PASSENGER_TRAM] = c->livery[LS_BUS];
+ c->livery[LS_FREIGHT_TRAM] = c->livery[LS_TRUCK];
+ }
+}
+
+static void Save_PLYR()
+{
+ Company *c;
+ FOR_ALL_COMPANIES(c) {
+ SlSetArrayIndex(c->index);
+ SlAutolength((AutolengthProc*)SaveLoad_PLYR, c);
+ }
+}
+
+static void Load_PLYR()
+{
+ int index;
+ while ((index = SlIterateArray()) != -1) {
+ Company *c = new (index) Company();
+ SaveLoad_PLYR(c);
+ _company_colours[index] = c->colour;
+
+ /* This is needed so an AI is attached to a loaded AI */
+ if (c->is_ai && (!_networking || _network_server) && _ai.enabled) {
+ /* Clear the memory of the new AI, otherwise we might be doing wrong things. */
+ memset(&_companies_ainew[index], 0, sizeof(CompanyAiNew));
+ AI_StartNewAI(c->index);
+ }
+ }
+}
+
+extern const ChunkHandler _company_chunk_handlers[] = {
+ { 'PLYR', Save_PLYR, Load_PLYR, CH_ARRAY | CH_LAST},
+};
diff --git a/src/saveload/depot_sl.cpp b/src/saveload/depot_sl.cpp
new file mode 100644
index 000000000..fb546ef73
--- /dev/null
+++ b/src/saveload/depot_sl.cpp
@@ -0,0 +1,39 @@
+/* $Id$ */
+
+/** @file depot_sl.cpp Code handling saving and loading of depots */
+
+#include "../stdafx.h"
+#include "../depot_base.h"
+
+#include "saveload.h"
+
+static const SaveLoad _depot_desc[] = {
+ SLE_CONDVAR(Depot, xy, SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
+ SLE_CONDVAR(Depot, xy, SLE_UINT32, 6, SL_MAX_VERSION),
+ SLE_VAR(Depot, town_index, SLE_UINT16),
+ SLE_END()
+};
+
+static void Save_DEPT()
+{
+ Depot *depot;
+
+ FOR_ALL_DEPOTS(depot) {
+ SlSetArrayIndex(depot->index);
+ SlObject(depot, _depot_desc);
+ }
+}
+
+static void Load_DEPT()
+{
+ int index;
+
+ while ((index = SlIterateArray()) != -1) {
+ Depot *depot = new (index) Depot();
+ SlObject(depot, _depot_desc);
+ }
+}
+
+extern const ChunkHandler _depot_chunk_handlers[] = {
+ { 'DEPT', Save_DEPT, Load_DEPT, CH_ARRAY | CH_LAST},
+};
diff --git a/src/saveload/economy_sl.cpp b/src/saveload/economy_sl.cpp
new file mode 100644
index 000000000..99f306f52
--- /dev/null
+++ b/src/saveload/economy_sl.cpp
@@ -0,0 +1,58 @@
+/* $Id$ */
+
+/** @file economy_sl.cpp Code handling saving and loading of economy data */
+
+#include "../stdafx.h"
+#include "../economy_func.h"
+
+#include "saveload.h"
+
+/** Prices */
+static void SaveLoad_PRIC()
+{
+ int vt = CheckSavegameVersion(65) ? (SLE_FILE_I32 | SLE_VAR_I64) : SLE_INT64;
+ SlArray(&_price, NUM_PRICES, vt);
+ SlArray(&_price_frac, NUM_PRICES, SLE_UINT16);
+}
+
+/** Cargo payment rates */
+static void SaveLoad_CAPR()
+{
+ uint num_cargo = CheckSavegameVersion(55) ? 12 : NUM_CARGO;
+ int vt = CheckSavegameVersion(65) ? (SLE_FILE_I32 | SLE_VAR_I64) : SLE_INT64;
+ SlArray(&_cargo_payment_rates, num_cargo, vt);
+ SlArray(&_cargo_payment_rates_frac, num_cargo, SLE_UINT16);
+}
+
+static const SaveLoad _economy_desc[] = {
+ SLE_CONDVAR(Economy, max_loan, SLE_FILE_I32 | SLE_VAR_I64, 0, 64),
+ SLE_CONDVAR(Economy, max_loan, SLE_INT64, 65, SL_MAX_VERSION),
+ SLE_CONDVAR(Economy, max_loan_unround, SLE_FILE_I32 | SLE_VAR_I64, 0, 64),
+ SLE_CONDVAR(Economy, max_loan_unround, SLE_INT64, 65, SL_MAX_VERSION),
+ SLE_CONDVAR(Economy, max_loan_unround_fract, SLE_UINT16, 70, SL_MAX_VERSION),
+ SLE_VAR(Economy, fluct, SLE_INT16),
+ SLE_VAR(Economy, interest_rate, SLE_UINT8),
+ SLE_VAR(Economy, infl_amount, SLE_UINT8),
+ SLE_VAR(Economy, infl_amount_pr, SLE_UINT8),
+ SLE_CONDVAR(Economy, industry_daily_change_counter, SLE_UINT32, 102, SL_MAX_VERSION),
+ SLE_END()
+};
+
+/** Economy variables */
+static void Save_ECMY()
+{
+ SlObject(&_economy, _economy_desc);
+}
+
+/** Economy variables */
+static void Load_ECMY()
+{
+ SlObject(&_economy, _economy_desc);
+ StartupIndustryDailyChanges(CheckSavegameVersion(102)); // old savegames will need to be initialized
+}
+
+extern const ChunkHandler _economy_chunk_handlers[] = {
+ { 'PRIC', SaveLoad_PRIC, SaveLoad_PRIC, CH_RIFF | CH_AUTO_LENGTH},
+ { 'CAPR', SaveLoad_CAPR, SaveLoad_CAPR, CH_RIFF | CH_AUTO_LENGTH},
+ { 'ECMY', Save_ECMY, Load_ECMY, CH_RIFF | CH_LAST},
+};
diff --git a/src/saveload/engine_sl.cpp b/src/saveload/engine_sl.cpp
new file mode 100644
index 000000000..6dc47c56a
--- /dev/null
+++ b/src/saveload/engine_sl.cpp
@@ -0,0 +1,118 @@
+/* $Id$ */
+
+/** @file engine_sl.cpp Code handling saving and loading of engines */
+
+#include "../stdafx.h"
+#include "saveload.h"
+#include "saveload_internal.h"
+#include "../engine_base.h"
+#include <map>
+
+static const SaveLoad _engine_desc[] = {
+ SLE_CONDVAR(Engine, intro_date, SLE_FILE_U16 | SLE_VAR_I32, 0, 30),
+ SLE_CONDVAR(Engine, intro_date, SLE_INT32, 31, SL_MAX_VERSION),
+ SLE_CONDVAR(Engine, age, SLE_FILE_U16 | SLE_VAR_I32, 0, 30),
+ SLE_CONDVAR(Engine, age, SLE_INT32, 31, SL_MAX_VERSION),
+ SLE_VAR(Engine, reliability, SLE_UINT16),
+ SLE_VAR(Engine, reliability_spd_dec, SLE_UINT16),
+ SLE_VAR(Engine, reliability_start, SLE_UINT16),
+ SLE_VAR(Engine, reliability_max, SLE_UINT16),
+ SLE_VAR(Engine, reliability_final, SLE_UINT16),
+ SLE_VAR(Engine, duration_phase_1, SLE_UINT16),
+ SLE_VAR(Engine, duration_phase_2, SLE_UINT16),
+ SLE_VAR(Engine, duration_phase_3, SLE_UINT16),
+
+ SLE_VAR(Engine, lifelength, SLE_UINT8),
+ SLE_VAR(Engine, flags, SLE_UINT8),
+ SLE_VAR(Engine, preview_company_rank,SLE_UINT8),
+ SLE_VAR(Engine, preview_wait, SLE_UINT8),
+ SLE_CONDNULL(1, 0, 44),
+ SLE_CONDVAR(Engine, company_avail, SLE_FILE_U8 | SLE_VAR_U16, 0, 103),
+ SLE_CONDVAR(Engine, company_avail, SLE_UINT16, 104, SL_MAX_VERSION),
+ SLE_CONDSTR(Engine, name, SLE_STR, 0, 84, SL_MAX_VERSION),
+
+ /* reserve extra space in savegame here. (currently 16 bytes) */
+ SLE_CONDNULL(16, 2, SL_MAX_VERSION),
+
+ SLE_END()
+};
+
+static std::map<EngineID, Engine> _temp_engine;
+
+Engine *GetTempDataEngine(EngineID index)
+{
+ return &_temp_engine[index];
+}
+
+static void Save_ENGN()
+{
+ Engine *e;
+ FOR_ALL_ENGINES(e) {
+ SlSetArrayIndex(e->index);
+ SlObject(e, _engine_desc);
+ }
+}
+
+static void Load_ENGN()
+{
+ /* As engine data is loaded before engines are initialized we need to load
+ * this information into a temporary array. This is then copied into the
+ * engine pool after processing NewGRFs by CopyTempEngineData(). */
+ int index;
+ while ((index = SlIterateArray()) != -1) {
+ Engine *e = GetTempDataEngine(index);
+ SlObject(e, _engine_desc);
+ }
+}
+
+/**
+ * Copy data from temporary engine array into the real engine pool.
+ */
+void CopyTempEngineData()
+{
+ Engine *e;
+ FOR_ALL_ENGINES(e) {
+ if (e->index >= _temp_engine.size()) break;
+
+ const Engine *se = GetTempDataEngine(e->index);
+ e->intro_date = se->intro_date;
+ e->age = se->age;
+ e->reliability = se->reliability;
+ e->reliability_spd_dec = se->reliability_spd_dec;
+ e->reliability_start = se->reliability_start;
+ e->reliability_max = se->reliability_max;
+ e->reliability_final = se->reliability_final;
+ e->duration_phase_1 = se->duration_phase_1;
+ e->duration_phase_2 = se->duration_phase_2;
+ e->duration_phase_3 = se->duration_phase_3;
+ e->lifelength = se->lifelength;
+ e->flags = se->flags;
+ e->preview_company_rank= se->preview_company_rank;
+ e->preview_wait = se->preview_wait;
+ e->company_avail = se->company_avail;
+ if (se->name != NULL) e->name = strdup(se->name);
+ }
+
+ /* Get rid of temporary data */
+ _temp_engine.clear();
+}
+
+static void Load_ENGS()
+{
+ /* Load old separate String ID list into a temporary array. This
+ * was always 256 entries. */
+ StringID names[256];
+
+ SlArray(names, lengthof(names), SLE_STRINGID);
+
+ /* Copy each string into the temporary engine array. */
+ for (EngineID engine = 0; engine < lengthof(names); engine++) {
+ Engine *e = GetTempDataEngine(engine);
+ e->name = CopyFromOldName(names[engine]);
+ }
+}
+
+extern const ChunkHandler _engine_chunk_handlers[] = {
+ { 'ENGN', Save_ENGN, Load_ENGN, CH_ARRAY },
+ { 'ENGS', NULL, Load_ENGS, CH_RIFF | CH_LAST },
+};
diff --git a/src/saveload/gamelog_sl.cpp b/src/saveload/gamelog_sl.cpp
new file mode 100644
index 000000000..7364602bb
--- /dev/null
+++ b/src/saveload/gamelog_sl.cpp
@@ -0,0 +1,161 @@
+/* $Id$ */
+
+/** @file gamelog_sl.cpp Code handling saving and loading of gamelog data */
+
+#include "../stdafx.h"
+#include "../gamelog.h"
+#include "../gamelog_internal.h"
+#include "../core/alloc_func.hpp"
+
+#include "saveload.h"
+
+static const SaveLoad _glog_action_desc[] = {
+ SLE_VAR(LoggedAction, tick, SLE_UINT16),
+ SLE_END()
+};
+
+static const SaveLoad _glog_mode_desc[] = {
+ SLE_VAR(LoggedChange, mode.mode, SLE_UINT8),
+ SLE_VAR(LoggedChange, mode.landscape, SLE_UINT8),
+ SLE_END()
+};
+
+static const SaveLoad _glog_revision_desc[] = {
+ SLE_ARR(LoggedChange, revision.text, SLE_UINT8, NETWORK_REVISION_LENGTH),
+ SLE_VAR(LoggedChange, revision.newgrf, SLE_UINT32),
+ SLE_VAR(LoggedChange, revision.slver, SLE_UINT16),
+ SLE_VAR(LoggedChange, revision.modified, SLE_UINT8),
+ SLE_END()
+};
+
+static const SaveLoad _glog_oldver_desc[] = {
+ SLE_VAR(LoggedChange, oldver.type, SLE_UINT32),
+ SLE_VAR(LoggedChange, oldver.version, SLE_UINT32),
+ SLE_END()
+};
+
+static const SaveLoad _glog_patch_desc[] = {
+ SLE_STR(LoggedChange, patch.name, SLE_STR, 128),
+ SLE_VAR(LoggedChange, patch.oldval, SLE_INT32),
+ SLE_VAR(LoggedChange, patch.newval, SLE_INT32),
+ SLE_END()
+};
+
+static const SaveLoad _glog_grfadd_desc[] = {
+ SLE_VAR(LoggedChange, grfadd.grfid, SLE_UINT32 ),
+ SLE_ARR(LoggedChange, grfadd.md5sum, SLE_UINT8, 16),
+ SLE_END()
+};
+
+static const SaveLoad _glog_grfrem_desc[] = {
+ SLE_VAR(LoggedChange, grfrem.grfid, SLE_UINT32),
+ SLE_END()
+};
+
+static const SaveLoad _glog_grfcompat_desc[] = {
+ SLE_VAR(LoggedChange, grfcompat.grfid, SLE_UINT32 ),
+ SLE_ARR(LoggedChange, grfcompat.md5sum, SLE_UINT8, 16),
+ SLE_END()
+};
+
+static const SaveLoad _glog_grfparam_desc[] = {
+ SLE_VAR(LoggedChange, grfparam.grfid, SLE_UINT32),
+ SLE_END()
+};
+
+static const SaveLoad _glog_grfmove_desc[] = {
+ SLE_VAR(LoggedChange, grfmove.grfid, SLE_UINT32),
+ SLE_VAR(LoggedChange, grfmove.offset, SLE_INT32),
+ SLE_END()
+};
+
+static const SaveLoad _glog_grfbug_desc[] = {
+ SLE_VAR(LoggedChange, grfbug.data, SLE_UINT64),
+ SLE_VAR(LoggedChange, grfbug.grfid, SLE_UINT32),
+ SLE_VAR(LoggedChange, grfbug.bug, SLE_UINT8),
+ SLE_END()
+};
+
+static const SaveLoad *_glog_desc[] = {
+ _glog_mode_desc,
+ _glog_revision_desc,
+ _glog_oldver_desc,
+ _glog_patch_desc,
+ _glog_grfadd_desc,
+ _glog_grfrem_desc,
+ _glog_grfcompat_desc,
+ _glog_grfparam_desc,
+ _glog_grfmove_desc,
+ _glog_grfbug_desc,
+};
+
+assert_compile(lengthof(_glog_desc) == GLCT_END);
+
+static void Load_GLOG()
+{
+ assert(_gamelog_action == NULL);
+ assert(_gamelog_actions == 0);
+
+ GamelogActionType at;
+ while ((at = (GamelogActionType)SlReadByte()) != GLAT_NONE) {
+ _gamelog_action = ReallocT(_gamelog_action, _gamelog_actions + 1);
+ LoggedAction *la = &_gamelog_action[_gamelog_actions++];
+
+ la->at = at;
+
+ SlObject(la, _glog_action_desc); // has to be saved after 'DATE'!
+ la->change = NULL;
+ la->changes = 0;
+
+ GamelogChangeType ct;
+ while ((ct = (GamelogChangeType)SlReadByte()) != GLCT_NONE) {
+ la->change = ReallocT(la->change, la->changes + 1);
+
+ LoggedChange *lc = &la->change[la->changes++];
+ /* for SLE_STR, pointer has to be valid! so make it NULL */
+ memset(lc, 0, sizeof(*lc));
+ lc->ct = ct;
+
+ assert((uint)ct < GLCT_END);
+
+ SlObject(lc, _glog_desc[ct]);
+ }
+ }
+}
+
+static void Save_GLOG()
+{
+ const LoggedAction *laend = &_gamelog_action[_gamelog_actions];
+ size_t length = 0;
+
+ for (const LoggedAction *la = _gamelog_action; la != laend; la++) {
+ const LoggedChange *lcend = &la->change[la->changes];
+ for (LoggedChange *lc = la->change; lc != lcend; lc++) {
+ assert((uint)lc->ct < lengthof(_glog_desc));
+ length += SlCalcObjLength(lc, _glog_desc[lc->ct]) + 1;
+ }
+ length += 4;
+ }
+ length++;
+
+ SlSetLength(length);
+
+ for (LoggedAction *la = _gamelog_action; la != laend; la++) {
+ SlWriteByte(la->at);
+ SlObject(la, _glog_action_desc);
+
+ const LoggedChange *lcend = &la->change[la->changes];
+ for (LoggedChange *lc = la->change; lc != lcend; lc++) {
+ SlWriteByte(lc->ct);
+ assert((uint)lc->ct < GLCT_END);
+ SlObject(lc, _glog_desc[lc->ct]);
+ }
+ SlWriteByte(GLCT_NONE);
+ }
+ SlWriteByte(GLAT_NONE);
+}
+
+
+extern const ChunkHandler _gamelog_chunk_handlers[] = {
+ { 'GLOG', Save_GLOG, Load_GLOG, CH_RIFF | CH_LAST }
+};
diff --git a/src/saveload/group_sl.cpp b/src/saveload/group_sl.cpp
new file mode 100644
index 000000000..66e3585b4
--- /dev/null
+++ b/src/saveload/group_sl.cpp
@@ -0,0 +1,43 @@
+/* $Id$ */
+
+/** @file group_sl.cpp Code handling saving and loading of economy data */
+
+#include "../stdafx.h"
+#include "../group.h"
+
+#include "saveload.h"
+
+static const SaveLoad _group_desc[] = {
+ SLE_CONDVAR(Group, name, SLE_NAME, 0, 83),
+ SLE_CONDSTR(Group, name, SLE_STR, 0, 84, SL_MAX_VERSION),
+ SLE_VAR(Group, num_vehicle, SLE_UINT16),
+ SLE_VAR(Group, owner, SLE_UINT8),
+ SLE_VAR(Group, vehicle_type, SLE_UINT8),
+ SLE_VAR(Group, replace_protection, SLE_BOOL),
+ SLE_END()
+};
+
+static void Save_GRPS(void)
+{
+ Group *g;
+
+ FOR_ALL_GROUPS(g) {
+ SlSetArrayIndex(g->index);
+ SlObject(g, _group_desc);
+ }
+}
+
+
+static void Load_GRPS(void)
+{
+ int index;
+
+ while ((index = SlIterateArray()) != -1) {
+ Group *g = new (index) Group();
+ SlObject(g, _group_desc);
+ }
+}
+
+extern const ChunkHandler _group_chunk_handlers[] = {
+ { 'GRPS', Save_GRPS, Load_GRPS, CH_ARRAY | CH_LAST},
+};
diff --git a/src/saveload/industry_sl.cpp b/src/saveload/industry_sl.cpp
new file mode 100644
index 000000000..2b8aea788
--- /dev/null
+++ b/src/saveload/industry_sl.cpp
@@ -0,0 +1,155 @@
+/* $Id$ */
+
+/** @file industry_sl.cpp Code handling saving and loading of industries */
+
+#include "../stdafx.h"
+#include "../tile_type.h"
+#include "../strings_type.h"
+#include "../company_type.h"
+#include "../industry.h"
+#include "../newgrf_commons.h"
+
+#include "saveload.h"
+
+static const SaveLoad _industry_desc[] = {
+ SLE_CONDVAR(Industry, xy, SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
+ SLE_CONDVAR(Industry, xy, SLE_UINT32, 6, SL_MAX_VERSION),
+ SLE_VAR(Industry, width, SLE_UINT8),
+ SLE_VAR(Industry, height, SLE_UINT8),
+ SLE_REF(Industry, town, REF_TOWN),
+ SLE_CONDNULL( 2, 0, 60), ///< used to be industry's produced_cargo
+ SLE_CONDARR(Industry, produced_cargo, SLE_UINT8, 2, 78, SL_MAX_VERSION),
+ SLE_CONDARR(Industry, incoming_cargo_waiting, SLE_UINT16, 3, 70, SL_MAX_VERSION),
+ SLE_ARR(Industry, produced_cargo_waiting, SLE_UINT16, 2),
+ SLE_ARR(Industry, production_rate, SLE_UINT8, 2),
+ SLE_CONDNULL( 3, 0, 60), ///< used to be industry's accepts_cargo
+ SLE_CONDARR(Industry, accepts_cargo, SLE_UINT8, 3, 78, SL_MAX_VERSION),
+ SLE_VAR(Industry, prod_level, SLE_UINT8),
+ SLE_ARR(Industry, this_month_production, SLE_UINT16, 2),
+ SLE_ARR(Industry, this_month_transported, SLE_UINT16, 2),
+ SLE_ARR(Industry, last_month_pct_transported, SLE_UINT8, 2),
+ SLE_ARR(Industry, last_month_production, SLE_UINT16, 2),
+ SLE_ARR(Industry, last_month_transported, SLE_UINT16, 2),
+
+ SLE_VAR(Industry, counter, SLE_UINT16),
+
+ SLE_VAR(Industry, type, SLE_UINT8),
+ SLE_VAR(Industry, owner, SLE_UINT8),
+ SLE_VAR(Industry, random_color, SLE_UINT8),
+ SLE_CONDVAR(Industry, last_prod_year, SLE_FILE_U8 | SLE_VAR_I32, 0, 30),
+ SLE_CONDVAR(Industry, last_prod_year, SLE_INT32, 31, SL_MAX_VERSION),
+ SLE_VAR(Industry, was_cargo_delivered, SLE_UINT8),
+
+ SLE_CONDVAR(Industry, founder, SLE_UINT8, 70, SL_MAX_VERSION),
+ SLE_CONDVAR(Industry, construction_date, SLE_INT32, 70, SL_MAX_VERSION),
+ SLE_CONDVAR(Industry, construction_type, SLE_UINT8, 70, SL_MAX_VERSION),
+ SLE_CONDVAR(Industry, last_cargo_accepted_at, SLE_INT32, 70, SL_MAX_VERSION),
+ SLE_CONDVAR(Industry, selected_layout, SLE_UINT8, 73, SL_MAX_VERSION),
+
+ SLE_CONDARRX(cpp_offsetof(Industry, psa) + cpp_offsetof(Industry::PersistentStorage, storage), SLE_UINT32, 16, 76, SL_MAX_VERSION),
+
+ SLE_CONDVAR(Industry, random_triggers, SLE_UINT8, 82, SL_MAX_VERSION),
+ SLE_CONDVAR(Industry, random, SLE_UINT16, 82, SL_MAX_VERSION),
+
+ /* reserve extra space in savegame here. (currently 32 bytes) */
+ SLE_CONDNULL(32, 2, SL_MAX_VERSION),
+
+ SLE_END()
+};
+
+static void Save_INDY()
+{
+ Industry *ind;
+
+ /* Write the industries */
+ FOR_ALL_INDUSTRIES(ind) {
+ SlSetArrayIndex(ind->index);
+ SlObject(ind, _industry_desc);
+ }
+}
+
+/* Save and load the mapping between the industry/tile id on the map, and the grf file
+ * it came from. */
+static const SaveLoad _industries_id_mapping_desc[] = {
+ SLE_VAR(EntityIDMapping, grfid, SLE_UINT32),
+ SLE_VAR(EntityIDMapping, entity_id, SLE_UINT8),
+ SLE_VAR(EntityIDMapping, substitute_id, SLE_UINT8),
+ SLE_END()
+};
+
+static void Save_IIDS()
+{
+ uint i;
+ uint j = _industry_mngr.GetMaxMapping();
+
+ for (i = 0; i < j; i++) {
+ SlSetArrayIndex(i);
+ SlObject(&_industry_mngr.mapping_ID[i], _industries_id_mapping_desc);
+ }
+}
+
+static void Save_TIDS()
+{
+ uint i;
+ uint j = _industile_mngr.GetMaxMapping();
+
+ for (i = 0; i < j; i++) {
+ SlSetArrayIndex(i);
+ SlObject(&_industile_mngr.mapping_ID[i], _industries_id_mapping_desc);
+ }
+}
+
+static void Load_INDY()
+{
+ int index;
+
+ ResetIndustryCounts();
+
+ while ((index = SlIterateArray()) != -1) {
+ Industry *i = new (index) Industry();
+ SlObject(i, _industry_desc);
+ IncIndustryTypeCount(i->type);
+ }
+}
+
+static void Load_IIDS()
+{
+ int index;
+ uint max_id;
+
+ /* clear the current mapping stored.
+ * This will create the manager if ever it is not yet done */
+ _industry_mngr.ResetMapping();
+
+ /* get boundary for the temporary map loader NUM_INDUSTRYTYPES? */
+ max_id = _industry_mngr.GetMaxMapping();
+
+ while ((index = SlIterateArray()) != -1) {
+ if ((uint)index >= max_id) break;
+ SlObject(&_industry_mngr.mapping_ID[index], _industries_id_mapping_desc);
+ }
+}
+
+static void Load_TIDS()
+{
+ int index;
+ uint max_id;
+
+ /* clear the current mapping stored.
+ * This will create the manager if ever it is not yet done */
+ _industile_mngr.ResetMapping();
+
+ /* get boundary for the temporary map loader NUM_INDUSTILES? */
+ max_id = _industile_mngr.GetMaxMapping();
+
+ while ((index = SlIterateArray()) != -1) {
+ if ((uint)index >= max_id) break;
+ SlObject(&_industile_mngr.mapping_ID[index], _industries_id_mapping_desc);
+ }
+}
+
+extern const ChunkHandler _industry_chunk_handlers[] = {
+ { 'INDY', Save_INDY, Load_INDY, CH_ARRAY},
+ { 'IIDS', Save_IIDS, Load_IIDS, CH_ARRAY},
+ { 'TIDS', Save_TIDS, Load_TIDS, CH_ARRAY | CH_LAST},
+};
diff --git a/src/saveload/map_sl.cpp b/src/saveload/map_sl.cpp
new file mode 100644
index 000000000..6025f0fca
--- /dev/null
+++ b/src/saveload/map_sl.cpp
@@ -0,0 +1,249 @@
+/* $Id$ */
+
+/** @file map_sl.cpp Code handling saving and loading of map */
+
+#include "../stdafx.h"
+#include "../tile_type.h"
+#include "../map_func.h"
+#include "../core/alloc_type.hpp"
+#include "../core/bitmath_func.hpp"
+
+#include "saveload.h"
+
+static uint32 _map_dim_x;
+static uint32 _map_dim_y;
+
+static const SaveLoadGlobVarList _map_dimensions[] = {
+ SLEG_CONDVAR(_map_dim_x, SLE_UINT32, 6, SL_MAX_VERSION),
+ SLEG_CONDVAR(_map_dim_y, SLE_UINT32, 6, SL_MAX_VERSION),
+ SLEG_END()
+};
+
+static void Save_MAPS()
+{
+ _map_dim_x = MapSizeX();
+ _map_dim_y = MapSizeY();
+ SlGlobList(_map_dimensions);
+}
+
+static void Load_MAPS()
+{
+ SlGlobList(_map_dimensions);
+ AllocateMap(_map_dim_x, _map_dim_y);
+}
+
+enum {
+ MAP_SL_BUF_SIZE = 4096
+};
+
+static void Load_MAPT()
+{
+ SmallStackSafeStackAlloc<byte, MAP_SL_BUF_SIZE> buf;
+ TileIndex size = MapSize();
+
+ for (TileIndex i = 0; i != size;) {
+ SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8);
+ for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].type_height = buf[j];
+ }
+}
+
+static void Save_MAPT()
+{
+ SmallStackSafeStackAlloc<byte, MAP_SL_BUF_SIZE> buf;
+ TileIndex size = MapSize();
+
+ SlSetLength(size);
+ for (TileIndex i = 0; i != size;) {
+ for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _m[i++].type_height;
+ SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8);
+ }
+}
+
+static void Load_MAP1()
+{
+ SmallStackSafeStackAlloc<byte, MAP_SL_BUF_SIZE> buf;
+ TileIndex size = MapSize();
+
+ for (TileIndex i = 0; i != size;) {
+ SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8);
+ for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m1 = buf[j];
+ }
+}
+
+static void Save_MAP1()
+{
+ SmallStackSafeStackAlloc<byte, MAP_SL_BUF_SIZE> buf;
+ TileIndex size = MapSize();
+
+ SlSetLength(size);
+ for (TileIndex i = 0; i != size;) {
+ for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _m[i++].m1;
+ SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8);
+ }
+}
+
+static void Load_MAP2()
+{
+ SmallStackSafeStackAlloc<uint16, MAP_SL_BUF_SIZE> buf;
+ TileIndex size = MapSize();
+
+ for (TileIndex i = 0; i != size;) {
+ SlArray(buf, MAP_SL_BUF_SIZE,
+ /* In those versions the m2 was 8 bits */
+ CheckSavegameVersion(5) ? SLE_FILE_U8 | SLE_VAR_U16 : SLE_UINT16
+ );
+ for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m2 = buf[j];
+ }
+}
+
+static void Save_MAP2()
+{
+ SmallStackSafeStackAlloc<uint16, MAP_SL_BUF_SIZE> buf;
+ TileIndex size = MapSize();
+
+ SlSetLength(size * sizeof(uint16));
+ for (TileIndex i = 0; i != size;) {
+ for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _m[i++].m2;
+ SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT16);
+ }
+}
+
+static void Load_MAP3()
+{
+ SmallStackSafeStackAlloc<byte, MAP_SL_BUF_SIZE> buf;
+ TileIndex size = MapSize();
+
+ for (TileIndex i = 0; i != size;) {
+ SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8);
+ for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m3 = buf[j];
+ }
+}
+
+static void Save_MAP3()
+{
+ SmallStackSafeStackAlloc<byte, MAP_SL_BUF_SIZE> buf;
+ TileIndex size = MapSize();
+
+ SlSetLength(size);
+ for (TileIndex i = 0; i != size;) {
+ for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _m[i++].m3;
+ SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8);
+ }
+}
+
+static void Load_MAP4()
+{
+ SmallStackSafeStackAlloc<byte, MAP_SL_BUF_SIZE> buf;
+ TileIndex size = MapSize();
+
+ for (TileIndex i = 0; i != size;) {
+ SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8);
+ for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m4 = buf[j];
+ }
+}
+
+static void Save_MAP4()
+{
+ SmallStackSafeStackAlloc<byte, MAP_SL_BUF_SIZE> buf;
+ TileIndex size = MapSize();
+
+ SlSetLength(size);
+ for (TileIndex i = 0; i != size;) {
+ for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _m[i++].m4;
+ SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8);
+ }
+}
+
+static void Load_MAP5()
+{
+ SmallStackSafeStackAlloc<byte, MAP_SL_BUF_SIZE> buf;
+ TileIndex size = MapSize();
+
+ for (TileIndex i = 0; i != size;) {
+ SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8);
+ for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m5 = buf[j];
+ }
+}
+
+static void Save_MAP5()
+{
+ SmallStackSafeStackAlloc<byte, MAP_SL_BUF_SIZE> buf;
+ TileIndex size = MapSize();
+
+ SlSetLength(size);
+ for (TileIndex i = 0; i != size;) {
+ for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _m[i++].m5;
+ SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8);
+ }
+}
+
+static void Load_MAP6()
+{
+ SmallStackSafeStackAlloc<byte, MAP_SL_BUF_SIZE> buf;
+ TileIndex size = MapSize();
+
+ if (CheckSavegameVersion(42)) {
+ for (TileIndex i = 0; i != size;) {
+ /* 1024, otherwise we overflow on 64x64 maps! */
+ SlArray(buf, 1024, SLE_UINT8);
+ for (uint j = 0; j != 1024; j++) {
+ _m[i++].m6 = GB(buf[j], 0, 2);
+ _m[i++].m6 = GB(buf[j], 2, 2);
+ _m[i++].m6 = GB(buf[j], 4, 2);
+ _m[i++].m6 = GB(buf[j], 6, 2);
+ }
+ }
+ } else {
+ for (TileIndex i = 0; i != size;) {
+ SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8);
+ for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m6 = buf[j];
+ }
+ }
+}
+
+static void Save_MAP6()
+{
+ SmallStackSafeStackAlloc<byte, MAP_SL_BUF_SIZE> buf;
+ TileIndex size = MapSize();
+
+ SlSetLength(size);
+ for (TileIndex i = 0; i != size;) {
+ for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _m[i++].m6;
+ SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8);
+ }
+}
+
+static void Load_MAP7()
+{
+ SmallStackSafeStackAlloc<byte, MAP_SL_BUF_SIZE> buf;
+ TileIndex size = MapSize();
+
+ for (TileIndex i = 0; i != size;) {
+ SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8);
+ for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _me[i++].m7 = buf[j];
+ }
+}
+
+static void Save_MAP7()
+{
+ SmallStackSafeStackAlloc<byte, MAP_SL_BUF_SIZE> buf;
+ TileIndex size = MapSize();
+
+ SlSetLength(size);
+ for (TileIndex i = 0; i != size;) {
+ for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _me[i++].m7;
+ SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8);
+ }
+}
+
+extern const ChunkHandler _map_chunk_handlers[] = {
+ { 'MAPS', Save_MAPS, Load_MAPS, CH_RIFF },
+ { 'MAPT', Save_MAPT, Load_MAPT, CH_RIFF },
+ { 'MAPO', Save_MAP1, Load_MAP1, CH_RIFF },
+ { 'MAP2', Save_MAP2, Load_MAP2, CH_RIFF },
+ { 'M3LO', Save_MAP3, Load_MAP3, CH_RIFF },
+ { 'M3HI', Save_MAP4, Load_MAP4, CH_RIFF },
+ { 'MAP5', Save_MAP5, Load_MAP5, CH_RIFF },
+ { 'MAPE', Save_MAP6, Load_MAP6, CH_RIFF },
+ { 'MAP7', Save_MAP7, Load_MAP7, CH_RIFF },
+};
diff --git a/src/saveload/misc_sl.cpp b/src/saveload/misc_sl.cpp
new file mode 100644
index 000000000..4b8e8b2c7
--- /dev/null
+++ b/src/saveload/misc_sl.cpp
@@ -0,0 +1,105 @@
+/* $Id$ */
+
+/** @file misc_sl.cpp Saving and loading of things that didn't fit anywhere else */
+
+#include "../stdafx.h"
+#include "../date_func.h"
+#include "../variables.h"
+#include "../core/random_func.hpp"
+#include "../openttd.h"
+#include "../tile_type.h"
+#include "../zoom_func.h"
+#include "../vehicle_func.h"
+#include "../window_gui.h"
+#include "../window_func.h"
+#include "../viewport_func.h"
+#include "../gfx_func.h"
+
+#include "saveload.h"
+
+extern TileIndex _cur_tileloop_tile;
+
+/* Keep track of current game position */
+int _saved_scrollpos_x;
+int _saved_scrollpos_y;
+
+void SaveViewportBeforeSaveGame()
+{
+ const Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
+
+ if (w != NULL) {
+ _saved_scrollpos_x = w->viewport->scrollpos_x;
+ _saved_scrollpos_y = w->viewport->scrollpos_y;
+ _saved_scrollpos_zoom = w->viewport->zoom;
+ }
+}
+
+void ResetViewportAfterLoadGame()
+{
+ Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
+
+ w->viewport->scrollpos_x = _saved_scrollpos_x;
+ w->viewport->scrollpos_y = _saved_scrollpos_y;
+ w->viewport->dest_scrollpos_x = _saved_scrollpos_x;
+ w->viewport->dest_scrollpos_y = _saved_scrollpos_y;
+
+ ViewPort *vp = w->viewport;
+ vp->zoom = min(_saved_scrollpos_zoom, ZOOM_LVL_MAX);
+ vp->virtual_width = ScaleByZoom(vp->width, vp->zoom);
+ vp->virtual_height = ScaleByZoom(vp->height, vp->zoom);
+
+ DoZoomInOutWindow(ZOOM_NONE, w); // update button status
+ MarkWholeScreenDirty();
+}
+
+
+static const SaveLoadGlobVarList _date_desc[] = {
+ SLEG_CONDVAR(_date, SLE_FILE_U16 | SLE_VAR_I32, 0, 30),
+ SLEG_CONDVAR(_date, SLE_INT32, 31, SL_MAX_VERSION),
+ SLEG_VAR(_date_fract, SLE_UINT16),
+ SLEG_VAR(_tick_counter, SLE_UINT16),
+ SLEG_VAR(_vehicle_id_ctr_day, SLE_UINT16),
+ SLEG_VAR(_age_cargo_skip_counter, SLE_UINT8),
+ SLE_CONDNULL(1, 0, 45),
+ SLEG_CONDVAR(_cur_tileloop_tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
+ SLEG_CONDVAR(_cur_tileloop_tile, SLE_UINT32, 6, SL_MAX_VERSION),
+ SLEG_VAR(_disaster_delay, SLE_UINT16),
+ SLEG_VAR(_station_tick_ctr, SLE_UINT16),
+ SLEG_VAR(_random.state[0], SLE_UINT32),
+ SLEG_VAR(_random.state[1], SLE_UINT32),
+ SLEG_CONDVAR(_cur_town_ctr, SLE_FILE_U8 | SLE_VAR_U32, 0, 9),
+ SLEG_CONDVAR(_cur_town_ctr, SLE_UINT32, 10, SL_MAX_VERSION),
+ SLEG_VAR(_cur_company_tick_index, SLE_FILE_U8 | SLE_VAR_U32),
+ SLEG_VAR(_next_competitor_start, SLE_FILE_U16 | SLE_VAR_U32),
+ SLEG_VAR(_trees_tick_ctr, SLE_UINT8),
+ SLEG_CONDVAR(_pause_game, SLE_UINT8, 4, SL_MAX_VERSION),
+ SLEG_CONDVAR(_cur_town_iter, SLE_UINT32, 11, SL_MAX_VERSION),
+ SLEG_END()
+};
+
+/* Save load date related variables as well as persistent tick counters
+ * XXX: currently some unrelated stuff is just put here */
+static void SaveLoad_DATE()
+{
+ SlGlobList(_date_desc);
+}
+
+
+static const SaveLoadGlobVarList _view_desc[] = {
+ SLEG_CONDVAR(_saved_scrollpos_x, SLE_FILE_I16 | SLE_VAR_I32, 0, 5),
+ SLEG_CONDVAR(_saved_scrollpos_x, SLE_INT32, 6, SL_MAX_VERSION),
+ SLEG_CONDVAR(_saved_scrollpos_y, SLE_FILE_I16 | SLE_VAR_I32, 0, 5),
+ SLEG_CONDVAR(_saved_scrollpos_y, SLE_INT32, 6, SL_MAX_VERSION),
+ SLEG_VAR(_saved_scrollpos_zoom, SLE_UINT8),
+ SLEG_END()
+};
+
+static void SaveLoad_VIEW()
+{
+ SlGlobList(_view_desc);
+}
+
+extern const ChunkHandler _misc_chunk_handlers[] = {
+ { 'DATE', SaveLoad_DATE, SaveLoad_DATE, CH_RIFF},
+ { 'VIEW', SaveLoad_VIEW, SaveLoad_VIEW, CH_RIFF | CH_LAST},
+};
diff --git a/src/saveload/newgrf_sl.cpp b/src/saveload/newgrf_sl.cpp
new file mode 100644
index 000000000..5269f235d
--- /dev/null
+++ b/src/saveload/newgrf_sl.cpp
@@ -0,0 +1,52 @@
+/* $Id$ */
+
+/** @file newgrf_sl.cpp Code handling saving and loading of newgrf config */
+
+#include "../stdafx.h"
+#include "../newgrf_config.h"
+#include "../core/bitmath_func.hpp"
+#include "../core/alloc_func.hpp"
+#include "../gfx_func.h"
+
+#include "saveload.h"
+
+static const SaveLoad _grfconfig_desc[] = {
+ SLE_STR(GRFConfig, filename, SLE_STR, 0x40),
+ SLE_VAR(GRFConfig, grfid, SLE_UINT32),
+ SLE_ARR(GRFConfig, md5sum, SLE_UINT8, 16),
+ SLE_ARR(GRFConfig, param, SLE_UINT32, 0x80),
+ SLE_VAR(GRFConfig, num_params, SLE_UINT8),
+ SLE_CONDVAR(GRFConfig, windows_paletted, SLE_BOOL, 101, SL_MAX_VERSION),
+ SLE_END()
+};
+
+
+static void Save_NGRF()
+{
+ int index = 0;
+
+ for (GRFConfig *c = _grfconfig; c != NULL; c = c->next) {
+ if (HasBit(c->flags, GCF_STATIC)) continue;
+ SlSetArrayIndex(index++);
+ SlObject(c, _grfconfig_desc);
+ }
+}
+
+
+static void Load_NGRF()
+{
+ ClearGRFConfigList(&_grfconfig);
+ while (SlIterateArray() != -1) {
+ GRFConfig *c = CallocT<GRFConfig>(1);
+ SlObject(c, _grfconfig_desc);
+ if (CheckSavegameVersion(101)) c->windows_paletted = (_use_palette == PAL_WINDOWS);
+ AppendToGRFConfigList(&_grfconfig, c);
+ }
+
+ /* Append static NewGRF configuration */
+ AppendStaticGRFConfigs(&_grfconfig);
+}
+
+extern const ChunkHandler _newgrf_chunk_handlers[] = {
+ { 'NGRF', Save_NGRF, Load_NGRF, CH_ARRAY | CH_LAST }
+};
diff --git a/src/saveload/oldloader.cpp b/src/saveload/oldloader.cpp
new file mode 100644
index 000000000..532d58f8c
--- /dev/null
+++ b/src/saveload/oldloader.cpp
@@ -0,0 +1,1736 @@
+/* $Id$ */
+
+/** @file oldloader.cpp Loading of old TTD(patch) savegames. */
+
+#include "../stdafx.h"
+#include "../openttd.h"
+#include "../station_map.h"
+#include "../town.h"
+#include "../industry.h"
+#include "../company_func.h"
+#include "../company_base.h"
+#include "../aircraft.h"
+#include "../roadveh.h"
+#include "../ship.h"
+#include "../train.h"
+#include "../signs_base.h"
+#include "../debug.h"
+#include "../depot_base.h"
+#include "../newgrf_config.h"
+#include "../ai/ai.h"
+#include "../ai/default/default.h"
+#include "../zoom_func.h"
+#include "../functions.h"
+#include "../date_func.h"
+#include "../vehicle_func.h"
+#include "../variables.h"
+#include "../strings_func.h"
+#include "../effectvehicle_base.h"
+
+#include "table/strings.h"
+
+#include "saveload.h"
+#include "saveload_internal.h"
+
+enum {
+ HEADER_SIZE = 49,
+ BUFFER_SIZE = 4096,
+
+ OLD_MAP_SIZE = 256 * 256
+};
+
+struct LoadgameState {
+ FILE *file;
+
+ uint chunk_size;
+
+ bool decoding;
+ byte decode_char;
+
+ uint buffer_count;
+ uint buffer_cur;
+ byte buffer[BUFFER_SIZE];
+
+ uint total_read;
+ bool failed;
+};
+
+/* OldChunk-Type */
+enum OldChunkType {
+ OC_SIMPLE = 0,
+ OC_NULL = 1,
+ OC_CHUNK = 2,
+ OC_ASSERT = 3,
+ /* 8 bits allocated (256 max) */
+
+ OC_VAR_I8 = 1 << 8,
+ OC_VAR_U8 = 2 << 8,
+ OC_VAR_I16 = 3 << 8,
+ OC_VAR_U16 = 4 << 8,
+ OC_VAR_I32 = 5 << 8,
+ OC_VAR_U32 = 6 << 8,
+ OC_VAR_I64 = 7 << 8,
+ /* 8 bits allocated (256 max) */
+
+ OC_FILE_I8 = 1 << 16,
+ OC_FILE_U8 = 2 << 16,
+ OC_FILE_I16 = 3 << 16,
+ OC_FILE_U16 = 4 << 16,
+ OC_FILE_I32 = 5 << 16,
+ OC_FILE_U32 = 6 << 16,
+ /* 8 bits allocated (256 max) */
+
+ OC_INT8 = OC_VAR_I8 | OC_FILE_I8,
+ OC_UINT8 = OC_VAR_U8 | OC_FILE_U8,
+ OC_INT16 = OC_VAR_I16 | OC_FILE_I16,
+ OC_UINT16 = OC_VAR_U16 | OC_FILE_U16,
+ OC_INT32 = OC_VAR_I32 | OC_FILE_I32,
+ OC_UINT32 = OC_VAR_U32 | OC_FILE_U32,
+
+ OC_TILE = OC_VAR_U32 | OC_FILE_U16,
+
+ /**
+ * Dereference the pointer once before writing to it,
+ * so we do not have to use big static arrays.
+ */
+ OC_DEREFERENCE_POINTER = 1 << 31,
+
+ OC_END = 0 ///< End of the whole chunk, all 32 bits set to zero
+};
+
+DECLARE_ENUM_AS_BIT_SET(OldChunkType);
+
+typedef bool OldChunkProc(LoadgameState *ls, int num);
+
+struct OldChunks {
+ OldChunkType type; ///< Type of field
+ uint32 amount; ///< Amount of fields
+
+ void *ptr; ///< Pointer where to save the data (may only be set if offset is 0)
+ uint offset; ///< Offset from basepointer (may only be set if ptr is NULL)
+ OldChunkProc *proc; ///< Pointer to function that is called with OC_CHUNK
+};
+
+/* If it fails, check lines above.. */
+assert_compile(sizeof(TileIndex) == 4);
+
+extern SavegameType _savegame_type;
+extern uint32 _ttdp_version;
+
+static uint32 _bump_assert_value;
+static bool _read_ttdpatch_flags;
+
+static OldChunkType GetOldChunkType(OldChunkType type) {return (OldChunkType)GB(type, 0, 8);}
+static OldChunkType GetOldChunkVarType(OldChunkType type) {return (OldChunkType)(GB(type, 8, 8) << 8);}
+static OldChunkType GetOldChunkFileType(OldChunkType type) {return (OldChunkType)(GB(type, 16, 8) << 16);}
+
+static inline byte CalcOldVarLen(OldChunkType type)
+{
+ static const byte type_mem_size[] = {0, 1, 1, 2, 2, 4, 4, 8};
+ byte length = GB(type, 8, 8);
+ assert(length != 0 && length < lengthof(type_mem_size));
+ return type_mem_size[length];
+}
+
+/**
+ *
+ * Reads a byte from a file (do not call yourself, use ReadByte())
+ *
+ */
+static byte ReadByteFromFile(LoadgameState *ls)
+{
+ /* To avoid slow reads, we read BUFFER_SIZE of bytes per time
+ and just return a byte per time */
+ if (ls->buffer_cur >= ls->buffer_count) {
+ /* Read some new bytes from the file */
+ int count = (int)fread(ls->buffer, 1, BUFFER_SIZE, ls->file);
+
+ /* We tried to read, but there is nothing in the file anymore.. */
+ if (count == 0) {
+ DEBUG(oldloader, 0, "Read past end of file, loading failed");
+ ls->failed = true;
+ }
+
+ ls->buffer_count = count;
+ ls->buffer_cur = 0;
+ }
+
+ return ls->buffer[ls->buffer_cur++];
+}
+
+/**
+ *
+ * Reads a byte from the buffer and decompress if needed
+ *
+ */
+static byte ReadByte(LoadgameState *ls)
+{
+ /* Old savegames have a nice compression algorithm (RLE)
+ which means that we have a chunk, which starts with a length
+ byte. If that byte is negative, we have to repeat the next byte
+ that many times ( + 1). Else, we need to read that amount of bytes.
+ Works pretty good if you have many zero's behind eachother */
+
+ if (ls->chunk_size == 0) {
+ /* Read new chunk */
+ int8 new_byte = ReadByteFromFile(ls);
+
+ if (new_byte < 0) {
+ /* Repeat next char for new_byte times */
+ ls->decoding = true;
+ ls->decode_char = ReadByteFromFile(ls);
+ ls->chunk_size = -new_byte + 1;
+ } else {
+ ls->decoding = false;
+ ls->chunk_size = new_byte + 1;
+ }
+ }
+
+ ls->total_read++;
+ ls->chunk_size--;
+
+ return ls->decoding ? ls->decode_char : ReadByteFromFile(ls);
+}
+
+static inline uint16 ReadUint16(LoadgameState *ls)
+{
+ byte x = ReadByte(ls);
+ return x | ReadByte(ls) << 8;
+}
+
+static inline uint32 ReadUint32(LoadgameState *ls)
+{
+ uint16 x = ReadUint16(ls);
+ return x | ReadUint16(ls) << 16;
+}
+
+/**
+ *
+ * Loads a chunk from the old savegame
+ *
+ */
+static bool LoadChunk(LoadgameState *ls, void *base, const OldChunks *chunks)
+{
+ const OldChunks *chunk = chunks;
+ byte *base_ptr = (byte*)base;
+
+ while (chunk->type != OC_END) {
+ byte *ptr = (byte*)chunk->ptr;
+ if ((chunk->type & OC_DEREFERENCE_POINTER) != 0) ptr = *(byte**)ptr;
+
+ for (uint i = 0; i < chunk->amount; i++) {
+ if (ls->failed) return false;
+
+ /* Handle simple types */
+ if (GetOldChunkType(chunk->type) != 0) {
+ switch (GetOldChunkType(chunk->type)) {
+ /* Just read the byte and forget about it */
+ case OC_NULL: ReadByte(ls); break;
+
+ case OC_CHUNK:
+ /* Call function, with 'i' as parameter to tell which item we
+ * are going to read */
+ if (!chunk->proc(ls, i)) return false;
+ break;
+
+ case OC_ASSERT:
+ DEBUG(oldloader, 4, "Assert point: 0x%X / 0x%X", ls->total_read, chunk->offset + _bump_assert_value);
+ if (ls->total_read != chunk->offset + _bump_assert_value) ls->failed = true;
+ default: break;
+ }
+ } else {
+ uint64 res = 0;
+
+ /* Reading from the file: bits 16 to 23 have the FILE type */
+ switch (GetOldChunkFileType(chunk->type)) {
+ case OC_FILE_I8: res = (int8)ReadByte(ls); break;
+ case OC_FILE_U8: res = ReadByte(ls); break;
+ case OC_FILE_I16: res = (int16)ReadUint16(ls); break;
+ case OC_FILE_U16: res = ReadUint16(ls); break;
+ case OC_FILE_I32: res = (int32)ReadUint32(ls); break;
+ case OC_FILE_U32: res = ReadUint32(ls); break;
+ default: NOT_REACHED();
+ }
+
+ /* Sanity check */
+ assert(base_ptr != NULL || chunk->ptr != NULL);
+
+ /* Writing to the var: bits 8 to 15 have the VAR type */
+ if (chunk->ptr == NULL) ptr = base_ptr + chunk->offset;
+
+ /* Write the data */
+ switch (GetOldChunkVarType(chunk->type)) {
+ case OC_VAR_I8: *(int8 *)ptr = GB(res, 0, 8); break;
+ case OC_VAR_U8: *(uint8 *)ptr = GB(res, 0, 8); break;
+ case OC_VAR_I16:*(int16 *)ptr = GB(res, 0, 16); break;
+ case OC_VAR_U16:*(uint16*)ptr = GB(res, 0, 16); break;
+ case OC_VAR_I32:*(int32 *)ptr = res; break;
+ case OC_VAR_U32:*(uint32*)ptr = res; break;
+ case OC_VAR_I64:*(int64 *)ptr = res; break;
+ default: NOT_REACHED();
+ }
+
+ /* Increase pointer base for arrays when looping */
+ if (chunk->amount > 1 && chunk->ptr != NULL) ptr += CalcOldVarLen(chunk->type);
+ }
+ }
+
+ chunk++;
+ }
+
+ return true;
+}
+
+/**
+ *
+ * Initialize some data before reading
+ *
+ */
+static void InitLoading(LoadgameState *ls)
+{
+ ls->chunk_size = 0;
+ ls->total_read = 0;
+ ls->failed = false;
+
+ ls->decoding = false;
+ ls->decode_char = 0;
+
+ ls->buffer_cur = 0;
+ ls->buffer_count = 0;
+ memset(ls->buffer, 0, BUFFER_SIZE);
+
+ _bump_assert_value = 0;
+
+ _savegame_type = SGT_TTD;
+ _ttdp_version = 0;
+
+ _read_ttdpatch_flags = false;
+}
+
+
+/*
+ * Begin -- Stuff to fix the savegames to be OpenTTD compatible
+ */
+
+extern uint32 GetOldTownName(uint32 townnameparts, byte old_town_name_type);
+
+static void FixOldTowns()
+{
+ Town *town;
+
+ /* Convert town-names if needed */
+ FOR_ALL_TOWNS(town) {
+ if (IsInsideMM(town->townnametype, 0x20C1, 0x20C3)) {
+ town->townnametype = SPECSTR_TOWNNAME_ENGLISH + _settings_game.game_creation.town_name;
+ town->townnameparts = GetOldTownName(town->townnameparts, _settings_game.game_creation.town_name);
+ }
+ }
+}
+
+static StringID *_old_vehicle_names = NULL;
+
+static void FixOldVehicles()
+{
+ Vehicle* v;
+
+ FOR_ALL_VEHICLES(v) {
+ v->name = CopyFromOldName(_old_vehicle_names[v->index]);
+
+ /* We haven't used this bit for stations for ages */
+ if (v->type == VEH_ROAD &&
+ v->u.road.state != RVSB_IN_DEPOT &&
+ v->u.road.state != RVSB_WORMHOLE) {
+ ClrBit(v->u.road.state, RVS_IS_STOPPING);
+ }
+
+ /* The subtype should be 0, but it sometimes isn't :( */
+ if (v->type == VEH_ROAD) v->subtype = 0;
+
+ /* Sometimes primary vehicles would have a nothing (invalid) order
+ * or vehicles that could not have an order would still have a
+ * (loading) order which causes assertions and the like later on.
+ */
+ if (!IsCompanyBuildableVehicleType(v) ||
+ (v->IsPrimaryVehicle() && v->current_order.IsType(OT_NOTHING))) {
+ v->current_order.MakeDummy();
+ }
+
+ /* Shared orders are fixed in AfterLoadVehicles now */
+ }
+}
+
+/*
+ * End -- Stuff to fix the savegames to be OpenTTD compatible
+ */
+
+
+/* Help:
+ * - OCL_SVAR: load 'type' to offset 'offset' in a struct of type 'base', which must also
+ * be given via base in LoadChunk() as real pointer
+ * - OCL_VAR: load 'type' to a global var
+ * - OCL_END: every struct must end with this
+ * - OCL_NULL: read 'amount' of bytes and send them to /dev/null or something
+ * - OCL_CHUNK: load an other proc to load a part of the savegame, 'amount' times
+ * - OCL_ASSERT: to check if we are really at the place we expect to be.. because old savegames are too binary to be sure ;)
+ */
+#define OCL_SVAR(type, base, offset) { type, 1, NULL, (uint)cpp_offsetof(base, offset), NULL }
+#define OCL_VAR(type, amount, pointer) { type, amount, pointer, 0, NULL }
+#define OCL_END() { OC_END, 0, NULL, 0, NULL }
+#define OCL_NULL(amount) { OC_NULL, amount, NULL, 0, NULL }
+#define OCL_CHUNK(amount, proc) { OC_CHUNK, amount, NULL, 0, proc }
+#define OCL_ASSERT(size) { OC_ASSERT, 1, NULL, size, NULL }
+
+/* The savegames has some hard-coded pointers, because it always enters the same
+ piece of memory.. we don't.. so we need to remap ;)
+ Old Towns are 94 bytes big
+ Old Orders are 2 bytes big */
+#define REMAP_TOWN_IDX(x) ((x) - (0x0459154 - 0x0458EF0)) / 94
+#define REMAP_ORDER_IDX(x) ((x) - (0x045AB08 - 0x0458EF0)) / 2
+
+extern TileIndex *_animated_tile_list;
+extern uint _animated_tile_count;
+extern char *_old_name_array;
+
+static byte _old_vehicle_multiplier;
+static uint8 *_old_map3;
+static uint32 _old_town_index;
+static uint16 _old_string_id;
+static uint16 _old_string_id_2;
+static uint16 _old_extra_chunk_nums;
+
+static void ReadTTDPatchFlags()
+{
+ if (_read_ttdpatch_flags) return;
+
+ _read_ttdpatch_flags = true;
+
+ /* TTDPatch misuses _old_map3 for flags.. read them! */
+ _old_vehicle_multiplier = _old_map3[0];
+ /* Somehow.... there was an error in some savegames, so 0 becomes 1
+ and 1 becomes 2. The rest of the values are okay */
+ if (_old_vehicle_multiplier < 2) _old_vehicle_multiplier++;
+
+ _old_vehicle_names = MallocT<StringID>(_old_vehicle_multiplier * 850);
+
+ /* TTDPatch increases the Vehicle-part in the middle of the game,
+ so if the multipler is anything else but 1, the assert fails..
+ bump the assert value so it doesn't!
+ (1 multipler == 850 vehicles
+ 1 vehicle == 128 bytes */
+ _bump_assert_value = (_old_vehicle_multiplier - 1) * 850 * 128;
+
+ for (uint i = 0; i < 17; i++) { // check tile 0, too
+ if (_old_map3[i] != 0) _savegame_type = SGT_TTDP1;
+ }
+
+ /* Check if we have a modern TTDPatch savegame (has extra data all around) */
+ if (memcmp(&_old_map3[0x1FFFA], "TTDp", 4) == 0) _savegame_type = SGT_TTDP2;
+
+ _old_extra_chunk_nums = _old_map3[_savegame_type == SGT_TTDP2 ? 0x1FFFE : 0x2];
+
+ /* Clean the misused places */
+ for (uint i = 0; i < 17; i++) _old_map3[i] = 0;
+ for (uint i = 0x1FE00; i < 0x20000; i++) _old_map3[i] = 0;
+
+ if (_savegame_type == SGT_TTDP2) DEBUG(oldloader, 2, "Found TTDPatch game");
+
+ DEBUG(oldloader, 3, "Vehicle-multiplier is set to %d (%d vehicles)", _old_vehicle_multiplier, _old_vehicle_multiplier * 850);
+}
+
+static const OldChunks town_chunk[] = {
+ OCL_SVAR( OC_TILE, Town, xy ),
+ OCL_NULL( 2 ), ///< population, no longer in use
+ OCL_SVAR( OC_UINT16, Town, townnametype ),
+ OCL_SVAR( OC_UINT32, Town, townnameparts ),
+ OCL_SVAR( OC_UINT8, Town, grow_counter ),
+ OCL_NULL( 1 ), ///< sort_index, no longer in use
+ OCL_NULL( 4 ), ///< sign-coordinates, no longer in use
+ OCL_NULL( 2 ), ///< namewidth, no longer in use
+ OCL_SVAR( OC_UINT16, Town, flags12 ),
+ OCL_NULL( 10 ), ///< radius, no longer in use
+
+ OCL_SVAR( OC_UINT16, Town, ratings[0] ),
+ OCL_SVAR( OC_UINT16, Town, ratings[1] ),
+ OCL_SVAR( OC_UINT16, Town, ratings[2] ),
+ OCL_SVAR( OC_UINT16, Town, ratings[3] ),
+ OCL_SVAR( OC_UINT16, Town, ratings[4] ),
+ OCL_SVAR( OC_UINT16, Town, ratings[5] ),
+ OCL_SVAR( OC_UINT16, Town, ratings[6] ),
+ OCL_SVAR( OC_UINT16, Town, ratings[7] ),
+
+ /* XXX - This is pretty odd.. we read 32bit, but only write 16bit.. sure there is
+ nothing changed ? ? */
+ OCL_SVAR( OC_FILE_U32 | OC_VAR_U16, Town, have_ratings ),
+ OCL_SVAR( OC_FILE_U32 | OC_VAR_U16, Town, statues ),
+ OCL_NULL( 2 ), ///< num_houses, no longer in use
+ OCL_SVAR( OC_UINT8, Town, time_until_rebuild ),
+ OCL_SVAR( OC_UINT8, Town, growth_rate ),
+
+ OCL_SVAR( OC_UINT16, Town, new_max_pass ),
+ OCL_SVAR( OC_UINT16, Town, new_max_mail ),
+ OCL_SVAR( OC_UINT16, Town, new_act_pass ),
+ OCL_SVAR( OC_UINT16, Town, new_act_mail ),
+ OCL_SVAR( OC_UINT16, Town, max_pass ),
+ OCL_SVAR( OC_UINT16, Town, max_mail ),
+ OCL_SVAR( OC_UINT16, Town, act_pass ),
+ OCL_SVAR( OC_UINT16, Town, act_mail ),
+
+ OCL_SVAR( OC_UINT8, Town, pct_pass_transported ),
+ OCL_SVAR( OC_UINT8, Town, pct_mail_transported ),
+
+ OCL_SVAR( OC_UINT16, Town, new_act_food ),
+ OCL_SVAR( OC_UINT16, Town, new_act_water ),
+ OCL_SVAR( OC_UINT16, Town, act_food ),
+ OCL_SVAR( OC_UINT16, Town, act_water ),
+
+ OCL_SVAR( OC_UINT8, Town, road_build_months ),
+ OCL_SVAR( OC_UINT8, Town, fund_buildings_months ),
+
+ OCL_NULL( 8 ), ///< some junk at the end of the record
+
+ OCL_END()
+};
+static bool LoadOldTown(LoadgameState *ls, int num)
+{
+ Town *t = new (num) Town();
+ if (!LoadChunk(ls, t, town_chunk)) return false;
+
+ if (t->xy == 0) t->xy = INVALID_TILE;
+
+ return true;
+}
+
+static uint16 _old_order;
+static const OldChunks order_chunk[] = {
+ OCL_VAR ( OC_UINT16, 1, &_old_order ),
+ OCL_END()
+};
+
+static bool LoadOldOrder(LoadgameState *ls, int num)
+{
+ if (!LoadChunk(ls, NULL, order_chunk)) return false;
+
+ new (num) Order(UnpackOldOrder(_old_order));
+
+ /* Relink the orders to eachother (in TTD(Patch) the orders for one
+ vehicle are behind eachother, with an invalid order (OT_NOTHING) as indication that
+ it is the last order */
+ if (num > 0 && GetOrder(num)->IsValid())
+ GetOrder(num - 1)->next = GetOrder(num);
+
+ return true;
+}
+
+static bool LoadOldAnimTileList(LoadgameState *ls, int num)
+{
+ /* This is sligthly hackish - we must load a chunk into an array whose
+ * address isn't static, but instead pointed to by _animated_tile_list.
+ * To achieve that, create an OldChunks list on the stack on the fly.
+ * The list cannot be static because the value of _animated_tile_list
+ * can change between calls. */
+
+ const OldChunks anim_chunk[] = {
+ OCL_VAR ( OC_TILE, 256, _animated_tile_list ),
+ OCL_END ()
+ };
+
+ if (!LoadChunk(ls, NULL, anim_chunk)) return false;
+
+ /* Update the animated tile counter by counting till the first zero in the array */
+ for (_animated_tile_count = 0; _animated_tile_count < 256; _animated_tile_count++) {
+ if (_animated_tile_list[_animated_tile_count] == 0) break;
+ }
+
+ return true;
+}
+
+static const OldChunks depot_chunk[] = {
+ OCL_SVAR( OC_TILE, Depot, xy ),
+ OCL_VAR ( OC_UINT32, 1, &_old_town_index ),
+ OCL_END()
+};
+
+static bool LoadOldDepot(LoadgameState *ls, int num)
+{
+ Depot *d = new (num) Depot();
+ if (!LoadChunk(ls, d, depot_chunk)) return false;
+
+ if (d->xy != 0) {
+ GetDepot(num)->town_index = REMAP_TOWN_IDX(_old_town_index);
+ } else {
+ d->xy = INVALID_TILE;
+ }
+
+ return true;
+}
+
+static int32 _old_price;
+static uint16 _old_price_frac;
+static const OldChunks price_chunk[] = {
+ OCL_VAR ( OC_INT32, 1, &_old_price ),
+ OCL_VAR ( OC_UINT16, 1, &_old_price_frac ),
+ OCL_END()
+};
+
+static bool LoadOldPrice(LoadgameState *ls, int num)
+{
+ if (!LoadChunk(ls, NULL, price_chunk)) return false;
+
+ /* We use a struct to store the prices, but they are ints in a row..
+ so just access the struct as an array of int32's */
+ ((Money*)&_price)[num] = _old_price;
+ _price_frac[num] = _old_price_frac;
+
+ return true;
+}
+
+static const OldChunks cargo_payment_rate_chunk[] = {
+ OCL_VAR ( OC_INT32, 1, &_old_price ),
+ OCL_VAR ( OC_UINT16, 1, &_old_price_frac ),
+
+ OCL_NULL( 2 ), ///< Junk
+ OCL_END()
+};
+
+static bool LoadOldCargoPaymentRate(LoadgameState *ls, int num)
+{
+ if (!LoadChunk(ls, NULL, cargo_payment_rate_chunk)) return false;
+
+ _cargo_payment_rates[num] = -_old_price;
+ _cargo_payment_rates_frac[num] = _old_price_frac;
+
+ return true;
+}
+
+static uint _current_station_id;
+static uint16 _waiting_acceptance;
+static uint8 _cargo_source;
+static uint8 _cargo_days;
+
+static const OldChunks goods_chunk[] = {
+ OCL_VAR ( OC_UINT16, 1, &_waiting_acceptance ),
+ OCL_SVAR( OC_UINT8, GoodsEntry, days_since_pickup ),
+ OCL_SVAR( OC_UINT8, GoodsEntry, rating ),
+ OCL_VAR ( OC_UINT8, 1, &_cargo_source ),
+ OCL_VAR ( OC_UINT8, 1, &_cargo_days ),
+ OCL_SVAR( OC_UINT8, GoodsEntry, last_speed ),
+ OCL_SVAR( OC_UINT8, GoodsEntry, last_age ),
+
+ OCL_END()
+};
+
+static bool LoadOldGood(LoadgameState *ls, int num)
+{
+ Station *st = GetStation(_current_station_id);
+ GoodsEntry *ge = &st->goods[num];
+ bool ret = LoadChunk(ls, ge, goods_chunk);
+ if (!ret) return false;
+
+ SB(ge->acceptance_pickup, GoodsEntry::ACCEPTANCE, 1, HasBit(_waiting_acceptance, 15));
+ SB(ge->acceptance_pickup, GoodsEntry::PICKUP, 1, _cargo_source != 0xFF);
+ if (GB(_waiting_acceptance, 0, 12) != 0) {
+ CargoPacket *cp = new CargoPacket();
+ cp->source = (_cargo_source == 0xFF) ? INVALID_STATION : _cargo_source;
+ cp->count = GB(_waiting_acceptance, 0, 12);
+ cp->days_in_transit = _cargo_days;
+ ge->cargo.Append(cp);
+ }
+ return ret;
+}
+
+static const OldChunks station_chunk[] = {
+ OCL_SVAR( OC_TILE, Station, xy ),
+ OCL_VAR ( OC_UINT32, 1, &_old_town_index ),
+
+ OCL_NULL( 4 ), ///< bus/lorry tile
+ OCL_SVAR( OC_TILE, Station, train_tile ),
+ OCL_SVAR( OC_TILE, Station, airport_tile ),
+ OCL_SVAR( OC_TILE, Station, dock_tile ),
+ OCL_SVAR( OC_UINT8, Station, trainst_w ),
+
+ OCL_NULL( 1 ), ///< sort-index, no longer in use
+ OCL_NULL( 2 ), ///< sign-width, no longer in use
+
+ OCL_VAR ( OC_UINT16, 1, &_old_string_id ),
+
+ OCL_NULL( 4 ), ///< sign left/top, no longer in use
+
+ OCL_SVAR( OC_UINT16, Station, had_vehicle_of_type ),
+
+ OCL_CHUNK( 12, LoadOldGood ),
+
+ OCL_SVAR( OC_UINT8, Station, time_since_load ),
+ OCL_SVAR( OC_UINT8, Station, time_since_unload ),
+ OCL_SVAR( OC_UINT8, Station, delete_ctr ),
+ OCL_SVAR( OC_UINT8, Station, owner ),
+ OCL_SVAR( OC_UINT8, Station, facilities ),
+ OCL_SVAR( OC_UINT8, Station, airport_type ),
+ /* Bus/truck status, no longer in use
+ * Blocked months
+ * Unknown
+ */
+ OCL_NULL( 4 ),
+ OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Station, airport_flags ),
+ OCL_NULL( 2 ), ///< last_vehicle. now last_vehicle_type
+
+ OCL_NULL( 4 ), ///< Junk at end of chunk
+
+ OCL_END()
+};
+static bool LoadOldStation(LoadgameState *ls, int num)
+{
+ Station *st = new (num) Station();
+ _current_station_id = num;
+
+ if (!LoadChunk(ls, st, station_chunk))
+ return false;
+
+ if (st->xy != 0) {
+ st->town = GetTown(REMAP_TOWN_IDX(_old_town_index));
+ st->string_id = RemapOldStringID(_old_string_id);
+ } else {
+ st->xy = INVALID_TILE;
+ }
+
+ return true;
+}
+
+static const OldChunks industry_chunk[] = {
+ OCL_SVAR( OC_TILE, Industry, xy ),
+ OCL_VAR ( OC_UINT32, 1, &_old_town_index ),
+ OCL_SVAR( OC_UINT8, Industry, width ),
+ OCL_SVAR( OC_UINT8, Industry, height ),
+ OCL_NULL( 2 ), ///< used to be industry's produced_cargo
+
+ OCL_SVAR( OC_UINT16, Industry, produced_cargo_waiting[0] ),
+ OCL_SVAR( OC_UINT16, Industry, produced_cargo_waiting[1] ),
+
+ OCL_SVAR( OC_UINT8, Industry, production_rate[0] ),
+ OCL_SVAR( OC_UINT8, Industry, production_rate[1] ),
+
+ OCL_NULL( 3 ), ///< used to be industry's accepts_cargo
+
+ OCL_SVAR( OC_UINT8, Industry, prod_level ),
+
+ OCL_SVAR( OC_UINT16, Industry, this_month_production[0] ),
+ OCL_SVAR( OC_UINT16, Industry, this_month_production[1] ),
+ OCL_SVAR( OC_UINT16, Industry, this_month_transported[0] ),
+ OCL_SVAR( OC_UINT16, Industry, this_month_transported[1] ),
+
+ OCL_SVAR( OC_UINT8, Industry, last_month_pct_transported[0] ),
+ OCL_SVAR( OC_UINT8, Industry, last_month_pct_transported[1] ),
+
+ OCL_SVAR( OC_UINT16, Industry, last_month_production[0] ),
+ OCL_SVAR( OC_UINT16, Industry, last_month_production[1] ),
+ OCL_SVAR( OC_UINT16, Industry, last_month_transported[0] ),
+ OCL_SVAR( OC_UINT16, Industry, last_month_transported[1] ),
+
+ OCL_SVAR( OC_UINT8, Industry, type ),
+ OCL_SVAR( OC_UINT8, Industry, owner ),
+ OCL_SVAR( OC_UINT8, Industry, random_color ),
+ OCL_SVAR( OC_FILE_U8 | OC_VAR_I32, Industry, last_prod_year ),
+ OCL_SVAR( OC_UINT16, Industry, counter ),
+ OCL_SVAR( OC_UINT8, Industry, was_cargo_delivered ),
+
+ OCL_NULL( 9 ), ///< Random junk at the end of this chunk
+
+ OCL_END()
+};
+
+static bool LoadOldIndustry(LoadgameState *ls, int num)
+{
+ Industry *i = new (num) Industry();
+ if (!LoadChunk(ls, i, industry_chunk)) return false;
+
+ if (i->xy != 0) {
+ i->town = GetTown(REMAP_TOWN_IDX(_old_town_index));
+ IncIndustryTypeCount(i->type);
+ } else {
+ i->xy = INVALID_TILE;
+ }
+
+ return true;
+}
+
+static CompanyID _current_company_id;
+static int32 _old_yearly;
+
+static const OldChunks _company_yearly_chunk[] = {
+ OCL_VAR( OC_INT32, 1, &_old_yearly ),
+ OCL_END()
+};
+
+static bool OldCompanyYearly(LoadgameState *ls, int num)
+{
+ int i;
+ Company *c = GetCompany(_current_company_id);
+
+ for (i = 0; i < 13; i++) {
+ if (!LoadChunk(ls, NULL, _company_yearly_chunk)) return false;
+
+ c->yearly_expenses[num][i] = _old_yearly;
+ }
+
+ return true;
+}
+
+static const OldChunks _company_economy_chunk[] = {
+ OCL_SVAR( OC_FILE_I32 | OC_VAR_I64, CompanyEconomyEntry, income ),
+ OCL_SVAR( OC_FILE_I32 | OC_VAR_I64, CompanyEconomyEntry, expenses ),
+ OCL_SVAR( OC_INT32, CompanyEconomyEntry, delivered_cargo ),
+ OCL_SVAR( OC_INT32, CompanyEconomyEntry, performance_history ),
+ OCL_SVAR( OC_FILE_I32 | OC_VAR_I64, CompanyEconomyEntry, company_value ),
+
+ OCL_END()
+};
+
+static bool OldCompanyEconomy(LoadgameState *ls, int num)
+{
+ int i;
+ Company *c = GetCompany(_current_company_id);
+
+ if (!LoadChunk(ls, &c->cur_economy, _company_economy_chunk)) return false;
+
+ /* Don't ask, but the number in TTD(Patch) are inversed to OpenTTD */
+ c->cur_economy.income = -c->cur_economy.income;
+ c->cur_economy.expenses = -c->cur_economy.expenses;
+
+ for (i = 0; i < 24; i++) {
+ if (!LoadChunk(ls, &c->old_economy[i], _company_economy_chunk)) return false;
+
+ c->old_economy[i].income = -c->old_economy[i].income;
+ c->old_economy[i].expenses = -c->old_economy[i].expenses;
+ }
+
+ return true;
+}
+
+static const OldChunks _company_ai_build_rec_chunk[] = {
+ OCL_SVAR( OC_TILE, AiBuildRec, spec_tile ),
+ OCL_SVAR( OC_TILE, AiBuildRec, use_tile ),
+ OCL_SVAR( OC_UINT8, AiBuildRec, rand_rng ),
+ OCL_SVAR( OC_UINT8, AiBuildRec, cur_building_rule ),
+ OCL_SVAR( OC_UINT8, AiBuildRec, unk6 ),
+ OCL_SVAR( OC_UINT8, AiBuildRec, unk7 ),
+ OCL_SVAR( OC_UINT8, AiBuildRec, buildcmd_a ),
+ OCL_SVAR( OC_UINT8, AiBuildRec, buildcmd_b ),
+ OCL_SVAR( OC_UINT8, AiBuildRec, direction ),
+ OCL_SVAR( OC_UINT8, AiBuildRec, cargo ),
+
+ OCL_NULL( 8 ), ///< Junk...
+
+ OCL_END()
+};
+
+static bool OldLoadAIBuildRec(LoadgameState *ls, int num)
+{
+ Company *c = GetCompany(_current_company_id);
+
+ switch (num) {
+ case 0: return LoadChunk(ls, &_companies_ai[c->index].src, _company_ai_build_rec_chunk);
+ case 1: return LoadChunk(ls, &_companies_ai[c->index].dst, _company_ai_build_rec_chunk);
+ case 2: return LoadChunk(ls, &_companies_ai[c->index].mid1, _company_ai_build_rec_chunk);
+ case 3: return LoadChunk(ls, &_companies_ai[c->index].mid2, _company_ai_build_rec_chunk);
+ }
+
+ return false;
+}
+static const OldChunks _company_ai_chunk[] = {
+ OCL_SVAR( OC_UINT8, CompanyAI, state ),
+ OCL_NULL( 1 ), ///< Junk
+ OCL_SVAR( OC_UINT8, CompanyAI, state_mode ),
+ OCL_SVAR( OC_UINT16, CompanyAI, state_counter ),
+ OCL_SVAR( OC_UINT16, CompanyAI, timeout_counter ),
+
+ OCL_CHUNK( 4, OldLoadAIBuildRec ),
+
+ OCL_NULL( 20 ), ///< More junk
+
+ OCL_SVAR( OC_UINT8, CompanyAI, cargo_type ),
+ OCL_SVAR( OC_UINT8, CompanyAI, num_wagons ),
+ OCL_SVAR( OC_UINT8, CompanyAI, build_kind ),
+ OCL_SVAR( OC_UINT8, CompanyAI, num_build_rec ),
+ OCL_SVAR( OC_UINT8, CompanyAI, num_loco_to_build ),
+ OCL_SVAR( OC_UINT8, CompanyAI, num_want_fullload ),
+
+ OCL_NULL( 14 ), ///< Oh no more junk :|
+
+ OCL_NULL( 2 ), ///< Loco-id, not used
+
+ OCL_SVAR( OC_UINT16, CompanyAI, wagon_list[0] ),
+ OCL_SVAR( OC_UINT16, CompanyAI, wagon_list[1] ),
+ OCL_SVAR( OC_UINT16, CompanyAI, wagon_list[2] ),
+ OCL_SVAR( OC_UINT16, CompanyAI, wagon_list[3] ),
+ OCL_SVAR( OC_UINT16, CompanyAI, wagon_list[4] ),
+ OCL_SVAR( OC_UINT16, CompanyAI, wagon_list[5] ),
+ OCL_SVAR( OC_UINT16, CompanyAI, wagon_list[6] ),
+ OCL_SVAR( OC_UINT16, CompanyAI, wagon_list[7] ),
+ OCL_SVAR( OC_UINT16, CompanyAI, wagon_list[8] ),
+ OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[0] ),
+ OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[1] ),
+ OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[2] ),
+ OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[3] ),
+ OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[4] ),
+ OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[5] ),
+ OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[6] ),
+ OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[7] ),
+ OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[8] ),
+ OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[9] ),
+ OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[10] ),
+ OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[11] ),
+ OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[12] ),
+ OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[13] ),
+ OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[14] ),
+ OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[15] ),
+ OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[16] ),
+ OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[17] ),
+ OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[18] ),
+ OCL_SVAR( OC_UINT8, CompanyAI, order_list_blocks[19] ),
+
+ OCL_SVAR( OC_UINT16, CompanyAI, start_tile_a ),
+ OCL_SVAR( OC_UINT16, CompanyAI, start_tile_b ),
+ OCL_SVAR( OC_UINT16, CompanyAI, cur_tile_a ),
+ OCL_SVAR( OC_UINT16, CompanyAI, cur_tile_b ),
+
+ OCL_SVAR( OC_UINT8, CompanyAI, start_dir_a ),
+ OCL_SVAR( OC_UINT8, CompanyAI, start_dir_b ),
+ OCL_SVAR( OC_UINT8, CompanyAI, cur_dir_a ),
+ OCL_SVAR( OC_UINT8, CompanyAI, cur_dir_b ),
+
+ OCL_SVAR( OC_UINT8, CompanyAI, banned_tile_count ),
+
+ OCL_SVAR( OC_TILE, CompanyAI, banned_tiles[0] ),
+ OCL_SVAR( OC_UINT8, CompanyAI, banned_val[0] ),
+ OCL_SVAR( OC_TILE, CompanyAI, banned_tiles[1] ),
+ OCL_SVAR( OC_UINT8, CompanyAI, banned_val[1] ),
+ OCL_SVAR( OC_TILE, CompanyAI, banned_tiles[2] ),
+ OCL_SVAR( OC_UINT8, CompanyAI, banned_val[2] ),
+ OCL_SVAR( OC_TILE, CompanyAI, banned_tiles[3] ),
+ OCL_SVAR( OC_UINT8, CompanyAI, banned_val[3] ),
+ OCL_SVAR( OC_TILE, CompanyAI, banned_tiles[4] ),
+ OCL_SVAR( OC_UINT8, CompanyAI, banned_val[4] ),
+ OCL_SVAR( OC_TILE, CompanyAI, banned_tiles[5] ),
+ OCL_SVAR( OC_UINT8, CompanyAI, banned_val[5] ),
+ OCL_SVAR( OC_TILE, CompanyAI, banned_tiles[6] ),
+ OCL_SVAR( OC_UINT8, CompanyAI, banned_val[6] ),
+ OCL_SVAR( OC_TILE, CompanyAI, banned_tiles[7] ),
+ OCL_SVAR( OC_UINT8, CompanyAI, banned_val[7] ),
+ OCL_SVAR( OC_TILE, CompanyAI, banned_tiles[8] ),
+ OCL_SVAR( OC_UINT8, CompanyAI, banned_val[8] ),
+ OCL_SVAR( OC_TILE, CompanyAI, banned_tiles[9] ),
+ OCL_SVAR( OC_UINT8, CompanyAI, banned_val[9] ),
+ OCL_SVAR( OC_TILE, CompanyAI, banned_tiles[10] ),
+ OCL_SVAR( OC_UINT8, CompanyAI, banned_val[10] ),
+ OCL_SVAR( OC_TILE, CompanyAI, banned_tiles[11] ),
+ OCL_SVAR( OC_UINT8, CompanyAI, banned_val[11] ),
+ OCL_SVAR( OC_TILE, CompanyAI, banned_tiles[12] ),
+ OCL_SVAR( OC_UINT8, CompanyAI, banned_val[12] ),
+ OCL_SVAR( OC_TILE, CompanyAI, banned_tiles[13] ),
+ OCL_SVAR( OC_UINT8, CompanyAI, banned_val[13] ),
+ OCL_SVAR( OC_TILE, CompanyAI, banned_tiles[14] ),
+ OCL_SVAR( OC_UINT8, CompanyAI, banned_val[14] ),
+ OCL_SVAR( OC_TILE, CompanyAI, banned_tiles[15] ),
+ OCL_SVAR( OC_UINT8, CompanyAI, banned_val[15] ),
+
+ OCL_SVAR( OC_UINT8, CompanyAI, railtype_to_use ),
+ OCL_SVAR( OC_UINT8, CompanyAI, route_type_mask ),
+
+ OCL_END()
+};
+
+static bool OldCompanyAI(LoadgameState *ls, int num)
+{
+ return LoadChunk(ls, &_companies_ai[_current_company_id], _company_ai_chunk);
+}
+
+uint8 ai_tick;
+static const OldChunks _company_chunk[] = {
+ OCL_VAR ( OC_UINT16, 1, &_old_string_id ),
+ OCL_SVAR( OC_UINT32, Company, name_2 ),
+ OCL_SVAR( OC_UINT32, Company, face ),
+ OCL_VAR ( OC_UINT16, 1, &_old_string_id_2 ),
+ OCL_SVAR( OC_UINT32, Company, president_name_2 ),
+
+ OCL_SVAR( OC_INT32, Company, money ),
+ OCL_SVAR( OC_INT32, Company, current_loan ),
+
+ OCL_SVAR( OC_UINT8, Company, colour ),
+ OCL_SVAR( OC_UINT8, Company, money_fraction ),
+ OCL_SVAR( OC_UINT8, Company, quarters_of_bankrupcy ),
+ OCL_SVAR( OC_UINT8, Company, bankrupt_asked ),
+ OCL_SVAR( OC_FILE_U32 | OC_VAR_I64, Company, bankrupt_value ),
+ OCL_SVAR( OC_UINT16, Company, bankrupt_timeout ),
+
+ OCL_SVAR( OC_UINT32, Company, cargo_types ),
+
+ OCL_CHUNK( 3, OldCompanyYearly ),
+ OCL_CHUNK( 1, OldCompanyEconomy ),
+
+ OCL_SVAR( OC_FILE_U16 | OC_VAR_I32, Company, inaugurated_year),
+ OCL_SVAR( OC_TILE, Company, last_build_coordinate ),
+ OCL_SVAR( OC_UINT8, Company, num_valid_stat_ent ),
+
+ OCL_CHUNK( 1, OldCompanyAI ),
+
+ OCL_SVAR( OC_UINT8, Company, block_preview ),
+ OCL_VAR( OC_UINT8, 1, &ai_tick ),
+ OCL_SVAR( OC_UINT8, Company, avail_railtypes ),
+ OCL_SVAR( OC_TILE, Company, location_of_HQ ),
+ OCL_SVAR( OC_UINT8, Company, share_owners[0] ),
+ OCL_SVAR( OC_UINT8, Company, share_owners[1] ),
+ OCL_SVAR( OC_UINT8, Company, share_owners[2] ),
+ OCL_SVAR( OC_UINT8, Company, share_owners[3] ),
+
+ OCL_NULL( 8 ), ///< junk at end of chunk
+
+ OCL_END()
+};
+
+static bool LoadOldCompany(LoadgameState *ls, int num)
+{
+ Company *c = new (num) Company();
+
+ _current_company_id = (CompanyID)num;
+
+ if (!LoadChunk(ls, c, _company_chunk)) return false;
+
+ if (_old_string_id == 0) {
+ delete c;
+ return true;
+ }
+
+ c->name_1 = RemapOldStringID(_old_string_id);
+ c->president_name_1 = RemapOldStringID(_old_string_id_2);
+ _companies_ai[_current_company_id].tick = ai_tick;
+
+ if (num == 0) {
+ /* If the first company has no name, make sure we call it UNNAMED */
+ if (c->name_1 == 0)
+ c->name_1 = STR_SV_UNNAMED;
+ } else {
+ /* Beside some multiplayer maps (1 on 1), which we don't official support,
+ * all other companys are an AI.. mark them as such */
+ c->is_ai = true;
+ }
+
+ /* Sometimes it is better to not ask.. in old scenarios, the money
+ * was always 893288 pounds. In the newer versions this is correct,
+ * but correct for those oldies
+ * Ps: this also means that if you had exact 893288 pounds, you will go back
+ * to 100000.. this is a very VERY small chance ;) */
+ if (c->money == 893288) c->money = c->current_loan = 100000;
+
+ _company_colours[num] = c->colour;
+ c->inaugurated_year -= ORIGINAL_BASE_YEAR;
+
+ /* State 20 for AI companies is sell vehicle. Since the AI struct is not
+ * really figured out as of now, _companies_ai[c->index].cur_veh; needed for 'sell vehicle'
+ * is NULL and the function will crash. To fix this, just change the state
+ * to some harmless state, like 'loop vehicle'; 1 */
+ if (!IsHumanCompany((CompanyID)num) && _companies_ai[c->index].state == 20) _companies_ai[c->index].state = 1;
+
+ if (c->is_ai && (!_networking || _network_server) && _ai.enabled)
+ AI_StartNewAI(c->index);
+
+ return true;
+}
+
+static uint32 _old_order_ptr;
+static uint16 _old_next_ptr;
+static uint32 _current_vehicle_id;
+
+static const OldChunks vehicle_train_chunk[] = {
+ OCL_SVAR( OC_UINT8, VehicleRail, track ),
+ OCL_SVAR( OC_UINT8, VehicleRail, force_proceed ),
+ OCL_SVAR( OC_UINT16, VehicleRail, crash_anim_pos ),
+ OCL_SVAR( OC_UINT8, VehicleRail, railtype ),
+
+ OCL_NULL( 5 ), ///< Junk
+
+ OCL_END()
+};
+
+static const OldChunks vehicle_road_chunk[] = {
+ OCL_SVAR( OC_UINT8, VehicleRoad, state ),
+ OCL_SVAR( OC_UINT8, VehicleRoad, frame ),
+ OCL_SVAR( OC_UINT16, VehicleRoad, blocked_ctr ),
+ OCL_SVAR( OC_UINT8, VehicleRoad, overtaking ),
+ OCL_SVAR( OC_UINT8, VehicleRoad, overtaking_ctr ),
+ OCL_SVAR( OC_UINT16, VehicleRoad, crashed_ctr ),
+ OCL_SVAR( OC_UINT8, VehicleRoad, reverse_ctr ),
+
+ OCL_NULL( 1 ), ///< Junk
+
+ OCL_END()
+};
+
+static const OldChunks vehicle_ship_chunk[] = {
+ OCL_SVAR( OC_UINT8, VehicleShip, state ),
+
+ OCL_NULL( 9 ), ///< Junk
+
+ OCL_END()
+};
+
+static const OldChunks vehicle_air_chunk[] = {
+ OCL_SVAR( OC_UINT8, VehicleAir, pos ),
+ OCL_SVAR( OC_FILE_U8 | OC_VAR_U16, VehicleAir, targetairport ),
+ OCL_SVAR( OC_UINT16, VehicleAir, crashed_counter ),
+ OCL_SVAR( OC_UINT8, VehicleAir, state ),
+
+ OCL_NULL( 5 ), ///< Junk
+
+ OCL_END()
+};
+
+static const OldChunks vehicle_effect_chunk[] = {
+ OCL_SVAR( OC_UINT16, VehicleEffect, animation_state ),
+ OCL_SVAR( OC_UINT8, VehicleEffect, animation_substate ),
+
+ OCL_NULL( 7 ), // Junk
+
+ OCL_END()
+};
+
+static const OldChunks vehicle_disaster_chunk[] = {
+ OCL_SVAR( OC_UINT16, VehicleDisaster, image_override ),
+ OCL_SVAR( OC_UINT16, VehicleDisaster, big_ufo_destroyer_target ),
+
+ OCL_NULL( 6 ), ///< Junk
+
+ OCL_END()
+};
+
+static const OldChunks vehicle_empty_chunk[] = {
+ OCL_NULL( 10 ), ///< Junk
+
+ OCL_END()
+};
+
+static bool LoadOldVehicleUnion(LoadgameState *ls, int num)
+{
+ Vehicle *v = GetVehicle(_current_vehicle_id);
+ uint temp = ls->total_read;
+ bool res;
+
+ switch (v->type) {
+ default: NOT_REACHED();
+ case VEH_INVALID : res = LoadChunk(ls, NULL, vehicle_empty_chunk); break;
+ case VEH_TRAIN : res = LoadChunk(ls, &v->u.rail, vehicle_train_chunk); break;
+ case VEH_ROAD : res = LoadChunk(ls, &v->u.road, vehicle_road_chunk); break;
+ case VEH_SHIP : res = LoadChunk(ls, &v->u.ship, vehicle_ship_chunk); break;
+ case VEH_AIRCRAFT: res = LoadChunk(ls, &v->u.air, vehicle_air_chunk); break;
+ case VEH_EFFECT : res = LoadChunk(ls, &v->u.effect, vehicle_effect_chunk); break;
+ case VEH_DISASTER: res = LoadChunk(ls, &v->u.disaster, vehicle_disaster_chunk); break;
+ }
+
+ /* This chunk size should always be 10 bytes */
+ if (ls->total_read - temp != 10) {
+ DEBUG(oldloader, 0, "Assert failed in VehicleUnion: invalid chunk size");
+ return false;
+ }
+
+ return res;
+}
+
+static uint16 _cargo_count;
+
+static const OldChunks vehicle_chunk[] = {
+ OCL_SVAR( OC_UINT8, Vehicle, subtype ),
+
+ OCL_NULL( 2 ), ///< Hash, calculated automatically
+ OCL_NULL( 2 ), ///< Index, calculated automatically
+
+ OCL_VAR ( OC_UINT32, 1, &_old_order_ptr ),
+ OCL_VAR ( OC_UINT16, 1, &_old_order ),
+
+ OCL_NULL ( 1 ), ///< num_orders, now calculated
+ OCL_SVAR( OC_UINT8, Vehicle, cur_order_index ),
+ OCL_SVAR( OC_TILE, Vehicle, dest_tile ),
+ OCL_SVAR( OC_UINT16, Vehicle, load_unload_time_rem ),
+ OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Vehicle, date_of_last_service ),
+ OCL_SVAR( OC_UINT16, Vehicle, service_interval ),
+ OCL_SVAR( OC_FILE_U8 | OC_VAR_U16, Vehicle, last_station_visited ),
+ OCL_SVAR( OC_UINT8, Vehicle, tick_counter ),
+ OCL_SVAR( OC_UINT16, Vehicle, max_speed ),
+
+ OCL_SVAR( OC_FILE_U16 | OC_VAR_I32, Vehicle, x_pos ),
+ OCL_SVAR( OC_FILE_U16 | OC_VAR_I32, Vehicle, y_pos ),
+ OCL_SVAR( OC_UINT8, Vehicle, z_pos ),
+ OCL_SVAR( OC_UINT8, Vehicle, direction ),
+ OCL_NULL( 2 ), ///< x_offs and y_offs, calculated automatically
+ OCL_NULL( 2 ), ///< x_extent and y_extent, calculated automatically
+ OCL_NULL( 1 ), ///< z_extent, calculated automatically
+
+ OCL_SVAR( OC_UINT8, Vehicle, owner ),
+ OCL_SVAR( OC_TILE, Vehicle, tile ),
+ OCL_SVAR( OC_UINT16, Vehicle, cur_image ),
+
+ OCL_NULL( 8 ), ///< Vehicle sprite box, calculated automatically
+
+ OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, Vehicle, vehstatus ),
+ OCL_SVAR( OC_UINT16, Vehicle, cur_speed ),
+ OCL_SVAR( OC_UINT8, Vehicle, subspeed ),
+ OCL_SVAR( OC_UINT8, Vehicle, acceleration ),
+ OCL_SVAR( OC_UINT8, Vehicle, progress ),
+
+ OCL_SVAR( OC_UINT8, Vehicle, cargo_type ),
+ OCL_SVAR( OC_UINT16, Vehicle, cargo_cap ),
+ OCL_VAR ( OC_UINT16, 1, &_cargo_count ),
+ OCL_VAR ( OC_UINT8, 1, &_cargo_source ),
+ OCL_VAR ( OC_UINT8, 1, &_cargo_days ),
+
+ OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Vehicle, age ),
+ OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Vehicle, max_age ),
+ OCL_SVAR( OC_FILE_U8 | OC_VAR_I32, Vehicle, build_year ),
+ OCL_SVAR( OC_FILE_U8 | OC_VAR_U16, Vehicle, unitnumber ),
+
+ OCL_SVAR( OC_UINT16, Vehicle, engine_type ),
+
+ OCL_SVAR( OC_UINT8, Vehicle, spritenum ),
+ OCL_SVAR( OC_UINT8, Vehicle, day_counter ),
+
+ OCL_SVAR( OC_UINT8, Vehicle, breakdowns_since_last_service ),
+ OCL_SVAR( OC_UINT8, Vehicle, breakdown_ctr ),
+ OCL_SVAR( OC_UINT8, Vehicle, breakdown_delay ),
+ OCL_SVAR( OC_UINT8, Vehicle, breakdown_chance ),
+
+ OCL_SVAR( OC_UINT16, Vehicle, reliability ),
+ OCL_SVAR( OC_UINT16, Vehicle, reliability_spd_dec ),
+
+ OCL_SVAR( OC_FILE_I32 | OC_VAR_I64, Vehicle, profit_this_year ),
+ OCL_SVAR( OC_FILE_I32 | OC_VAR_I64, Vehicle, profit_last_year ),
+
+ OCL_VAR ( OC_UINT16, 1, &_old_next_ptr ),
+
+ OCL_SVAR( OC_UINT32, Vehicle, value ),
+
+ OCL_VAR ( OC_UINT16, 1, &_old_string_id ),
+
+ OCL_CHUNK( 1, LoadOldVehicleUnion ),
+
+ OCL_NULL( 20 ), ///< Junk at end of struct (TTDPatch has some data in it)
+
+ OCL_END()
+};
+
+bool LoadOldVehicle(LoadgameState *ls, int num)
+{
+ uint i;
+
+ /* Read the TTDPatch flags, because we need some info from it */
+ ReadTTDPatchFlags();
+
+ for (i = 0; i < _old_vehicle_multiplier; i++) {
+ _current_vehicle_id = num * _old_vehicle_multiplier + i;
+
+ /* Read the vehicle type and allocate the right vehicle */
+ Vehicle *v;
+ switch (ReadByte(ls)) {
+ default: NOT_REACHED();
+ case 0x00 /*VEH_INVALID */: v = new (_current_vehicle_id) InvalidVehicle(); break;
+ case 0x10 /*VEH_TRAIN */: v = new (_current_vehicle_id) Train(); break;
+ case 0x11 /*VEH_ROAD */: v = new (_current_vehicle_id) RoadVehicle(); break;
+ case 0x12 /*VEH_SHIP */: v = new (_current_vehicle_id) Ship(); break;
+ case 0x13 /*VEH_AIRCRAFT*/: v = new (_current_vehicle_id) Aircraft(); break;
+ case 0x14 /*VEH_EFFECT */: v = new (_current_vehicle_id) EffectVehicle(); break;
+ case 0x15 /*VEH_DISASTER*/: v = new (_current_vehicle_id) DisasterVehicle(); break;
+ }
+ if (!LoadChunk(ls, v, vehicle_chunk)) return false;
+
+ /* This should be consistent, else we have a big problem... */
+ if (v->index != _current_vehicle_id) {
+ DEBUG(oldloader, 0, "Loading failed - vehicle-array is invalid");
+ return false;
+ }
+
+ if (_old_order_ptr != 0 && _old_order_ptr != 0xFFFFFFFF) {
+ uint old_id = REMAP_ORDER_IDX(_old_order_ptr);
+ /* There is a maximum of 5000 orders in old savegames, so *if*
+ * we go over that limit something is very wrong. In that case
+ * we just assume there are no orders for the vehicle.
+ */
+ if (old_id < 5000) v->orders.old = GetOrder(old_id);
+ }
+ v->current_order.AssignOrder(UnpackOldOrder(_old_order));
+
+ /* For some reason we need to correct for this */
+ switch (v->spritenum) {
+ case 0xfd: break;
+ case 0xff: v->spritenum = 0xfe; break;
+ default: v->spritenum >>= 1; break;
+ }
+
+ if (_old_next_ptr != 0xFFFF) v->next = GetVehiclePoolSize() <= _old_next_ptr ? new (_old_next_ptr) InvalidVehicle() : GetVehicle(_old_next_ptr);
+
+ _old_vehicle_names[_current_vehicle_id] = RemapOldStringID(_old_string_id);
+ v->name = NULL;
+
+ /* Vehicle-subtype is different in TTD(Patch) */
+ if (v->type == VEH_EFFECT) v->subtype = v->subtype >> 1;
+
+ if (_cargo_count != 0) {
+ CargoPacket *cp = new CargoPacket((_cargo_source == 0xFF) ? INVALID_STATION : _cargo_source, _cargo_count);
+ cp->days_in_transit = _cargo_days;
+ v->cargo.Append(cp);
+ }
+ }
+
+ return true;
+}
+
+static const OldChunks sign_chunk[] = {
+ OCL_VAR ( OC_UINT16, 1, &_old_string_id ),
+ OCL_SVAR( OC_FILE_U16 | OC_VAR_I32, Sign, x ),
+ OCL_SVAR( OC_FILE_U16 | OC_VAR_I32, Sign, y ),
+ OCL_SVAR( OC_FILE_U16 | OC_VAR_I8, Sign, z ),
+
+ OCL_NULL( 6 ), ///< Width of sign, no longer in use
+
+ OCL_END()
+};
+
+static bool LoadOldSign(LoadgameState *ls, int num)
+{
+ Sign *si = new (num) Sign();
+ if (!LoadChunk(ls, si, sign_chunk)) return false;
+
+ _old_string_id = RemapOldStringID(_old_string_id);
+ si->name = CopyFromOldName(_old_string_id);
+
+ return true;
+}
+
+static const OldChunks engine_chunk[] = {
+ OCL_SVAR( OC_UINT16, Engine, company_avail ),
+ OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Engine, intro_date ),
+ OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Engine, age ),
+ OCL_SVAR( OC_UINT16, Engine, reliability ),
+ OCL_SVAR( OC_UINT16, Engine, reliability_spd_dec ),
+ OCL_SVAR( OC_UINT16, Engine, reliability_start ),
+ OCL_SVAR( OC_UINT16, Engine, reliability_max ),
+ OCL_SVAR( OC_UINT16, Engine, reliability_final ),
+ OCL_SVAR( OC_UINT16, Engine, duration_phase_1 ),
+ OCL_SVAR( OC_UINT16, Engine, duration_phase_2 ),
+ OCL_SVAR( OC_UINT16, Engine, duration_phase_3 ),
+
+ OCL_SVAR( OC_UINT8, Engine, lifelength ),
+ OCL_SVAR( OC_UINT8, Engine, flags ),
+ OCL_SVAR( OC_UINT8, Engine, preview_company_rank ),
+ OCL_SVAR( OC_UINT8, Engine, preview_wait ),
+
+ OCL_NULL( 2 ), ///< Junk
+
+ OCL_END()
+};
+
+static bool LoadOldEngine(LoadgameState *ls, int num)
+{
+ Engine *e = GetTempDataEngine(num);
+ return LoadChunk(ls, e, engine_chunk);
+}
+
+static bool LoadOldEngineName(LoadgameState *ls, int num)
+{
+ Engine *e = GetTempDataEngine(num);
+ e->name = CopyFromOldName(RemapOldStringID(ReadUint16(ls)));
+ return true;
+}
+
+static const OldChunks subsidy_chunk[] = {
+ OCL_SVAR( OC_UINT8, Subsidy, cargo_type ),
+ OCL_SVAR( OC_UINT8, Subsidy, age ),
+ OCL_SVAR( OC_FILE_U8 | OC_VAR_U16, Subsidy, from ),
+ OCL_SVAR( OC_FILE_U8 | OC_VAR_U16, Subsidy, to ),
+
+ OCL_END()
+};
+
+static inline bool LoadOldSubsidy(LoadgameState *ls, int num)
+{
+ return LoadChunk(ls, &_subsidies[num], subsidy_chunk);
+}
+
+static const OldChunks game_difficulty_chunk[] = {
+ OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, DifficultySettings, max_no_competitors ),
+ OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, DifficultySettings, competitor_start_time ),
+ OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, DifficultySettings, number_towns ),
+ OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, DifficultySettings, number_industries ),
+ OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, DifficultySettings, max_loan ),
+ OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, DifficultySettings, initial_interest ),
+ OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, DifficultySettings, vehicle_costs ),
+ OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, DifficultySettings, competitor_speed ),
+ OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, DifficultySettings, competitor_intelligence ),
+ OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, DifficultySettings, vehicle_breakdowns ),
+ OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, DifficultySettings, subsidy_multiplier ),
+ OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, DifficultySettings, construction_cost ),
+ OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, DifficultySettings, terrain_type ),
+ OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, DifficultySettings, quantity_sea_lakes ),
+ OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, DifficultySettings, economy ),
+ OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, DifficultySettings, line_reverse_mode ),
+ OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, DifficultySettings, disasters ),
+ OCL_END()
+};
+
+static inline bool LoadOldGameDifficulty(LoadgameState *ls, int num)
+{
+ bool ret = LoadChunk(ls, &_settings_game.difficulty, game_difficulty_chunk);
+ _settings_game.difficulty.max_loan *= 1000;
+ return ret;
+}
+
+
+static bool LoadOldMapPart1(LoadgameState *ls, int num)
+{
+ uint i;
+
+ for (i = 0; i < OLD_MAP_SIZE; i++) {
+ _m[i].m1 = ReadByte(ls);
+ }
+ for (i = 0; i < OLD_MAP_SIZE; i++) {
+ _m[i].m2 = ReadByte(ls);
+ }
+ for (i = 0; i < OLD_MAP_SIZE; i++) {
+ _old_map3[i * 2] = ReadByte(ls);
+ _old_map3[i * 2 + 1] = ReadByte(ls);
+ }
+ for (i = 0; i < OLD_MAP_SIZE / 4; i++) {
+ byte b = ReadByte(ls);
+ _m[i * 4 + 0].m6 = GB(b, 0, 2);
+ _m[i * 4 + 1].m6 = GB(b, 2, 2);
+ _m[i * 4 + 2].m6 = GB(b, 4, 2);
+ _m[i * 4 + 3].m6 = GB(b, 6, 2);
+ }
+
+ return !ls->failed;
+}
+
+static bool LoadOldMapPart2(LoadgameState *ls, int num)
+{
+ uint i;
+
+ for (i = 0; i < OLD_MAP_SIZE; i++) {
+ _m[i].type_height = ReadByte(ls);
+ }
+ for (i = 0; i < OLD_MAP_SIZE; i++) {
+ _m[i].m5 = ReadByte(ls);
+ }
+
+ return !ls->failed;
+}
+
+static bool LoadTTDPatchExtraChunks(LoadgameState *ls, int num)
+{
+ ReadTTDPatchFlags();
+
+ DEBUG(oldloader, 2, "Found %d extra chunk(s)", _old_extra_chunk_nums);
+
+ for (int i = 0; i != _old_extra_chunk_nums; i++) {
+ uint16 id = ReadUint16(ls);
+ uint32 len = ReadUint32(ls);
+
+ switch (id) {
+ /* List of GRFIDs, used in the savegame. 0x8004 is the new ID
+ * They are saved in a 'GRFID:4 active:1' format, 5 bytes for each entry */
+ case 0x2:
+ case 0x8004: {
+ /* Skip the first element: TTDP hack for the Action D special variables (FFFF0000 01) */
+ ReadUint32(ls); ReadByte(ls); len -= 5;
+
+ ClearGRFConfigList(&_grfconfig);
+ while (len != 0) {
+ uint32 grfid = ReadUint32(ls);
+
+ if (ReadByte(ls) == 1) {
+ GRFConfig *c = CallocT<GRFConfig>(1);
+ c->grfid = grfid;
+ c->filename = strdup("TTDP game, no information");
+
+ AppendToGRFConfigList(&_grfconfig, c);
+ DEBUG(oldloader, 3, "TTDPatch game using GRF file with GRFID %0X", BSWAP32(c->grfid));
+ }
+ len -= 5;
+ };
+
+ /* Append static NewGRF configuration */
+ AppendStaticGRFConfigs(&_grfconfig);
+ } break;
+
+ /* TTDPatch version and configuration */
+ case 0x3:
+ _ttdp_version = ReadUint32(ls);
+ DEBUG(oldloader, 3, "Game saved with TTDPatch version %d.%d.%d r%d",
+ GB(_ttdp_version, 24, 8), GB(_ttdp_version, 20, 4), GB(_ttdp_version, 16, 4), GB(_ttdp_version, 0, 16));
+ len -= 4;
+ while (len-- != 0) ReadByte(ls); // skip the configuration
+ break;
+
+ default:
+ DEBUG(oldloader, 4, "Skipping unknown extra chunk %X", id);
+ while (len-- != 0) ReadByte(ls);
+ break;
+ }
+ }
+
+ return !ls->failed;
+}
+
+extern TileIndex _cur_tileloop_tile;
+static uint32 _old_cur_town_ctr;
+static const OldChunks main_chunk[] = {
+ OCL_ASSERT( 0 ),
+ OCL_VAR ( OC_FILE_U16 | OC_VAR_U32, 1, &_date ),
+ OCL_VAR ( OC_UINT16, 1, &_date_fract ),
+ OCL_NULL( 600 ), ///< TextEffects
+ OCL_VAR ( OC_UINT32, 2, &_random.state ),
+
+ OCL_ASSERT( 0x264 ),
+ OCL_CHUNK( 70, LoadOldTown ),
+ OCL_ASSERT( 0x1C18 ),
+ OCL_CHUNK(5000, LoadOldOrder ),
+ OCL_ASSERT( 0x4328 ),
+
+ OCL_CHUNK( 1, LoadOldAnimTileList ),
+ OCL_NULL( 4 ), ///< old end-of-order-list-pointer, no longer in use
+
+ OCL_CHUNK( 255, LoadOldDepot ),
+ OCL_ASSERT( 0x4B26 ),
+
+ OCL_VAR ( OC_UINT32, 1, &_old_cur_town_ctr ),
+ OCL_NULL( 2 ), ///< timer_counter, no longer in use
+ OCL_NULL( 2 ), ///< land_code, no longer in use
+
+ OCL_VAR ( OC_FILE_U16 | OC_VAR_U8, 1, &_age_cargo_skip_counter ),
+ OCL_VAR ( OC_UINT16, 1, &_tick_counter ),
+ OCL_VAR ( OC_TILE, 1, &_cur_tileloop_tile ),
+
+ OCL_CHUNK( 49, LoadOldPrice ),
+ OCL_CHUNK( 12, LoadOldCargoPaymentRate ),
+
+ OCL_ASSERT( 0x4CBA ),
+
+ OCL_CHUNK( 1, LoadOldMapPart1 ),
+
+ OCL_ASSERT( 0x48CBA ),
+
+ OCL_CHUNK(250, LoadOldStation ),
+ OCL_CHUNK( 90, LoadOldIndustry ),
+ OCL_CHUNK( 8, LoadOldCompany ),
+
+ OCL_ASSERT( 0x547F2 ),
+
+ OCL_CHUNK( 850, LoadOldVehicle ),
+
+ OCL_ASSERT( 0x6F0F2 ),
+
+ OCL_VAR ( OC_UINT8 | OC_DEREFERENCE_POINTER, 32 * 500, &_old_name_array ),
+
+ OCL_NULL( 0x2000 ), ///< Old hash-table, no longer in use
+
+ OCL_CHUNK( 40, LoadOldSign ),
+ OCL_CHUNK(256, LoadOldEngine ),
+
+ OCL_VAR ( OC_UINT16, 1, &_vehicle_id_ctr_day ),
+
+ OCL_CHUNK( 8, LoadOldSubsidy ),
+
+ OCL_VAR ( OC_FILE_U16 | OC_VAR_U32, 1, &_next_competitor_start ),
+ OCL_VAR ( OC_FILE_I16 | OC_VAR_I32, 1, &_saved_scrollpos_x ),
+ OCL_VAR ( OC_FILE_I16 | OC_VAR_I32, 1, &_saved_scrollpos_y ),
+ OCL_VAR ( OC_FILE_U16 | OC_VAR_U8, 1, &_saved_scrollpos_zoom ),
+
+ OCL_VAR ( OC_FILE_U32 | OC_VAR_I64, 1, &_economy.max_loan ),
+ OCL_VAR ( OC_FILE_U32 | OC_VAR_I64, 1, &_economy.max_loan_unround ),
+ OCL_VAR ( OC_INT16, 1, &_economy.fluct ),
+
+ OCL_VAR ( OC_UINT16, 1, &_disaster_delay ),
+
+ OCL_NULL( 144 ), ///< cargo-stuff, calculated in InitializeLandscapeVariables
+
+ OCL_CHUNK(256, LoadOldEngineName ),
+
+ OCL_NULL( 144 ), ///< AI cargo-stuff, calculated in InitializeLandscapeVariables
+ OCL_NULL( 2 ), ///< Company indexes of companies, no longer in use
+
+ OCL_VAR ( OC_FILE_U8 | OC_VAR_U16, 1, &_station_tick_ctr ),
+
+ OCL_VAR ( OC_UINT8, 1, &_settings_game.locale.currency ),
+ OCL_VAR ( OC_UINT8, 1, &_settings_game.locale.units ),
+ OCL_VAR ( OC_FILE_U8 | OC_VAR_U32, 1, &_cur_company_tick_index ),
+
+ OCL_NULL( 2 ), ///< Date stuff, calculated automatically
+ OCL_NULL( 8 ), ///< Company colors, calculated automatically
+
+ OCL_VAR ( OC_UINT8, 1, &_economy.infl_amount ),
+ OCL_VAR ( OC_UINT8, 1, &_economy.infl_amount_pr ),
+ OCL_VAR ( OC_UINT8, 1, &_economy.interest_rate ),
+ OCL_NULL( 1 ), // available airports
+ OCL_VAR ( OC_UINT8, 1, &_settings_game.vehicle.road_side ),
+ OCL_VAR ( OC_UINT8, 1, &_settings_game.game_creation.town_name ),
+
+ OCL_CHUNK( 1, LoadOldGameDifficulty ),
+
+ OCL_ASSERT( 0x77130 ),
+
+ OCL_VAR ( OC_UINT8, 1, &_settings_game.difficulty.diff_level ),
+ OCL_VAR ( OC_UINT8, 1, &_settings_game.game_creation.landscape ),
+ OCL_VAR ( OC_UINT8, 1, &_trees_tick_ctr ),
+
+ OCL_NULL( 1 ), ///< Custom vehicle types yes/no, no longer used
+ OCL_VAR ( OC_UINT8, 1, &_settings_game.game_creation.snow_line ),
+
+ OCL_NULL( 32 ), ///< new_industry_randtable, no longer used (because of new design)
+ OCL_NULL( 36 ), ///< cargo-stuff, calculated in InitializeLandscapeVariables
+
+ OCL_ASSERT( 0x77179 ),
+
+ OCL_CHUNK( 1, LoadOldMapPart2 ),
+
+ OCL_ASSERT( 0x97179 ),
+
+ /* Below any (if available) extra chunks from TTDPatch can follow */
+ OCL_CHUNK(1, LoadTTDPatchExtraChunks),
+
+ OCL_END()
+};
+
+static bool LoadOldMain(LoadgameState *ls)
+{
+ int i;
+
+ /* The first 49 is the name of the game + checksum, skip it */
+ fseek(ls->file, HEADER_SIZE, SEEK_SET);
+
+ DEBUG(oldloader, 3, "Reading main chunk...");
+ /* Load the biggest chunk */
+ _old_map3 = MallocT<byte>(OLD_MAP_SIZE * 2);
+ _old_vehicle_names = NULL;
+ if (!LoadChunk(ls, NULL, main_chunk)) {
+ DEBUG(oldloader, 0, "Loading failed");
+ free(_old_map3);
+ free(_old_vehicle_names);
+ return false;
+ }
+ DEBUG(oldloader, 3, "Done, converting game data...");
+
+ /* Fix some general stuff */
+ _settings_game.game_creation.landscape = _settings_game.game_creation.landscape & 0xF;
+
+ /* Remap some pointers */
+ _cur_town_ctr = REMAP_TOWN_IDX(_old_cur_town_ctr);
+
+ /* _old_map3 is changed in _map3_lo and _map3_hi */
+ for (i = 0; i < OLD_MAP_SIZE; i++) {
+ _m[i].m3 = _old_map3[i * 2];
+ _m[i].m4 = _old_map3[i * 2 + 1];
+ }
+
+ for (i = 0; i < OLD_MAP_SIZE; i ++) {
+ switch (GetTileType(i)) {
+ case MP_STATION:
+ _m[i].m4 = 0; // We do not understand this TTDP station mapping (yet)
+ switch (_m[i].m5) {
+ /* We have drive through stops at a totally different place */
+ case 0x53: case 0x54: _m[i].m5 += 170 - 0x53; break; // Bus drive through
+ case 0x57: case 0x58: _m[i].m5 += 168 - 0x57; break; // Truck drive through
+ case 0x55: case 0x56: _m[i].m5 += 170 - 0x55; break; // Bus tram stop
+ case 0x59: case 0x5A: _m[i].m5 += 168 - 0x59; break; // Truck tram stop
+ default: break;
+ }
+ break;
+
+ case MP_RAILWAY:
+ /* We save presignals different from TTDPatch, convert them */
+ if (GetRailTileType(i) == RAIL_TILE_SIGNALS) {
+ /* This byte is always zero in TTD for this type of tile */
+ if (_m[i].m4) /* Convert the presignals to our own format */
+ _m[i].m4 = (_m[i].m4 >> 1) & 7;
+ }
+ /* TTDPatch stores PBS things in L6 and all elsewhere; so we'll just
+ * clear it for ourselves and let OTTD's rebuild PBS itself */
+ _m[i].m4 &= 0xF; /* Only keep the lower four bits; upper four is PBS */
+ break;
+
+ case MP_WATER:
+ if (GetWaterClass(i) == 3) MakeRiver(i, Random());
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ /* Make sure the available engines are really available, otherwise
+ * we will get a "new vehicle"-spree. */
+ Engine *e;
+ FOR_ALL_ENGINES(e) {
+ if (_date >= (e->intro_date + 365)) {
+ e->flags = (e->flags & ~ENGINE_EXCLUSIVE_PREVIEW) | ENGINE_AVAILABLE;
+ e->company_avail = (CompanyMask)-1;
+ }
+ }
+
+ /* Fix the game to be compatible with OpenTTD */
+ FixOldTowns();
+ FixOldVehicles();
+
+ /* We have a new difficulty setting */
+ _settings_game.difficulty.town_council_tolerance = Clamp(_settings_game.difficulty.diff_level, 0, 2);
+
+ DEBUG(oldloader, 3, "Finished converting game data");
+ DEBUG(oldloader, 1, "TTD(Patch) savegame successfully converted");
+
+ free(_old_map3);
+ free(_old_vehicle_names);
+
+ return true;
+}
+
+bool LoadOldSaveGame(const char *file)
+{
+ LoadgameState ls;
+
+ DEBUG(oldloader, 3, "Trying to load a TTD(Patch) savegame");
+
+ InitLoading(&ls);
+
+ /* Open file */
+ ls.file = fopen(file, "rb");
+
+ if (ls.file == NULL) {
+ DEBUG(oldloader, 0, "Cannot open file '%s'", file);
+ return false;
+ }
+
+ /* Load the main chunk */
+ if (!LoadOldMain(&ls)) return false;
+
+ fclose(ls.file);
+
+ /* Some old TTD(Patch) savegames could have buoys at tile 0
+ * (without assigned station struct)
+ * MakeWater() can be used as long as sea has the same
+ * format as old savegames (eg. everything is zeroed) */
+ MakeWater(0);
+
+ _pause_game = 2;
+
+ return true;
+}
+
+void GetOldSaveGameName(char *title, const char *path, const char *file)
+{
+ char filename[MAX_PATH];
+ FILE *f;
+
+ snprintf(filename, lengthof(filename), "%s" PATHSEP "%s", path, file);
+ f = fopen(filename, "rb");
+ title[0] = '\0';
+ title[48] = '\0';
+
+ if (f == NULL) return;
+
+ if (fread(title, 1, 48, f) != 48) snprintf(title, 48, "Corrupt file");
+
+ fclose(f);
+}
diff --git a/src/saveload/order_sl.cpp b/src/saveload/order_sl.cpp
new file mode 100644
index 000000000..ef11056ed
--- /dev/null
+++ b/src/saveload/order_sl.cpp
@@ -0,0 +1,203 @@
+/* $Id$ */
+
+/** @file order_sl.cpp Code handling saving and loading of orders */
+
+#include "../stdafx.h"
+#include "../order_base.h"
+#include "../core/alloc_func.hpp"
+#include "../settings_type.h"
+
+#include "saveload.h"
+
+void Order::ConvertFromOldSavegame()
+{
+ uint8 old_flags = this->flags;
+ this->flags = 0;
+
+ /* First handle non-stop */
+ if (_settings_client.gui.sg_new_nonstop) {
+ /* OFB_NON_STOP */
+ this->SetNonStopType((old_flags & 8) ? ONSF_NO_STOP_AT_ANY_STATION : ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS);
+ } else {
+ this->SetNonStopType((old_flags & 8) ? ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS : ONSF_STOP_EVERYWHERE);
+ }
+
+ switch (this->GetType()) {
+ /* Only a few types need the other savegame conversions. */
+ case OT_GOTO_DEPOT: case OT_GOTO_STATION: case OT_LOADING: break;
+ default: return;
+ }
+
+ if (this->GetType() != OT_GOTO_DEPOT) {
+ /* Then the load flags */
+ if ((old_flags & 2) != 0) { // OFB_UNLOAD
+ this->SetLoadType(OLFB_NO_LOAD);
+ } else if ((old_flags & 4) == 0) { // !OFB_FULL_LOAD
+ this->SetLoadType(OLF_LOAD_IF_POSSIBLE);
+ } else {
+ this->SetLoadType(_settings_client.gui.sg_full_load_any ? OLF_FULL_LOAD_ANY : OLFB_FULL_LOAD);
+ }
+
+ /* Finally fix the unload flags */
+ if ((old_flags & 1) != 0) { // OFB_TRANSFER
+ this->SetUnloadType(OUFB_TRANSFER);
+ } else if ((old_flags & 2) != 0) { // OFB_UNLOAD
+ this->SetUnloadType(OUFB_UNLOAD);
+ } else {
+ this->SetUnloadType(OUF_UNLOAD_IF_POSSIBLE);
+ }
+ } else {
+ /* Then the depot action flags */
+ this->SetDepotActionType(((old_flags & 6) == 4) ? ODATFB_HALT : ODATF_SERVICE_ONLY);
+
+ /* Finally fix the depot type flags */
+ uint t = ((old_flags & 6) == 6) ? ODTFB_SERVICE : ODTF_MANUAL;
+ if ((old_flags & 2) != 0) t |= ODTFB_PART_OF_ORDERS;
+ this->SetDepotOrderType((OrderDepotTypeFlags)t);
+ }
+}
+
+/** Unpacks a order from savegames with version 4 and lower
+ * @param packed packed order
+ * @return unpacked order
+ */
+static Order UnpackVersion4Order(uint16 packed)
+{
+ return Order(GB(packed, 8, 8) << 16 | GB(packed, 4, 4) << 8 | GB(packed, 0, 4));
+}
+
+/** Unpacks a order from savegames made with TTD(Patch)
+ * @param packed packed order
+ * @return unpacked order
+ */
+Order UnpackOldOrder(uint16 packed)
+{
+ Order order = UnpackVersion4Order(packed);
+
+ /*
+ * Sanity check
+ * TTD stores invalid orders as OT_NOTHING with non-zero flags/station
+ */
+ if (!order.IsValid() && packed != 0) order.MakeDummy();
+
+ return order;
+}
+
+const SaveLoad *GetOrderDescription()
+{
+ static const SaveLoad _order_desc[] = {
+ SLE_VAR(Order, type, SLE_UINT8),
+ SLE_VAR(Order, flags, SLE_UINT8),
+ SLE_VAR(Order, dest, SLE_UINT16),
+ SLE_REF(Order, next, REF_ORDER),
+ SLE_CONDVAR(Order, refit_cargo, SLE_UINT8, 36, SL_MAX_VERSION),
+ SLE_CONDVAR(Order, refit_subtype, SLE_UINT8, 36, SL_MAX_VERSION),
+ SLE_CONDVAR(Order, wait_time, SLE_UINT16, 67, SL_MAX_VERSION),
+ SLE_CONDVAR(Order, travel_time, SLE_UINT16, 67, SL_MAX_VERSION),
+
+ /* Leftover from the minor savegame version stuff
+ * We will never use those free bytes, but we have to keep this line to allow loading of old savegames */
+ SLE_CONDNULL(10, 5, 35),
+ SLE_END()
+ };
+
+ return _order_desc;
+}
+
+static void Save_ORDR()
+{
+ Order *order;
+
+ FOR_ALL_ORDERS(order) {
+ SlSetArrayIndex(order->index);
+ SlObject(order, GetOrderDescription());
+ }
+}
+
+static void Load_ORDR()
+{
+ if (CheckSavegameVersionOldStyle(5, 2)) {
+ /* Version older than 5.2 did not have a ->next pointer. Convert them
+ (in the old days, the orderlist was 5000 items big) */
+ size_t len = SlGetFieldLength();
+ uint i;
+
+ if (CheckSavegameVersion(5)) {
+ /* Pre-version 5 had an other layout for orders
+ (uint16 instead of uint32) */
+ len /= sizeof(uint16);
+ uint16 *orders = MallocT<uint16>(len + 1);
+
+ SlArray(orders, len, SLE_UINT16);
+
+ for (i = 0; i < len; ++i) {
+ Order *order = new (i) Order();
+ order->AssignOrder(UnpackVersion4Order(orders[i]));
+ }
+
+ free(orders);
+ } else if (CheckSavegameVersionOldStyle(5, 2)) {
+ len /= sizeof(uint16);
+ uint16 *orders = MallocT<uint16>(len + 1);
+
+ SlArray(orders, len, SLE_UINT32);
+
+ for (i = 0; i < len; ++i) {
+ new (i) Order(orders[i]);
+ }
+
+ free(orders);
+ }
+
+ /* Update all the next pointer */
+ for (i = 1; i < len; ++i) {
+ /* The orders were built like this:
+ * While the order is valid, set the previous will get it's next pointer set
+ * We start with index 1 because no order will have the first in it's next pointer */
+ if (GetOrder(i)->IsValid())
+ GetOrder(i - 1)->next = GetOrder(i);
+ }
+ } else {
+ int index;
+
+ while ((index = SlIterateArray()) != -1) {
+ Order *order = new (index) Order();
+ SlObject(order, GetOrderDescription());
+ }
+ }
+}
+
+const SaveLoad *GetOrderListDescription()
+{
+ static const SaveLoad _orderlist_desc[] = {
+ SLE_REF(OrderList, first, REF_ORDER),
+ SLE_END()
+ };
+
+ return _orderlist_desc;
+}
+
+static void Save_ORDL()
+{
+ OrderList *list;
+
+ FOR_ALL_ORDER_LISTS(list) {
+ SlSetArrayIndex(list->index);
+ SlObject(list, GetOrderListDescription());
+ }
+}
+
+static void Load_ORDL()
+{
+ int index;
+
+ while ((index = SlIterateArray()) != -1) {
+ OrderList *list = new (index) OrderList();
+ SlObject(list, GetOrderListDescription());
+ }
+}
+
+extern const ChunkHandler _order_chunk_handlers[] = {
+ { 'ORDR', Save_ORDR, Load_ORDR, CH_ARRAY},
+ { 'ORDL', Save_ORDL, Load_ORDL, CH_ARRAY | CH_LAST},
+};
diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp
new file mode 100644
index 000000000..0a6585ce8
--- /dev/null
+++ b/src/saveload/saveload.cpp
@@ -0,0 +1,1899 @@
+/* $Id$ */
+
+/** @file saveload.cpp
+ * All actions handling saving and loading goes on in this file. The general actions
+ * are as follows for saving a game (loading is analogous):
+ * <ol>
+ * <li>initialize the writer by creating a temporary memory-buffer for it
+ * <li>go through all to-be saved elements, each 'chunk' (ChunkHandler) prefixed by a label
+ * <li>use their description array (SaveLoad) to know what elements to save and in what version
+ * of the game it was active (used when loading)
+ * <li>write all data byte-by-byte to the temporary buffer so it is endian-safe
+ * <li>when the buffer is full; flush it to the output (eg save to file) (_sl.buf, _sl.bufp, _sl.bufe)
+ * <li>repeat this until everything is done, and flush any remaining output to file
+ * </ol>
+ */
+#include "../stdafx.h"
+#include "../openttd.h"
+#include "../debug.h"
+#include "../station_base.h"
+#include "../thread.h"
+#include "../town.h"
+#include "../network/network.h"
+#include "../variables.h"
+#include "../window_func.h"
+#include "../strings_func.h"
+#include "../gfx_func.h"
+#include "../core/alloc_func.hpp"
+#include "../functions.h"
+#include "../core/endian_func.hpp"
+#include "../vehicle_base.h"
+#include "../company_func.h"
+#include "../date_func.h"
+#include "../autoreplace_base.h"
+#include "../statusbar_gui.h"
+#include "../fileio_func.h"
+#include "../gamelog.h"
+
+#include "table/strings.h"
+
+#include "saveload.h"
+#include "saveload_internal.h"
+
+#include <list>
+
+extern const uint16 SAVEGAME_VERSION = 105;
+
+SavegameType _savegame_type; ///< type of savegame we are loading
+
+uint32 _ttdp_version; ///< version of TTDP savegame (if applicable)
+uint16 _sl_version; ///< the major savegame version identifier
+byte _sl_minor_version; ///< the minor savegame version, DO NOT USE!
+
+typedef void WriterProc(size_t len);
+typedef size_t ReaderProc();
+
+/** The saveload struct, containing reader-writer functions, bufffer, version, etc. */
+static struct {
+ bool save; ///< are we doing a save or a load atm. True when saving
+ byte need_length; ///< ???
+ byte block_mode; ///< ???
+ bool error; ///< did an error occur or not
+
+ size_t obj_len; ///< the length of the current object we are busy with
+ int array_index, last_array_index; ///< in the case of an array, the current and last positions
+
+ size_t offs_base; ///< the offset in number of bytes since we started writing data (eg uncompressed savegame size)
+
+ WriterProc *write_bytes; ///< savegame writer function
+ ReaderProc *read_bytes; ///< savegame loader function
+
+ const ChunkHandler* const *chs; ///< the chunk of data that is being processed atm (vehicles, signs, etc.)
+
+ /* When saving/loading savegames, they are always saved to a temporary memory-place
+ * to be flushed to file (save) or to final place (load) when full. */
+ byte *bufp, *bufe; ///< bufp(ointer) gives the current position in the buffer bufe(nd) gives the end of the buffer
+
+ /* these 3 may be used by compressor/decompressors. */
+ byte *buf; ///< pointer to temporary memory to read/write, initialized by SaveLoadFormat->initread/write
+ byte *buf_ori; ///< pointer to the original memory location of buf, used to free it afterwards
+ uint bufsize; ///< the size of the temporary memory *buf
+ FILE *fh; ///< the file from which is read or written to
+
+ void (*excpt_uninit)(); ///< the function to execute on any encountered error
+ StringID error_str; ///< the translateable error message to show
+ char *extra_msg; ///< the error message
+} _sl;
+
+
+enum NeedLengthValues {NL_NONE = 0, NL_WANTLENGTH = 1, NL_CALCLENGTH = 2};
+
+/** Error handler, calls longjmp to simulate an exception.
+ * @todo this was used to have a central place to handle errors, but it is
+ * pretty ugly, and seriously interferes with any multithreaded approaches */
+static void NORETURN SlError(StringID string, const char *extra_msg = NULL)
+{
+ _sl.error_str = string;
+ free(_sl.extra_msg);
+ _sl.extra_msg = (extra_msg == NULL) ? NULL : strdup(extra_msg);
+ throw std::exception();
+}
+
+typedef void (*AsyncSaveFinishProc)();
+static AsyncSaveFinishProc _async_save_finish = NULL;
+static ThreadObject *_save_thread;
+
+/**
+ * Called by save thread to tell we finished saving.
+ */
+static void SetAsyncSaveFinish(AsyncSaveFinishProc proc)
+{
+ if (_exit_game) return;
+ while (_async_save_finish != NULL) CSleep(10);
+
+ _async_save_finish = proc;
+}
+
+/**
+ * Handle async save finishes.
+ */
+void ProcessAsyncSaveFinish()
+{
+ if (_async_save_finish == NULL) return;
+
+ _async_save_finish();
+
+ _async_save_finish = NULL;
+
+ if (_save_thread != NULL) {
+ _save_thread->Join();
+ delete _save_thread;
+ _save_thread = NULL;
+ }
+}
+
+/**
+ * Fill the input buffer by reading from the file with the given reader
+ */
+static void SlReadFill()
+{
+ size_t len = _sl.read_bytes();
+ if (len == 0) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Unexpected end of chunk");
+
+ _sl.bufp = _sl.buf;
+ _sl.bufe = _sl.buf + len;
+ _sl.offs_base += len;
+}
+
+static inline size_t SlGetOffs() {return _sl.offs_base - (_sl.bufe - _sl.bufp);}
+
+/** Return the size in bytes of a certain type of normal/atomic variable
+ * as it appears in memory. See VarTypes
+ * @param conv VarType type of variable that is used for calculating the size
+ * @return Return the size of this type in bytes */
+static inline byte SlCalcConvMemLen(VarType conv)
+{
+ static const byte conv_mem_size[] = {1, 1, 1, 2, 2, 4, 4, 8, 8, 0};
+ byte length = GB(conv, 4, 4);
+ assert(length < lengthof(conv_mem_size));
+ return conv_mem_size[length];
+}
+
+/** Return the size in bytes of a certain type of normal/atomic variable
+ * as it appears in a saved game. See VarTypes
+ * @param conv VarType type of variable that is used for calculating the size
+ * @return Return the size of this type in bytes */
+static inline byte SlCalcConvFileLen(VarType conv)
+{
+ static const byte conv_file_size[] = {1, 1, 2, 2, 4, 4, 8, 8, 2};
+ byte length = GB(conv, 0, 4);
+ assert(length < lengthof(conv_file_size));
+ return conv_file_size[length];
+}
+
+/** Return the size in bytes of a reference (pointer) */
+static inline size_t SlCalcRefLen() {return CheckSavegameVersion(69) ? 2 : 4;}
+
+/** Flush the output buffer by writing to disk with the given reader.
+ * If the buffer pointer has not yet been set up, set it up now. Usually
+ * only called when the buffer is full, or there is no more data to be processed
+ */
+static void SlWriteFill()
+{
+ /* flush the buffer to disk (the writer) */
+ if (_sl.bufp != NULL) {
+ uint len = _sl.bufp - _sl.buf;
+ _sl.offs_base += len;
+ if (len) _sl.write_bytes(len);
+ }
+
+ /* All the data from the buffer has been written away, rewind to the beginning
+ * to start reading in more data */
+ _sl.bufp = _sl.buf;
+ _sl.bufe = _sl.buf + _sl.bufsize;
+}
+
+/** Read in a single byte from file. If the temporary buffer is full,
+ * flush it to its final destination
+ * @return return the read byte from file
+ */
+static inline byte SlReadByteInternal()
+{
+ if (_sl.bufp == _sl.bufe) SlReadFill();
+ return *_sl.bufp++;
+}
+
+/** Wrapper for SlReadByteInternal */
+byte SlReadByte() {return SlReadByteInternal();}
+
+/** Write away a single byte from memory. If the temporary buffer is full,
+ * flush it to its destination (file)
+ * @param b the byte that is currently written
+ */
+static inline void SlWriteByteInternal(byte b)
+{
+ if (_sl.bufp == _sl.bufe) SlWriteFill();
+ *_sl.bufp++ = b;
+}
+
+/** Wrapper for SlWriteByteInternal */
+void SlWriteByte(byte b) {SlWriteByteInternal(b);}
+
+static inline int SlReadUint16()
+{
+ int x = SlReadByte() << 8;
+ return x | SlReadByte();
+}
+
+static inline uint32 SlReadUint32()
+{
+ uint32 x = SlReadUint16() << 16;
+ return x | SlReadUint16();
+}
+
+static inline uint64 SlReadUint64()
+{
+ uint32 x = SlReadUint32();
+ uint32 y = SlReadUint32();
+ return (uint64)x << 32 | y;
+}
+
+static inline void SlWriteUint16(uint16 v)
+{
+ SlWriteByte(GB(v, 8, 8));
+ SlWriteByte(GB(v, 0, 8));
+}
+
+static inline void SlWriteUint32(uint32 v)
+{
+ SlWriteUint16(GB(v, 16, 16));
+ SlWriteUint16(GB(v, 0, 16));
+}
+
+static inline void SlWriteUint64(uint64 x)
+{
+ SlWriteUint32((uint32)(x >> 32));
+ SlWriteUint32((uint32)x);
+}
+
+/**
+ * Read in the header descriptor of an object or an array.
+ * If the highest bit is set (7), then the index is bigger than 127
+ * elements, so use the next byte to read in the real value.
+ * The actual value is then both bytes added with the first shifted
+ * 8 bits to the left, and dropping the highest bit (which only indicated a big index).
+ * x = ((x & 0x7F) << 8) + SlReadByte();
+ * @return Return the value of the index
+ */
+static uint SlReadSimpleGamma()
+{
+ uint i = SlReadByte();
+ if (HasBit(i, 7)) {
+ i &= ~0x80;
+ if (HasBit(i, 6)) {
+ i &= ~0x40;
+ if (HasBit(i, 5)) {
+ i &= ~0x20;
+ if (HasBit(i, 4))
+ SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Unsupported gamma");
+ i = (i << 8) | SlReadByte();
+ }
+ i = (i << 8) | SlReadByte();
+ }
+ i = (i << 8) | SlReadByte();
+ }
+ return i;
+}
+
+/**
+ * Write the header descriptor of an object or an array.
+ * If the element is bigger than 127, use 2 bytes for saving
+ * and use the highest byte of the first written one as a notice
+ * that the length consists of 2 bytes, etc.. like this:
+ * 0xxxxxxx
+ * 10xxxxxx xxxxxxxx
+ * 110xxxxx xxxxxxxx xxxxxxxx
+ * 1110xxxx xxxxxxxx xxxxxxxx xxxxxxxx
+ * @param i Index being written
+ */
+
+static void SlWriteSimpleGamma(size_t i)
+{
+ if (i >= (1 << 7)) {
+ if (i >= (1 << 14)) {
+ if (i >= (1 << 21)) {
+ assert(i < (1 << 28));
+ SlWriteByte((byte)(0xE0 | (i >> 24)));
+ SlWriteByte((byte)(i >> 16));
+ } else {
+ SlWriteByte((byte)(0xC0 | (i >> 16)));
+ }
+ SlWriteByte((byte)(i >> 8));
+ } else {
+ SlWriteByte((byte)(0x80 | (i >> 8)));
+ }
+ }
+ SlWriteByte((byte)i);
+}
+
+/** Return how many bytes used to encode a gamma value */
+static inline uint SlGetGammaLength(size_t i)
+{
+ return 1 + (i >= (1 << 7)) + (i >= (1 << 14)) + (i >= (1 << 21));
+}
+
+static inline uint SlReadSparseIndex() {return SlReadSimpleGamma();}
+static inline void SlWriteSparseIndex(uint index) {SlWriteSimpleGamma(index);}
+
+static inline uint SlReadArrayLength() {return SlReadSimpleGamma();}
+static inline void SlWriteArrayLength(size_t length) {SlWriteSimpleGamma(length);}
+static inline uint SlGetArrayLength(size_t length) {return SlGetGammaLength(length);}
+
+void SlSetArrayIndex(uint index)
+{
+ _sl.need_length = NL_WANTLENGTH;
+ _sl.array_index = index;
+}
+
+static size_t _next_offs;
+
+/**
+ * Iterate through the elements of an array and read the whole thing
+ * @return The index of the object, or -1 if we have reached the end of current block
+ */
+int SlIterateArray()
+{
+ int index;
+
+ /* After reading in the whole array inside the loop
+ * we must have read in all the data, so we must be at end of current block. */
+ if (_next_offs != 0 && SlGetOffs() != _next_offs) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Invalid chunk size");
+
+ while (true) {
+ uint length = SlReadArrayLength();
+ if (length == 0) {
+ _next_offs = 0;
+ return -1;
+ }
+
+ _sl.obj_len = --length;
+ _next_offs = SlGetOffs() + length;
+
+ switch (_sl.block_mode) {
+ case CH_SPARSE_ARRAY: index = (int)SlReadSparseIndex(); break;
+ case CH_ARRAY: index = _sl.array_index++; break;
+ default:
+ DEBUG(sl, 0, "SlIterateArray error");
+ return -1; // error
+ }
+
+ if (length != 0) return index;
+ }
+}
+
+/**
+ * Sets the length of either a RIFF object or the number of items in an array.
+ * This lets us load an object or an array of arbitrary size
+ * @param length The length of the sought object/array
+ */
+void SlSetLength(size_t length)
+{
+ assert(_sl.save);
+
+ switch (_sl.need_length) {
+ case NL_WANTLENGTH:
+ _sl.need_length = NL_NONE;
+ switch (_sl.block_mode) {
+ case CH_RIFF:
+ /* Ugly encoding of >16M RIFF chunks
+ * The lower 24 bits are normal
+ * The uppermost 4 bits are bits 24:27 */
+ assert(length < (1 << 28));
+ SlWriteUint32((uint32)((length & 0xFFFFFF) | ((length >> 24) << 28)));
+ break;
+ case CH_ARRAY:
+ assert(_sl.last_array_index <= _sl.array_index);
+ while (++_sl.last_array_index <= _sl.array_index)
+ SlWriteArrayLength(1);
+ SlWriteArrayLength(length + 1);
+ break;
+ case CH_SPARSE_ARRAY:
+ SlWriteArrayLength(length + 1 + SlGetArrayLength(_sl.array_index)); // Also include length of sparse index.
+ SlWriteSparseIndex(_sl.array_index);
+ break;
+ default: NOT_REACHED();
+ } break;
+ case NL_CALCLENGTH:
+ _sl.obj_len += (int)length;
+ break;
+ }
+}
+
+/**
+ * Save/Load bytes. These do not need to be converted to Little/Big Endian
+ * so directly write them or read them to/from file
+ * @param ptr The source or destination of the object being manipulated
+ * @param length number of bytes this fast CopyBytes lasts
+ */
+static void SlCopyBytes(void *ptr, size_t length)
+{
+ byte *p = (byte*)ptr;
+
+ if (_sl.save) {
+ for (; length != 0; length--) {SlWriteByteInternal(*p++);}
+ } else {
+ for (; length != 0; length--) {*p++ = SlReadByteInternal();}
+ }
+}
+
+/** Read in bytes from the file/data structure but don't do
+ * anything with them, discarding them in effect
+ * @param length The amount of bytes that is being treated this way
+ */
+static inline void SlSkipBytes(size_t length)
+{
+ for (; length != 0; length--) SlReadByte();
+}
+
+/* Get the length of the current object */
+size_t SlGetFieldLength() {return _sl.obj_len;}
+
+/** Return a signed-long version of the value of a setting
+ * @param ptr pointer to the variable
+ * @param conv type of variable, can be a non-clean
+ * type, eg one with other flags because it is parsed
+ * @return returns the value of the pointer-setting */
+int64 ReadValue(const void *ptr, VarType conv)
+{
+ switch (GetVarMemType(conv)) {
+ case SLE_VAR_BL: return (*(bool*)ptr != 0);
+ case SLE_VAR_I8: return *(int8* )ptr;
+ case SLE_VAR_U8: return *(byte* )ptr;
+ case SLE_VAR_I16: return *(int16* )ptr;
+ case SLE_VAR_U16: return *(uint16*)ptr;
+ case SLE_VAR_I32: return *(int32* )ptr;
+ case SLE_VAR_U32: return *(uint32*)ptr;
+ case SLE_VAR_I64: return *(int64* )ptr;
+ case SLE_VAR_U64: return *(uint64*)ptr;
+ case SLE_VAR_NULL:return 0;
+ default: NOT_REACHED();
+ }
+
+ /* useless, but avoids compiler warning this way */
+ return 0;
+}
+
+/** Write the value of a setting
+ * @param ptr pointer to the variable
+ * @param conv type of variable, can be a non-clean type, eg
+ * with other flags. It is parsed upon read
+ * @param val the new value being given to the variable */
+void WriteValue(void *ptr, VarType conv, int64 val)
+{
+ switch (GetVarMemType(conv)) {
+ case SLE_VAR_BL: *(bool *)ptr = (val != 0); break;
+ case SLE_VAR_I8: *(int8 *)ptr = val; break;
+ case SLE_VAR_U8: *(byte *)ptr = val; break;
+ case SLE_VAR_I16: *(int16 *)ptr = val; break;
+ case SLE_VAR_U16: *(uint16*)ptr = val; break;
+ case SLE_VAR_I32: *(int32 *)ptr = val; break;
+ case SLE_VAR_U32: *(uint32*)ptr = val; break;
+ case SLE_VAR_I64: *(int64 *)ptr = val; break;
+ case SLE_VAR_U64: *(uint64*)ptr = val; break;
+ case SLE_VAR_NAME: *(char**)ptr = CopyFromOldName(val); break;
+ case SLE_VAR_NULL: break;
+ default: NOT_REACHED();
+ }
+}
+
+/**
+ * Handle all conversion and typechecking of variables here.
+ * In the case of saving, read in the actual value from the struct
+ * and then write them to file, endian safely. Loading a value
+ * goes exactly the opposite way
+ * @param ptr The object being filled/read
+ * @param conv VarType type of the current element of the struct
+ */
+static void SlSaveLoadConv(void *ptr, VarType conv)
+{
+ int64 x = 0;
+
+ if (_sl.save) { // SAVE values
+ /* Read a value from the struct. These ARE endian safe. */
+ x = ReadValue(ptr, conv);
+
+ /* Write the value to the file and check if its value is in the desired range */
+ switch (GetVarFileType(conv)) {
+ case SLE_FILE_I8: assert(x >= -128 && x <= 127); SlWriteByte(x);break;
+ case SLE_FILE_U8: assert(x >= 0 && x <= 255); SlWriteByte(x);break;
+ case SLE_FILE_I16:assert(x >= -32768 && x <= 32767); SlWriteUint16(x);break;
+ case SLE_FILE_STRINGID:
+ case SLE_FILE_U16:assert(x >= 0 && x <= 65535); SlWriteUint16(x);break;
+ case SLE_FILE_I32:
+ case SLE_FILE_U32: SlWriteUint32((uint32)x);break;
+ case SLE_FILE_I64:
+ case SLE_FILE_U64: SlWriteUint64(x);break;
+ default: NOT_REACHED();
+ }
+ } else { // LOAD values
+
+ /* Read a value from the file */
+ switch (GetVarFileType(conv)) {
+ case SLE_FILE_I8: x = (int8 )SlReadByte(); break;
+ case SLE_FILE_U8: x = (byte )SlReadByte(); break;
+ case SLE_FILE_I16: x = (int16 )SlReadUint16(); break;
+ case SLE_FILE_U16: x = (uint16)SlReadUint16(); break;
+ case SLE_FILE_I32: x = (int32 )SlReadUint32(); break;
+ case SLE_FILE_U32: x = (uint32)SlReadUint32(); break;
+ case SLE_FILE_I64: x = (int64 )SlReadUint64(); break;
+ case SLE_FILE_U64: x = (uint64)SlReadUint64(); break;
+ case SLE_FILE_STRINGID: x = RemapOldStringID((uint16)SlReadUint16()); break;
+ default: NOT_REACHED();
+ }
+
+ /* Write The value to the struct. These ARE endian safe. */
+ WriteValue(ptr, conv, x);
+ }
+}
+
+/** Calculate the net length of a string. This is in almost all cases
+ * just strlen(), but if the string is not properly terminated, we'll
+ * resort to the maximum length of the buffer.
+ * @param ptr pointer to the stringbuffer
+ * @param length maximum length of the string (buffer). If -1 we don't care
+ * about a maximum length, but take string length as it is.
+ * @return return the net length of the string */
+static inline size_t SlCalcNetStringLen(const char *ptr, size_t length)
+{
+ if (ptr == NULL) return 0;
+ return min(strlen(ptr), length - 1);
+}
+
+/** Calculate the gross length of the string that it
+ * will occupy in the savegame. This includes the real length, returned
+ * by SlCalcNetStringLen and the length that the index will occupy.
+ * @param ptr pointer to the stringbuffer
+ * @param length maximum length of the string (buffer size, etc.)
+ * @param conv type of data been used
+ * @return return the gross length of the string */
+static inline size_t SlCalcStringLen(const void *ptr, size_t length, VarType conv)
+{
+ size_t len;
+ const char *str;
+
+ switch (GetVarMemType(conv)) {
+ default: NOT_REACHED();
+ case SLE_VAR_STR:
+ case SLE_VAR_STRQ:
+ str = *(const char**)ptr;
+ len = SIZE_MAX;
+ break;
+ case SLE_VAR_STRB:
+ case SLE_VAR_STRBQ:
+ str = (const char*)ptr;
+ len = length;
+ break;
+ }
+
+ len = SlCalcNetStringLen(str, len);
+ return len + SlGetArrayLength(len); // also include the length of the index
+}
+
+/**
+ * Save/Load a string.
+ * @param ptr the string being manipulated
+ * @param length of the string (full length)
+ * @param conv must be SLE_FILE_STRING */
+static void SlString(void *ptr, size_t length, VarType conv)
+{
+ size_t len;
+
+ if (_sl.save) { // SAVE string
+ switch (GetVarMemType(conv)) {
+ default: NOT_REACHED();
+ case SLE_VAR_STRB:
+ case SLE_VAR_STRBQ:
+ len = SlCalcNetStringLen((char*)ptr, length);
+ break;
+ case SLE_VAR_STR:
+ case SLE_VAR_STRQ:
+ ptr = *(char**)ptr;
+ len = SlCalcNetStringLen((char*)ptr, SIZE_MAX);
+ break;
+ }
+
+ SlWriteArrayLength(len);
+ SlCopyBytes(ptr, len);
+ } else { // LOAD string
+ len = SlReadArrayLength();
+
+ switch (GetVarMemType(conv)) {
+ default: NOT_REACHED();
+ case SLE_VAR_STRB:
+ case SLE_VAR_STRBQ:
+ if (len >= length) {
+ DEBUG(sl, 1, "String length in savegame is bigger than buffer, truncating");
+ SlCopyBytes(ptr, length);
+ SlSkipBytes(len - length);
+ len = length - 1;
+ } else {
+ SlCopyBytes(ptr, len);
+ }
+ break;
+ case SLE_VAR_STR:
+ case SLE_VAR_STRQ: // Malloc'd string, free previous incarnation, and allocate
+ free(*(char**)ptr);
+ if (len == 0) {
+ *(char**)ptr = NULL;
+ } else {
+ *(char**)ptr = MallocT<char>(len + 1); // terminating '\0'
+ ptr = *(char**)ptr;
+ SlCopyBytes(ptr, len);
+ }
+ break;
+ }
+
+ ((char*)ptr)[len] = '\0'; // properly terminate the string
+ }
+}
+
+/**
+ * Return the size in bytes of a certain type of atomic array
+ * @param length The length of the array counted in elements
+ * @param conv VarType type of the variable that is used in calculating the size
+ */
+static inline size_t SlCalcArrayLen(size_t length, VarType conv)
+{
+ return SlCalcConvFileLen(conv) * length;
+}
+
+/**
+ * Save/Load an array.
+ * @param array The array being manipulated
+ * @param length The length of the array in elements
+ * @param conv VarType type of the atomic array (int, byte, uint64, etc.)
+ */
+void SlArray(void *array, size_t length, VarType conv)
+{
+ /* Automatically calculate the length? */
+ if (_sl.need_length != NL_NONE) {
+ SlSetLength(SlCalcArrayLen(length, conv));
+ /* Determine length only? */
+ if (_sl.need_length == NL_CALCLENGTH) return;
+ }
+
+ /* NOTICE - handle some buggy stuff, in really old versions everything was saved
+ * as a byte-type. So detect this, and adjust array size accordingly */
+ if (!_sl.save && _sl_version == 0) {
+ /* all arrays except difficulty settings */
+ if (conv == SLE_INT16 || conv == SLE_UINT16 || conv == SLE_STRINGID ||
+ conv == SLE_INT32 || conv == SLE_UINT32) {
+ SlCopyBytes(array, length * SlCalcConvFileLen(conv));
+ return;
+ }
+ /* used for conversion of Money 32bit->64bit */
+ if (conv == (SLE_FILE_I32 | SLE_VAR_I64)) {
+ for (uint i = 0; i < length; i++) {
+ ((int64*)array)[i] = (int32)BSWAP32(SlReadUint32());
+ }
+ return;
+ }
+ }
+
+ /* If the size of elements is 1 byte both in file and memory, no special
+ * conversion is needed, use specialized copy-copy function to speed up things */
+ if (conv == SLE_INT8 || conv == SLE_UINT8) {
+ SlCopyBytes(array, length);
+ } else {
+ byte *a = (byte*)array;
+ byte mem_size = SlCalcConvMemLen(conv);
+
+ for (; length != 0; length --) {
+ SlSaveLoadConv(a, conv);
+ a += mem_size; // get size
+ }
+ }
+}
+
+
+static uint ReferenceToInt(const void* obj, SLRefType rt);
+static void* IntToReference(uint index, SLRefType rt);
+
+
+/**
+ * Return the size in bytes of a list
+ * @param list The std::list to find the size of
+ */
+static inline size_t SlCalcListLen(const void *list)
+{
+ std::list<void *> *l = (std::list<void *> *) list;
+
+ int type_size = CheckSavegameVersion(69) ? 2 : 4;
+ /* Each entry is saved as type_size bytes, plus type_size bytes are used for the length
+ * of the list */
+ return l->size() * type_size + type_size;
+}
+
+
+/**
+ * Save/Load a list.
+ * @param list The list being manipulated
+ * @param conv SLRefType type of the list (Vehicle *, Station *, etc)
+ */
+void SlList(void *list, SLRefType conv)
+{
+ /* Automatically calculate the length? */
+ if (_sl.need_length != NL_NONE) {
+ SlSetLength(SlCalcListLen(list));
+ /* Determine length only? */
+ if (_sl.need_length == NL_CALCLENGTH) return;
+ }
+
+ std::list<void *> *l = (std::list<void *> *) list;
+
+ if (_sl.save) {
+ SlWriteUint32((uint32)l->size());
+
+ std::list<void *>::iterator iter;
+ for (iter = l->begin(); iter != l->end(); ++iter) {
+ void *ptr = *iter;
+ SlWriteUint32(ReferenceToInt(ptr, conv));
+ }
+ } else {
+ uint length = CheckSavegameVersion(69) ? SlReadUint16() : SlReadUint32();
+
+ /* Load each reference and push to the end of the list */
+ for (uint i = 0; i < length; i++) {
+ void *ptr = IntToReference(CheckSavegameVersion(69) ? SlReadUint16() : SlReadUint32(), conv);
+ l->push_back(ptr);
+ }
+ }
+}
+
+
+/** Are we going to save this object or not? */
+static inline bool SlIsObjectValidInSavegame(const SaveLoad *sld)
+{
+ if (_sl_version < sld->version_from || _sl_version > sld->version_to) return false;
+ if (sld->conv & SLF_SAVE_NO) return false;
+
+ return true;
+}
+
+/** Are we going to load this variable when loading a savegame or not?
+ * @note If the variable is skipped it is skipped in the savegame
+ * bytestream itself as well, so there is no need to skip it somewhere else */
+static inline bool SlSkipVariableOnLoad(const SaveLoad *sld)
+{
+ if ((sld->conv & SLF_NETWORK_NO) && !_sl.save && _networking && !_network_server) {
+ SlSkipBytes(SlCalcConvMemLen(sld->conv) * sld->length);
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Calculate the size of an object.
+ * @param object to be measured
+ * @param sld The SaveLoad description of the object so we know how to manipulate it
+ * @return size of given objetc
+ */
+size_t SlCalcObjLength(const void *object, const SaveLoad *sld)
+{
+ size_t length = 0;
+
+ /* Need to determine the length and write a length tag. */
+ for (; sld->cmd != SL_END; sld++) {
+ length += SlCalcObjMemberLength(object, sld);
+ }
+ return length;
+}
+
+size_t SlCalcObjMemberLength(const void *object, const SaveLoad *sld)
+{
+ assert(_sl.save);
+
+ switch (sld->cmd) {
+ case SL_VAR:
+ case SL_REF:
+ case SL_ARR:
+ case SL_STR:
+ case SL_LST:
+ /* CONDITIONAL saveload types depend on the savegame version */
+ if (!SlIsObjectValidInSavegame(sld)) break;
+
+ switch (sld->cmd) {
+ case SL_VAR: return SlCalcConvFileLen(sld->conv);
+ case SL_REF: return SlCalcRefLen();
+ case SL_ARR: return SlCalcArrayLen(sld->length, sld->conv);
+ case SL_STR: return SlCalcStringLen(GetVariableAddress(object, sld), sld->length, sld->conv);
+ case SL_LST: return SlCalcListLen(GetVariableAddress(object, sld));
+ default: NOT_REACHED();
+ }
+ break;
+ case SL_WRITEBYTE: return 1; // a byte is logically of size 1
+ case SL_VEH_INCLUDE: return SlCalcObjLength(object, GetVehicleDescription(VEH_END));
+ default: NOT_REACHED();
+ }
+ return 0;
+}
+
+
+bool SlObjectMember(void *ptr, const SaveLoad *sld)
+{
+ VarType conv = GB(sld->conv, 0, 8);
+ switch (sld->cmd) {
+ case SL_VAR:
+ case SL_REF:
+ case SL_ARR:
+ case SL_STR:
+ case SL_LST:
+ /* CONDITIONAL saveload types depend on the savegame version */
+ if (!SlIsObjectValidInSavegame(sld)) return false;
+ if (SlSkipVariableOnLoad(sld)) return false;
+
+ switch (sld->cmd) {
+ case SL_VAR: SlSaveLoadConv(ptr, conv); break;
+ case SL_REF: // Reference variable, translate
+ if (_sl.save) {
+ SlWriteUint32(ReferenceToInt(*(void**)ptr, (SLRefType)conv));
+ } else {
+ *(void**)ptr = IntToReference(CheckSavegameVersion(69) ? SlReadUint16() : SlReadUint32(), (SLRefType)conv);
+ }
+ break;
+ case SL_ARR: SlArray(ptr, sld->length, conv); break;
+ case SL_STR: SlString(ptr, sld->length, conv); break;
+ case SL_LST: SlList(ptr, (SLRefType)conv); break;
+ default: NOT_REACHED();
+ }
+ break;
+
+ /* SL_WRITEBYTE translates a value of a variable to another one upon
+ * saving or loading.
+ * XXX - variable renaming abuse
+ * game_value: the value of the variable ingame is abused by sld->version_from
+ * file_value: the value of the variable in the savegame is abused by sld->version_to */
+ case SL_WRITEBYTE:
+ if (_sl.save) {
+ SlWriteByte(sld->version_to);
+ } else {
+ *(byte*)ptr = sld->version_from;
+ }
+ break;
+
+ /* SL_VEH_INCLUDE loads common code for vehicles */
+ case SL_VEH_INCLUDE:
+ SlObject(ptr, GetVehicleDescription(VEH_END));
+ break;
+ default: NOT_REACHED();
+ }
+ return true;
+}
+
+/**
+ * Main SaveLoad function.
+ * @param object The object that is being saved or loaded
+ * @param sld The SaveLoad description of the object so we know how to manipulate it
+ */
+void SlObject(void *object, const SaveLoad *sld)
+{
+ /* Automatically calculate the length? */
+ if (_sl.need_length != NL_NONE) {
+ SlSetLength(SlCalcObjLength(object, sld));
+ if (_sl.need_length == NL_CALCLENGTH) return;
+ }
+
+ for (; sld->cmd != SL_END; sld++) {
+ void *ptr = sld->global ? sld->address : GetVariableAddress(object, sld);
+ SlObjectMember(ptr, sld);
+ }
+}
+
+/**
+ * Save or Load (a list of) global variables
+ * @param sldg The global variable that is being loaded or saved
+ */
+void SlGlobList(const SaveLoadGlobVarList *sldg)
+{
+ SlObject(NULL, (const SaveLoad*)sldg);
+}
+
+/**
+ * Do something of which I have no idea what it is :P
+ * @param proc The callback procedure that is called
+ * @param arg The variable that will be used for the callback procedure
+ */
+void SlAutolength(AutolengthProc *proc, void *arg)
+{
+ size_t offs;
+
+ assert(_sl.save);
+
+ /* Tell it to calculate the length */
+ _sl.need_length = NL_CALCLENGTH;
+ _sl.obj_len = 0;
+ proc(arg);
+
+ /* Setup length */
+ _sl.need_length = NL_WANTLENGTH;
+ SlSetLength(_sl.obj_len);
+
+ offs = SlGetOffs() + _sl.obj_len;
+
+ /* And write the stuff */
+ proc(arg);
+
+ if (offs != SlGetOffs()) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Invalid chunk size");
+}
+
+/**
+ * Load a chunk of data (eg vehicles, stations, etc.)
+ * @param ch The chunkhandler that will be used for the operation
+ */
+static void SlLoadChunk(const ChunkHandler *ch)
+{
+ byte m = SlReadByte();
+ size_t len;
+ size_t endoffs;
+
+ _sl.block_mode = m;
+ _sl.obj_len = 0;
+
+ switch (m) {
+ case CH_ARRAY:
+ _sl.array_index = 0;
+ ch->load_proc();
+ break;
+ case CH_SPARSE_ARRAY:
+ ch->load_proc();
+ break;
+ default:
+ if ((m & 0xF) == CH_RIFF) {
+ /* Read length */
+ len = (SlReadByte() << 16) | ((m >> 4) << 24);
+ len += SlReadUint16();
+ _sl.obj_len = len;
+ endoffs = SlGetOffs() + len;
+ ch->load_proc();
+ if (SlGetOffs() != endoffs) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Invalid chunk size");
+ } else {
+ SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Invalid chunk type");
+ }
+ break;
+ }
+}
+
+/* Stub Chunk handlers to only calculate length and do nothing else */
+static ChunkSaveLoadProc *_tmp_proc_1;
+static inline void SlStubSaveProc2(void *arg) {_tmp_proc_1();}
+static void SlStubSaveProc() {SlAutolength(SlStubSaveProc2, NULL);}
+
+/** Save a chunk of data (eg. vehicles, stations, etc.). Each chunk is
+ * prefixed by an ID identifying it, followed by data, and terminator where appropiate
+ * @param ch The chunkhandler that will be used for the operation
+ */
+static void SlSaveChunk(const ChunkHandler *ch)
+{
+ ChunkSaveLoadProc *proc = ch->save_proc;
+
+ /* Don't save any chunk information if there is no save handler. */
+ if (proc == NULL) return;
+
+ SlWriteUint32(ch->id);
+ DEBUG(sl, 2, "Saving chunk %c%c%c%c", ch->id >> 24, ch->id >> 16, ch->id >> 8, ch->id);
+
+ if (ch->flags & CH_AUTO_LENGTH) {
+ /* Need to calculate the length. Solve that by calling SlAutoLength in the save_proc. */
+ _tmp_proc_1 = proc;
+ proc = SlStubSaveProc;
+ }
+
+ _sl.block_mode = ch->flags & CH_TYPE_MASK;
+ switch (ch->flags & CH_TYPE_MASK) {
+ case CH_RIFF:
+ _sl.need_length = NL_WANTLENGTH;
+ proc();
+ break;
+ case CH_ARRAY:
+ _sl.last_array_index = 0;
+ SlWriteByte(CH_ARRAY);
+ proc();
+ SlWriteArrayLength(0); // Terminate arrays
+ break;
+ case CH_SPARSE_ARRAY:
+ SlWriteByte(CH_SPARSE_ARRAY);
+ proc();
+ SlWriteArrayLength(0); // Terminate arrays
+ break;
+ default: NOT_REACHED();
+ }
+}
+
+/** Save all chunks */
+static void SlSaveChunks()
+{
+ const ChunkHandler *ch;
+ const ChunkHandler* const *chsc;
+ uint p;
+
+ for (p = 0; p != CH_NUM_PRI_LEVELS; p++) {
+ for (chsc = _sl.chs; (ch = *chsc++) != NULL;) {
+ while (true) {
+ if (((ch->flags >> CH_PRI_SHL) & (CH_NUM_PRI_LEVELS - 1)) == p)
+ SlSaveChunk(ch);
+ if (ch->flags & CH_LAST)
+ break;
+ ch++;
+ }
+ }
+ }
+
+ /* Terminator */
+ SlWriteUint32(0);
+}
+
+/** Find the ChunkHandler that will be used for processing the found
+ * chunk in the savegame or in memory
+ * @param id the chunk in question
+ * @return returns the appropiate chunkhandler
+ */
+static const ChunkHandler *SlFindChunkHandler(uint32 id)
+{
+ const ChunkHandler *ch;
+ const ChunkHandler *const *chsc;
+ for (chsc = _sl.chs; (ch = *chsc++) != NULL;) {
+ for (;;) {
+ if (ch->id == id) return ch;
+ if (ch->flags & CH_LAST) break;
+ ch++;
+ }
+ }
+ return NULL;
+}
+
+/** Load all chunks */
+static void SlLoadChunks()
+{
+ uint32 id;
+ const ChunkHandler *ch;
+
+ for (id = SlReadUint32(); id != 0; id = SlReadUint32()) {
+ DEBUG(sl, 2, "Loading chunk %c%c%c%c", id >> 24, id >> 16, id >> 8, id);
+
+ ch = SlFindChunkHandler(id);
+ if (ch == NULL) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Unknown chunk type");
+ SlLoadChunk(ch);
+ }
+}
+
+/*******************************************
+ ********** START OF LZO CODE **************
+ *******************************************/
+#define LZO_SIZE 8192
+
+#include "../minilzo.h"
+
+static size_t ReadLZO()
+{
+ byte out[LZO_SIZE + LZO_SIZE / 64 + 16 + 3 + 8];
+ uint32 tmp[2];
+ uint32 size;
+ uint len;
+
+ /* Read header*/
+ if (fread(tmp, sizeof(tmp), 1, _sl.fh) != 1) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE, "File read failed");
+
+ /* Check if size is bad */
+ ((uint32*)out)[0] = size = tmp[1];
+
+ if (_sl_version != 0) {
+ tmp[0] = TO_BE32(tmp[0]);
+ size = TO_BE32(size);
+ }
+
+ if (size >= sizeof(out)) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Inconsistent size");
+
+ /* Read block */
+ if (fread(out + sizeof(uint32), size, 1, _sl.fh) != 1) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
+
+ /* Verify checksum */
+ if (tmp[0] != lzo_adler32(0, out, size + sizeof(uint32))) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Bad checksum");
+
+ /* Decompress */
+ lzo1x_decompress(out + sizeof(uint32)*1, size, _sl.buf, &len, NULL);
+ return len;
+}
+
+/* p contains the pointer to the buffer, len contains the pointer to the length.
+ * len bytes will be written, p and l will be updated to reflect the next buffer. */
+static void WriteLZO(size_t size)
+{
+ byte out[LZO_SIZE + LZO_SIZE / 64 + 16 + 3 + 8];
+ byte wrkmem[sizeof(byte*)*4096];
+ uint outlen;
+
+ lzo1x_1_compress(_sl.buf, (lzo_uint)size, out + sizeof(uint32)*2, &outlen, wrkmem);
+ ((uint32*)out)[1] = TO_BE32(outlen);
+ ((uint32*)out)[0] = TO_BE32(lzo_adler32(0, out + sizeof(uint32), outlen + sizeof(uint32)));
+ if (fwrite(out, outlen + sizeof(uint32)*2, 1, _sl.fh) != 1) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE);
+}
+
+static bool InitLZO()
+{
+ _sl.bufsize = LZO_SIZE;
+ _sl.buf = _sl.buf_ori = MallocT<byte>(LZO_SIZE);
+ return true;
+}
+
+static void UninitLZO()
+{
+ free(_sl.buf_ori);
+}
+
+/*********************************************
+ ******** START OF NOCOMP CODE (uncompressed)*
+ *********************************************/
+static size_t ReadNoComp()
+{
+ return fread(_sl.buf, 1, LZO_SIZE, _sl.fh);
+}
+
+static void WriteNoComp(size_t size)
+{
+ if (fwrite(_sl.buf, 1, size, _sl.fh) != size) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE);
+}
+
+static bool InitNoComp()
+{
+ _sl.bufsize = LZO_SIZE;
+ _sl.buf = _sl.buf_ori = MallocT<byte>(LZO_SIZE);
+ return true;
+}
+
+static void UninitNoComp()
+{
+ free(_sl.buf_ori);
+}
+
+/********************************************
+ ********** START OF MEMORY CODE (in ram)****
+ ********************************************/
+
+#include "../table/sprites.h"
+#include "../gui.h"
+
+struct ThreadedSave {
+ uint count;
+ byte ff_state;
+ bool saveinprogress;
+ CursorID cursor;
+};
+
+/* A maximum size of of 128K * 500 = 64.000KB savegames */
+STATIC_OLD_POOL(Savegame, byte, 17, 500, NULL, NULL)
+static ThreadedSave _ts;
+
+static bool InitMem()
+{
+ _ts.count = 0;
+
+ _Savegame_pool.CleanPool();
+ _Savegame_pool.AddBlockToPool();
+
+ /* A block from the pool is a contigious area of memory, so it is safe to write to it sequentially */
+ _sl.bufsize = GetSavegamePoolSize();
+ _sl.buf = GetSavegame(_ts.count);
+ return true;
+}
+
+static void UnInitMem()
+{
+ _Savegame_pool.CleanPool();
+}
+
+static void WriteMem(size_t size)
+{
+ _ts.count += (uint)size;
+ /* Allocate new block and new buffer-pointer */
+ _Savegame_pool.AddBlockIfNeeded(_ts.count);
+ _sl.buf = GetSavegame(_ts.count);
+}
+
+/********************************************
+ ********** START OF ZLIB CODE **************
+ ********************************************/
+
+#if defined(WITH_ZLIB)
+#include <zlib.h>
+
+static z_stream _z;
+
+static bool InitReadZlib()
+{
+ memset(&_z, 0, sizeof(_z));
+ if (inflateInit(&_z) != Z_OK) return false;
+
+ _sl.bufsize = 4096;
+ _sl.buf = _sl.buf_ori = MallocT<byte>(4096 + 4096); // also contains fread buffer
+ return true;
+}
+
+static size_t ReadZlib()
+{
+ int r;
+
+ _z.next_out = _sl.buf;
+ _z.avail_out = 4096;
+
+ do {
+ /* read more bytes from the file? */
+ if (_z.avail_in == 0) {
+ _z.avail_in = (uint)fread(_z.next_in = _sl.buf + 4096, 1, 4096, _sl.fh);
+ }
+
+ /* inflate the data */
+ r = inflate(&_z, 0);
+ if (r == Z_STREAM_END)
+ break;
+
+ if (r != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "inflate() failed");
+ } while (_z.avail_out);
+
+ return 4096 - _z.avail_out;
+}
+
+static void UninitReadZlib()
+{
+ inflateEnd(&_z);
+ free(_sl.buf_ori);
+}
+
+static bool InitWriteZlib()
+{
+ memset(&_z, 0, sizeof(_z));
+ if (deflateInit(&_z, 6) != Z_OK) return false;
+
+ _sl.bufsize = 4096;
+ _sl.buf = _sl.buf_ori = MallocT<byte>(4096); // also contains fread buffer
+ return true;
+}
+
+static void WriteZlibLoop(z_streamp z, byte *p, size_t len, int mode)
+{
+ byte buf[1024]; // output buffer
+ int r;
+ uint n;
+ z->next_in = p;
+ z->avail_in = (uInt)len;
+ do {
+ z->next_out = buf;
+ z->avail_out = sizeof(buf);
+ r = deflate(z, mode);
+ /* bytes were emitted? */
+ if ((n = sizeof(buf) - z->avail_out) != 0) {
+ if (fwrite(buf, n, 1, _sl.fh) != 1) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE);
+ }
+ if (r == Z_STREAM_END)
+ break;
+ if (r != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "zlib returned error code");
+ } while (z->avail_in || !z->avail_out);
+}
+
+static void WriteZlib(size_t len)
+{
+ WriteZlibLoop(&_z, _sl.buf, len, 0);
+}
+
+static void UninitWriteZlib()
+{
+ /* flush any pending output. */
+ if (_sl.fh) WriteZlibLoop(&_z, NULL, 0, Z_FINISH);
+ deflateEnd(&_z);
+ free(_sl.buf_ori);
+}
+
+#endif /* WITH_ZLIB */
+
+/*******************************************
+ ************* END OF CODE *****************
+ *******************************************/
+
+/* these define the chunks */
+extern const ChunkHandler _gamelog_chunk_handlers[];
+extern const ChunkHandler _map_chunk_handlers[];
+extern const ChunkHandler _misc_chunk_handlers[];
+extern const ChunkHandler _name_chunk_handlers[];
+extern const ChunkHandler _cheat_chunk_handlers[] ;
+extern const ChunkHandler _setting_chunk_handlers[];
+extern const ChunkHandler _company_chunk_handlers[];
+extern const ChunkHandler _engine_chunk_handlers[];
+extern const ChunkHandler _veh_chunk_handlers[];
+extern const ChunkHandler _waypoint_chunk_handlers[];
+extern const ChunkHandler _depot_chunk_handlers[];
+extern const ChunkHandler _order_chunk_handlers[];
+extern const ChunkHandler _town_chunk_handlers[];
+extern const ChunkHandler _sign_chunk_handlers[];
+extern const ChunkHandler _station_chunk_handlers[];
+extern const ChunkHandler _industry_chunk_handlers[];
+extern const ChunkHandler _economy_chunk_handlers[];
+extern const ChunkHandler _subsidy_chunk_handlers[];
+extern const ChunkHandler _animated_tile_chunk_handlers[];
+extern const ChunkHandler _newgrf_chunk_handlers[];
+extern const ChunkHandler _group_chunk_handlers[];
+extern const ChunkHandler _cargopacket_chunk_handlers[];
+extern const ChunkHandler _autoreplace_chunk_handlers[];
+
+static const ChunkHandler * const _chunk_handlers[] = {
+ _gamelog_chunk_handlers,
+ _map_chunk_handlers,
+ _misc_chunk_handlers,
+ _name_chunk_handlers,
+ _cheat_chunk_handlers,
+ _setting_chunk_handlers,
+ _veh_chunk_handlers,
+ _waypoint_chunk_handlers,
+ _depot_chunk_handlers,
+ _order_chunk_handlers,
+ _industry_chunk_handlers,
+ _economy_chunk_handlers,
+ _subsidy_chunk_handlers,
+ _engine_chunk_handlers,
+ _town_chunk_handlers,
+ _sign_chunk_handlers,
+ _station_chunk_handlers,
+ _company_chunk_handlers,
+ _animated_tile_chunk_handlers,
+ _newgrf_chunk_handlers,
+ _group_chunk_handlers,
+ _cargopacket_chunk_handlers,
+ _autoreplace_chunk_handlers,
+ NULL,
+};
+
+/**
+ * Pointers cannot be saved to a savegame, so this functions gets
+ * the index of the item, and if not available, it hussles with
+ * pointers (looks really bad :()
+ * Remember that a NULL item has value 0, and all
+ * indeces have +1, so vehicle 0 is saved as index 1.
+ * @param obj The object that we want to get the index of
+ * @param rt SLRefType type of the object the index is being sought of
+ * @return Return the pointer converted to an index of the type pointed to
+ */
+static uint ReferenceToInt(const void *obj, SLRefType rt)
+{
+ if (obj == NULL) return 0;
+
+ switch (rt) {
+ case REF_VEHICLE_OLD: // Old vehicles we save as new onces
+ case REF_VEHICLE: return ((const Vehicle*)obj)->index + 1;
+ case REF_STATION: return ((const Station*)obj)->index + 1;
+ case REF_TOWN: return ((const Town*)obj)->index + 1;
+ case REF_ORDER: return ((const Order*)obj)->index + 1;
+ case REF_ROADSTOPS: return ((const RoadStop*)obj)->index + 1;
+ case REF_ENGINE_RENEWS: return ((const EngineRenew*)obj)->index + 1;
+ case REF_CARGO_PACKET: return ((const CargoPacket*)obj)->index + 1;
+ case REF_ORDERLIST: return ((const OrderList*)obj)->index + 1;
+ default: NOT_REACHED();
+ }
+
+ return 0; // avoid compiler warning
+}
+
+/**
+ * Pointers cannot be loaded from a savegame, so this function
+ * gets the index from the savegame and returns the appropiate
+ * pointer from the already loaded base.
+ * Remember that an index of 0 is a NULL pointer so all indeces
+ * are +1 so vehicle 0 is saved as 1.
+ * @param index The index that is being converted to a pointer
+ * @param rt SLRefType type of the object the pointer is sought of
+ * @return Return the index converted to a pointer of any type
+ */
+static void *IntToReference(uint index, SLRefType rt)
+{
+ /* After version 4.3 REF_VEHICLE_OLD is saved as REF_VEHICLE,
+ * and should be loaded like that */
+ if (rt == REF_VEHICLE_OLD && !CheckSavegameVersionOldStyle(4, 4)) {
+ rt = REF_VEHICLE;
+ }
+
+ /* No need to look up NULL pointers, just return immediately */
+ if (rt != REF_VEHICLE_OLD && index == 0) {
+ return NULL;
+ }
+
+ index--; // correct for the NULL index
+
+ switch (rt) {
+ case REF_ORDERLIST:
+ if (_OrderList_pool.AddBlockIfNeeded(index)) return GetOrderList(index);
+ error("Orders: failed loading savegame: too many order lists");
+
+ case REF_ORDER:
+ if (_Order_pool.AddBlockIfNeeded(index)) return GetOrder(index);
+ error("Orders: failed loading savegame: too many orders");
+
+ case REF_VEHICLE:
+ if (_Vehicle_pool.AddBlockIfNeeded(index)) return GetVehicle(index);
+ error("Vehicles: failed loading savegame: too many vehicles");
+
+ case REF_STATION:
+ if (_Station_pool.AddBlockIfNeeded(index)) return GetStation(index);
+ error("Stations: failed loading savegame: too many stations");
+
+ case REF_TOWN:
+ if (_Town_pool.AddBlockIfNeeded(index)) return GetTown(index);
+ error("Towns: failed loading savegame: too many towns");
+
+ case REF_ROADSTOPS:
+ if (_RoadStop_pool.AddBlockIfNeeded(index)) return GetRoadStop(index);
+ error("RoadStops: failed loading savegame: too many RoadStops");
+
+ case REF_ENGINE_RENEWS:
+ if (_EngineRenew_pool.AddBlockIfNeeded(index)) return GetEngineRenew(index);
+ error("EngineRenews: failed loading savegame: too many EngineRenews");
+
+ case REF_CARGO_PACKET:
+ if (_CargoPacket_pool.AddBlockIfNeeded(index)) return GetCargoPacket(index);
+ error("CargoPackets: failed loading savegame: too many Cargo packets");
+
+ case REF_VEHICLE_OLD:
+ /* Old vehicles were saved differently:
+ * invalid vehicle was 0xFFFF,
+ * and the index was not - 1.. correct for this */
+ index++;
+ if (index == INVALID_VEHICLE) return NULL;
+
+ if (_Vehicle_pool.AddBlockIfNeeded(index)) return GetVehicle(index);
+ error("Vehicles: failed loading savegame: too many vehicles");
+
+ default: NOT_REACHED();
+ }
+
+ return NULL;
+}
+
+/** The format for a reader/writer type of a savegame */
+struct SaveLoadFormat {
+ const char *name; ///< name of the compressor/decompressor (debug-only)
+ uint32 tag; ///< the 4-letter tag by which it is identified in the savegame
+
+ bool (*init_read)(); ///< function executed upon initalization of the loader
+ ReaderProc *reader; ///< function that loads the data from the file
+ void (*uninit_read)(); ///< function executed when reading is finished
+
+ bool (*init_write)(); ///< function executed upon intialization of the saver
+ WriterProc *writer; ///< function that saves the data to the file
+ void (*uninit_write)(); ///< function executed when writing is done
+};
+
+static const SaveLoadFormat _saveload_formats[] = {
+ {"memory", 0, NULL, NULL, NULL, InitMem, WriteMem, UnInitMem},
+ {"lzo", TO_BE32X('OTTD'), InitLZO, ReadLZO, UninitLZO, InitLZO, WriteLZO, UninitLZO},
+ {"none", TO_BE32X('OTTN'), InitNoComp, ReadNoComp, UninitNoComp, InitNoComp, WriteNoComp, UninitNoComp},
+#if defined(WITH_ZLIB)
+ {"zlib", TO_BE32X('OTTZ'), InitReadZlib, ReadZlib, UninitReadZlib, InitWriteZlib, WriteZlib, UninitWriteZlib},
+#else
+ {"zlib", TO_BE32X('OTTZ'), NULL, NULL, NULL, NULL, NULL, NULL},
+#endif
+};
+
+/**
+ * Return the savegameformat of the game. Whether it was create with ZLIB compression
+ * uncompressed, or another type
+ * @param s Name of the savegame format. If NULL it picks the first available one
+ * @return Pointer to SaveLoadFormat struct giving all characteristics of this type of savegame
+ */
+static const SaveLoadFormat *GetSavegameFormat(const char *s)
+{
+ const SaveLoadFormat *def = endof(_saveload_formats) - 1;
+
+ /* find default savegame format, the highest one with which files can be written */
+ while (!def->init_write) def--;
+
+ if (s != NULL && s[0] != '\0') {
+ const SaveLoadFormat *slf;
+ for (slf = &_saveload_formats[0]; slf != endof(_saveload_formats); slf++) {
+ if (slf->init_write != NULL && strcmp(s, slf->name) == 0)
+ return slf;
+ }
+
+ ShowInfoF("Savegame format '%s' is not available. Reverting to '%s'.", s, def->name);
+ }
+ return def;
+}
+
+/* actual loader/saver function */
+void InitializeGame(uint size_x, uint size_y, bool reset_date);
+extern bool AfterLoadGame();
+extern bool LoadOldSaveGame(const char *file);
+
+/** Small helper function to close the to be loaded savegame an signal error */
+static inline SaveOrLoadResult AbortSaveLoad()
+{
+ if (_sl.fh != NULL) fclose(_sl.fh);
+
+ _sl.fh = NULL;
+ return SL_ERROR;
+}
+
+/** Update the gui accordingly when starting saving
+ * and set locks on saveload. Also turn off fast-forward cause with that
+ * saving takes Aaaaages */
+static void SaveFileStart()
+{
+ _ts.ff_state = _fast_forward;
+ _fast_forward = 0;
+ if (_cursor.sprite == SPR_CURSOR_MOUSE) SetMouseCursor(SPR_CURSOR_ZZZ, PAL_NONE);
+
+ InvalidateWindowData(WC_STATUS_BAR, 0, SBI_SAVELOAD_START);
+ _ts.saveinprogress = true;
+}
+
+/** Update the gui accordingly when saving is done and release locks
+ * on saveload */
+static void SaveFileDone()
+{
+ if (_game_mode != GM_MENU) _fast_forward = _ts.ff_state;
+ if (_cursor.sprite == SPR_CURSOR_ZZZ) SetMouseCursor(SPR_CURSOR_MOUSE, PAL_NONE);
+
+ InvalidateWindowData(WC_STATUS_BAR, 0, SBI_SAVELOAD_FINISH);
+ _ts.saveinprogress = false;
+}
+
+/** Set the error message from outside of the actual loading/saving of the game (AfterLoadGame and friends) */
+void SetSaveLoadError(StringID str)
+{
+ _sl.error_str = str;
+}
+
+/** Get the string representation of the error message */
+const char *GetSaveLoadErrorString()
+{
+ SetDParam(0, _sl.error_str);
+ SetDParamStr(1, _sl.extra_msg);
+
+ static char err_str[512];
+ GetString(err_str, _sl.save ? STR_4007_GAME_SAVE_FAILED : STR_4009_GAME_LOAD_FAILED, lastof(err_str));
+ return err_str;
+}
+
+/** Show a gui message when saving has failed */
+static void SaveFileError()
+{
+ SetDParamStr(0, GetSaveLoadErrorString());
+ ShowErrorMessage(STR_JUST_RAW_STRING, STR_NULL, 0, 0);
+ SaveFileDone();
+}
+
+/** We have written the whole game into memory, _Savegame_pool, now find
+ * and appropiate compressor and start writing to file.
+ */
+static SaveOrLoadResult SaveFileToDisk(bool threaded)
+{
+ const SaveLoadFormat *fmt;
+ uint32 hdr[2];
+
+ _sl.excpt_uninit = NULL;
+ try {
+ fmt = GetSavegameFormat(_savegame_format);
+
+ /* We have written our stuff to memory, now write it to file! */
+ hdr[0] = fmt->tag;
+ hdr[1] = TO_BE32(SAVEGAME_VERSION << 16);
+ if (fwrite(hdr, sizeof(hdr), 1, _sl.fh) != 1) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE);
+
+ if (!fmt->init_write()) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor");
+
+ {
+ uint i;
+ uint count = 1 << Savegame_POOL_BLOCK_SIZE_BITS;
+
+ if (_ts.count != _sl.offs_base) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Unexpected size of chunk");
+ for (i = 0; i != _Savegame_pool.GetBlockCount() - 1; i++) {
+ _sl.buf = _Savegame_pool.blocks[i];
+ fmt->writer(count);
+ }
+
+ /* The last block is (almost) always not fully filled, so only write away
+ * as much data as it is in there */
+ _sl.buf = _Savegame_pool.blocks[i];
+ fmt->writer(_ts.count - (i * count));
+ }
+
+ fmt->uninit_write();
+ if (_ts.count != _sl.offs_base) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Unexpected size of chunk");
+ GetSavegameFormat("memory")->uninit_write(); // clean the memorypool
+ fclose(_sl.fh);
+
+ if (threaded) SetAsyncSaveFinish(SaveFileDone);
+
+ return SL_OK;
+ }
+ catch (...) {
+ AbortSaveLoad();
+ if (_sl.excpt_uninit != NULL) _sl.excpt_uninit();
+
+ /* Skip the "color" character */
+ DEBUG(sl, 0, GetSaveLoadErrorString() + 3);
+
+ if (threaded) {
+ SetAsyncSaveFinish(SaveFileError);
+ } else {
+ SaveFileError();
+ }
+ return SL_ERROR;
+ }
+}
+
+static void SaveFileToDiskThread(void *arg)
+{
+ SaveFileToDisk(true);
+}
+
+void WaitTillSaved()
+{
+ if (_save_thread == NULL) return;
+
+ _save_thread->Join();
+ delete _save_thread;
+ _save_thread = NULL;
+}
+
+/**
+ * Main Save or Load function where the high-level saveload functions are
+ * handled. It opens the savegame, selects format and checks versions
+ * @param filename The name of the savegame being created/loaded
+ * @param mode Save or load. Load can also be a TTD(Patch) game. Use SL_LOAD, SL_OLD_LOAD or SL_SAVE
+ * @return Return the results of the action. SL_OK, SL_ERROR or SL_REINIT ("unload" the game)
+ */
+SaveOrLoadResult SaveOrLoad(const char *filename, int mode, Subdirectory sb)
+{
+ uint32 hdr[2];
+ const SaveLoadFormat *fmt;
+
+ /* An instance of saving is already active, so don't go saving again */
+ if (_ts.saveinprogress && mode == SL_SAVE) {
+ /* if not an autosave, but a user action, show error message */
+ if (!_do_autosave) ShowErrorMessage(INVALID_STRING_ID, STR_SAVE_STILL_IN_PROGRESS, 0, 0);
+ return SL_OK;
+ }
+ WaitTillSaved();
+
+ _next_offs = 0;
+
+ /* Load a TTDLX or TTDPatch game */
+ if (mode == SL_OLD_LOAD) {
+ InitializeGame(256, 256, true); // set a mapsize of 256x256 for TTDPatch games or it might get confused
+ GamelogReset();
+ if (!LoadOldSaveGame(filename)) return SL_REINIT;
+ _sl_version = 0;
+ _sl_minor_version = 0;
+ GamelogStartAction(GLAT_LOAD);
+ if (!AfterLoadGame()) {
+ GamelogStopAction();
+ return SL_REINIT;
+ }
+ GamelogStopAction();
+ return SL_OK;
+ }
+
+ _sl.excpt_uninit = NULL;
+ try {
+ _sl.fh = (mode == SL_SAVE) ? FioFOpenFile(filename, "wb", sb) : FioFOpenFile(filename, "rb", sb);
+
+ /* Make it a little easier to load savegames from the console */
+ if (_sl.fh == NULL && mode == SL_LOAD) _sl.fh = FioFOpenFile(filename, "rb", SAVE_DIR);
+ if (_sl.fh == NULL && mode == SL_LOAD) _sl.fh = FioFOpenFile(filename, "rb", BASE_DIR);
+
+ if (_sl.fh == NULL) {
+ SlError(mode == SL_SAVE ? STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE : STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
+ }
+
+ _sl.bufe = _sl.bufp = NULL;
+ _sl.offs_base = 0;
+ _sl.save = (mode != 0);
+ _sl.chs = _chunk_handlers;
+
+ /* General tactic is to first save the game to memory, then use an available writer
+ * to write it to file, either in threaded mode if possible, or single-threaded */
+ if (mode == SL_SAVE) { /* SAVE game */
+ DEBUG(desync, 1, "save: %s\n", filename);
+ fmt = GetSavegameFormat("memory"); // write to memory
+
+ _sl.write_bytes = fmt->writer;
+ _sl.excpt_uninit = fmt->uninit_write;
+ if (!fmt->init_write()) {
+ DEBUG(sl, 0, "Initializing writer '%s' failed.", fmt->name);
+ return AbortSaveLoad();
+ }
+
+ _sl_version = SAVEGAME_VERSION;
+
+ SaveViewportBeforeSaveGame();
+ SlSaveChunks();
+ SlWriteFill(); // flush the save buffer
+
+ SaveFileStart();
+ if (_network_server ||
+ (_save_thread = ThreadObject::New(&SaveFileToDiskThread, NULL)) == NULL) {
+ if (!_network_server) DEBUG(sl, 1, "Cannot create savegame thread, reverting to single-threaded mode...");
+
+ SaveOrLoadResult result = SaveFileToDisk(false);
+ SaveFileDone();
+
+ return result;
+ }
+ } else { /* LOAD game */
+ assert(mode == SL_LOAD);
+ DEBUG(desync, 1, "load: %s\n", filename);
+
+ if (fread(hdr, sizeof(hdr), 1, _sl.fh) != 1) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
+
+ /* see if we have any loader for this type. */
+ for (fmt = _saveload_formats; ; fmt++) {
+ /* No loader found, treat as version 0 and use LZO format */
+ if (fmt == endof(_saveload_formats)) {
+ DEBUG(sl, 0, "Unknown savegame type, trying to load it as the buggy format");
+ #if defined(WINCE)
+ /* Of course some system had not to support rewind ;) */
+ fseek(_sl.fh, 0L, SEEK_SET);
+ clearerr(_sl.fh);
+ #else
+ rewind(_sl.fh);
+ #endif
+ _sl_version = 0;
+ _sl_minor_version = 0;
+ fmt = _saveload_formats + 1; // LZO
+ break;
+ }
+
+ if (fmt->tag == hdr[0]) {
+ /* check version number */
+ _sl_version = TO_BE32(hdr[1]) >> 16;
+ /* Minor is not used anymore from version 18.0, but it is still needed
+ * in versions before that (4 cases) which can't be removed easy.
+ * Therefor it is loaded, but never saved (or, it saves a 0 in any scenario).
+ * So never EVER use this minor version again. -- TrueLight -- 22-11-2005 */
+ _sl_minor_version = (TO_BE32(hdr[1]) >> 8) & 0xFF;
+
+ DEBUG(sl, 1, "Loading savegame version %d", _sl_version);
+
+ /* Is the version higher than the current? */
+ if (_sl_version > SAVEGAME_VERSION) SlError(STR_GAME_SAVELOAD_ERROR_TOO_NEW_SAVEGAME);
+ break;
+ }
+ }
+
+ _sl.read_bytes = fmt->reader;
+ _sl.excpt_uninit = fmt->uninit_read;
+
+ /* loader for this savegame type is not implemented? */
+ if (fmt->init_read == NULL) {
+ char err_str[64];
+ snprintf(err_str, lengthof(err_str), "Loader for '%s' is not available.", fmt->name);
+ SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, err_str);
+ }
+
+ if (!fmt->init_read()) {
+ char err_str[64];
+ snprintf(err_str, lengthof(err_str), "Initializing loader '%s' failed", fmt->name);
+ SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, err_str);
+ }
+
+ /* Old maps were hardcoded to 256x256 and thus did not contain
+ * any mapsize information. Pre-initialize to 256x256 to not to
+ * confuse old games */
+ InitializeGame(256, 256, true);
+
+ GamelogReset();
+
+ SlLoadChunks();
+ fmt->uninit_read();
+ fclose(_sl.fh);
+
+ GamelogStartAction(GLAT_LOAD);
+
+ _savegame_type = SGT_OTTD;
+
+ /* After loading fix up savegame for any internal changes that
+ * might've occured since then. If it fails, load back the old game */
+ if (!AfterLoadGame()) {
+ GamelogStopAction();
+ return SL_REINIT;
+ }
+
+ GamelogStopAction();
+ }
+
+ return SL_OK;
+ }
+ catch (...) {
+ AbortSaveLoad();
+
+ /* deinitialize compressor. */
+ if (_sl.excpt_uninit != NULL) _sl.excpt_uninit();
+
+ /* Skip the "color" character */
+ DEBUG(sl, 0, GetSaveLoadErrorString() + 3);
+
+ /* A saver/loader exception!! reinitialize all variables to prevent crash! */
+ return (mode == SL_LOAD) ? SL_REINIT : SL_ERROR;
+ }
+}
+
+/** Do a save when exiting the game (patch option) _settings_client.gui.autosave_on_exit */
+void DoExitSave()
+{
+ SaveOrLoad("exit.sav", SL_SAVE, AUTOSAVE_DIR);
+}
+
+/**
+ * Fill the buffer with the default name for a savegame *or* screenshot.
+ * @param buf the buffer to write to.
+ * @param last the last element in the buffer.
+ */
+void GenerateDefaultSaveName(char *buf, const char *last)
+{
+ /* Check if we have a name for this map, which is the name of the first
+ * available company. When there's no company available we'll use
+ * 'Spectator' as "company" name. */
+ CompanyID cid = _local_company;
+ if (!IsValidCompanyID(cid)) {
+ const Company *c;
+ FOR_ALL_COMPANIES(c) {
+ cid = c->index;
+ break;
+ }
+ }
+
+ SetDParam(0, cid);
+
+ /* Insert current date */
+ switch (_settings_client.gui.date_format_in_default_names) {
+ case 0: SetDParam(1, STR_JUST_DATE_LONG); break;
+ case 1: SetDParam(1, STR_JUST_DATE_TINY); break;
+ case 2: SetDParam(1, STR_JUST_DATE_ISO); break;
+ default: NOT_REACHED();
+ }
+ SetDParam(2, _date);
+
+ /* Get the correct string (special string for when there's not company) */
+ GetString(buf, !IsValidCompanyID(cid) ? STR_GAME_SAVELOAD_SPECTATOR_SAVEGAME : STR_4004, last);
+ SanitizeFilename(buf);
+}
+
+#if 0
+/**
+ * Function to get the type of the savegame by looking at the file header.
+ * NOTICE: Not used right now, but could be used if extensions of savegames are garbled
+ * @param file Savegame to be checked
+ * @return SL_OLD_LOAD or SL_LOAD of the file
+ */
+int GetSavegameType(char *file)
+{
+ const SaveLoadFormat *fmt;
+ uint32 hdr;
+ FILE *f;
+ int mode = SL_OLD_LOAD;
+
+ f = fopen(file, "rb");
+ if (fread(&hdr, sizeof(hdr), 1, f) != 1) {
+ DEBUG(sl, 0, "Savegame is obsolete or invalid format");
+ mode = SL_LOAD; // don't try to get filename, just show name as it is written
+ } else {
+ /* see if we have any loader for this type. */
+ for (fmt = _saveload_formats; fmt != endof(_saveload_formats); fmt++) {
+ if (fmt->tag == hdr) {
+ mode = SL_LOAD; // new type of savegame
+ break;
+ }
+ }
+ }
+
+ fclose(f);
+ return mode;
+}
+#endif
diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h
new file mode 100644
index 000000000..f71b0dec4
--- /dev/null
+++ b/src/saveload/saveload.h
@@ -0,0 +1,332 @@
+/* $Id$ */
+
+/** @file saveload.h Functions/types related to saving and loading games. */
+
+#ifndef SAVELOAD_H
+#define SAVELOAD_H
+
+#include "../fileio_type.h"
+
+#ifdef SIZE_MAX
+#undef SIZE_MAX
+#endif
+
+#define SIZE_MAX ((size_t)-1)
+
+enum SaveOrLoadResult {
+ SL_OK = 0, ///< completed successfully
+ SL_ERROR = 1, ///< error that was caught before internal structures were modified
+ SL_REINIT = 2, ///< error that was caught in the middle of updating game state, need to clear it. (can only happen during load)
+};
+
+enum SaveOrLoadMode {
+ SL_INVALID = -1,
+ SL_LOAD = 0,
+ SL_SAVE = 1,
+ SL_OLD_LOAD = 2,
+ SL_PNG = 3,
+ SL_BMP = 4,
+};
+
+enum SavegameType {
+ SGT_TTD, ///< TTD savegame (can be detected incorrectly)
+ SGT_TTDP1, ///< TTDP savegame ( -//- ) (data at NW border)
+ SGT_TTDP2, ///< TTDP savegame in new format (data at SE border)
+ SGT_OTTD ///< OTTD savegame
+};
+
+void GenerateDefaultSaveName(char *buf, const char *last);
+void SetSaveLoadError(uint16 str);
+const char *GetSaveLoadErrorString();
+SaveOrLoadResult SaveOrLoad(const char *filename, int mode, Subdirectory sb);
+void WaitTillSaved();
+void DoExitSave();
+
+
+typedef void ChunkSaveLoadProc();
+typedef void AutolengthProc(void *arg);
+
+struct ChunkHandler {
+ uint32 id;
+ ChunkSaveLoadProc *save_proc;
+ ChunkSaveLoadProc *load_proc;
+ uint32 flags;
+};
+
+struct NullStruct {
+ byte null;
+};
+
+enum SLRefType {
+ REF_ORDER = 0,
+ REF_VEHICLE = 1,
+ REF_STATION = 2,
+ REF_TOWN = 3,
+ REF_VEHICLE_OLD = 4,
+ REF_ROADSTOPS = 5,
+ REF_ENGINE_RENEWS = 6,
+ REF_CARGO_PACKET = 7,
+ REF_ORDERLIST = 8,
+};
+
+#define SL_MAX_VERSION 255
+
+enum {
+ INC_VEHICLE_COMMON = 0,
+};
+
+enum {
+ CH_RIFF = 0,
+ CH_ARRAY = 1,
+ CH_SPARSE_ARRAY = 2,
+ CH_TYPE_MASK = 3,
+ CH_LAST = 8,
+ CH_AUTO_LENGTH = 16,
+
+ CH_PRI_0 = 0 << 4,
+ CH_PRI_1 = 1 << 4,
+ CH_PRI_2 = 2 << 4,
+ CH_PRI_3 = 3 << 4,
+ CH_PRI_SHL = 4,
+ CH_NUM_PRI_LEVELS = 4,
+};
+
+/** VarTypes is the general bitmasked magic type that tells us
+ * certain characteristics about the variable it refers to. For example
+ * SLE_FILE_* gives the size(type) as it would be in the savegame and
+ * SLE_VAR_* the size(type) as it is in memory during runtime. These are
+ * the first 8 bits (0-3 SLE_FILE, 4-7 SLE_VAR).
+ * Bits 8-15 are reserved for various flags as explained below */
+enum VarTypes {
+ /* 4 bits allocated a maximum of 16 types for NumberType */
+ SLE_FILE_I8 = 0,
+ SLE_FILE_U8 = 1,
+ SLE_FILE_I16 = 2,
+ SLE_FILE_U16 = 3,
+ SLE_FILE_I32 = 4,
+ SLE_FILE_U32 = 5,
+ SLE_FILE_I64 = 6,
+ SLE_FILE_U64 = 7,
+ SLE_FILE_STRINGID = 8, ///< StringID offset into strings-array
+ SLE_FILE_STRING = 9,
+ /* 6 more possible file-primitives */
+
+ /* 4 bits allocated a maximum of 16 types for NumberType */
+ SLE_VAR_BL = 0 << 4,
+ SLE_VAR_I8 = 1 << 4,
+ SLE_VAR_U8 = 2 << 4,
+ SLE_VAR_I16 = 3 << 4,
+ SLE_VAR_U16 = 4 << 4,
+ SLE_VAR_I32 = 5 << 4,
+ SLE_VAR_U32 = 6 << 4,
+ SLE_VAR_I64 = 7 << 4,
+ SLE_VAR_U64 = 8 << 4,
+ SLE_VAR_NULL = 9 << 4, ///< useful to write zeros in savegame.
+ SLE_VAR_STRB = 10 << 4, ///< string (with pre-allocated buffer)
+ SLE_VAR_STRBQ = 11 << 4, ///< string enclosed in quotes (with pre-allocated buffer)
+ SLE_VAR_STR = 12 << 4, ///< string pointer
+ SLE_VAR_STRQ = 13 << 4, ///< string pointer enclosed in quotes
+ SLE_VAR_NAME = 14 << 4, ///< old custom name to be converted to a char pointer
+ /* 1 more possible memory-primitives */
+
+ /* Shortcut values */
+ SLE_VAR_CHAR = SLE_VAR_I8,
+
+ /* Default combinations of variables. As savegames change, so can variables
+ * and thus it is possible that the saved value and internal size do not
+ * match and you need to specify custom combo. The defaults are listed here */
+ SLE_BOOL = SLE_FILE_I8 | SLE_VAR_BL,
+ SLE_INT8 = SLE_FILE_I8 | SLE_VAR_I8,
+ SLE_UINT8 = SLE_FILE_U8 | SLE_VAR_U8,
+ SLE_INT16 = SLE_FILE_I16 | SLE_VAR_I16,
+ SLE_UINT16 = SLE_FILE_U16 | SLE_VAR_U16,
+ SLE_INT32 = SLE_FILE_I32 | SLE_VAR_I32,
+ SLE_UINT32 = SLE_FILE_U32 | SLE_VAR_U32,
+ SLE_INT64 = SLE_FILE_I64 | SLE_VAR_I64,
+ SLE_UINT64 = SLE_FILE_U64 | SLE_VAR_U64,
+ SLE_CHAR = SLE_FILE_I8 | SLE_VAR_CHAR,
+ SLE_STRINGID = SLE_FILE_STRINGID | SLE_VAR_U16,
+ SLE_STRINGBUF = SLE_FILE_STRING | SLE_VAR_STRB,
+ SLE_STRINGBQUOTE = SLE_FILE_STRING | SLE_VAR_STRBQ,
+ SLE_STRING = SLE_FILE_STRING | SLE_VAR_STR,
+ SLE_STRINGQUOTE = SLE_FILE_STRING | SLE_VAR_STRQ,
+ SLE_NAME = SLE_FILE_STRINGID | SLE_VAR_NAME,
+
+ /* Shortcut values */
+ SLE_UINT = SLE_UINT32,
+ SLE_INT = SLE_INT32,
+ SLE_STRB = SLE_STRINGBUF,
+ SLE_STRBQ = SLE_STRINGBQUOTE,
+ SLE_STR = SLE_STRING,
+ SLE_STRQ = SLE_STRINGQUOTE,
+
+ /* 8 bits allocated for a maximum of 8 flags
+ * Flags directing saving/loading of a variable */
+ SLF_SAVE_NO = 1 << 8, ///< do not save with savegame, basically client-based
+ SLF_CONFIG_NO = 1 << 9, ///< do not save to config file
+ SLF_NETWORK_NO = 1 << 10, ///< do not synchronize over network (but it is saved if SSF_SAVE_NO is not set)
+ /* 5 more possible flags */
+};
+
+typedef uint32 VarType;
+
+enum SaveLoadTypes {
+ SL_VAR = 0,
+ SL_REF = 1,
+ SL_ARR = 2,
+ SL_STR = 3,
+ SL_LST = 4,
+ // non-normal save-load types
+ SL_WRITEBYTE = 8,
+ SL_VEH_INCLUDE = 9,
+ SL_END = 15
+};
+
+typedef byte SaveLoadType;
+
+/** SaveLoad type struct. Do NOT use this directly but use the SLE_ macros defined just below! */
+struct SaveLoad {
+ bool global; ///< should we load a global variable or a non-global one
+ SaveLoadType cmd; ///< the action to take with the saved/loaded type, All types need different action
+ VarType conv; ///< type of the variable to be saved, int
+ uint16 length; ///< (conditional) length of the variable (eg. arrays) (max array size is 65536 elements)
+ uint16 version_from; ///< save/load the variable starting from this savegame version
+ uint16 version_to; ///< save/load the variable until this savegame version
+ /* NOTE: This element either denotes the address of the variable for a global
+ * variable, or the offset within a struct which is then bound to a variable
+ * during runtime. Decision on which one to use is controlled by the function
+ * that is called to save it. address: global=true, offset: global=false */
+ void *address; ///< address of variable OR offset of variable in the struct (max offset is 65536)
+};
+
+/* Same as SaveLoad but global variables are used (for better readability); */
+typedef SaveLoad SaveLoadGlobVarList;
+
+/* Simple variables, references (pointers) and arrays */
+#define SLE_GENERAL(cmd, base, variable, type, length, from, to) {false, cmd, type, length, from, to, (void*)cpp_offsetof(base, variable)}
+#define SLE_CONDVAR(base, variable, type, from, to) SLE_GENERAL(SL_VAR, base, variable, type, 0, from, to)
+#define SLE_CONDREF(base, variable, type, from, to) SLE_GENERAL(SL_REF, base, variable, type, 0, from, to)
+#define SLE_CONDARR(base, variable, type, length, from, to) SLE_GENERAL(SL_ARR, base, variable, type, length, from, to)
+#define SLE_CONDSTR(base, variable, type, length, from, to) SLE_GENERAL(SL_STR, base, variable, type, length, from, to)
+#define SLE_CONDLST(base, variable, type, from, to) SLE_GENERAL(SL_LST, base, variable, type, 0, from, to)
+
+#define SLE_VAR(base, variable, type) SLE_CONDVAR(base, variable, type, 0, SL_MAX_VERSION)
+#define SLE_REF(base, variable, type) SLE_CONDREF(base, variable, type, 0, SL_MAX_VERSION)
+#define SLE_ARR(base, variable, type, length) SLE_CONDARR(base, variable, type, length, 0, SL_MAX_VERSION)
+#define SLE_STR(base, variable, type, length) SLE_CONDSTR(base, variable, type, length, 0, SL_MAX_VERSION)
+#define SLE_LST(base, variable, type) SLE_CONDLST(base, variable, type, 0, SL_MAX_VERSION)
+
+#define SLE_CONDNULL(length, from, to) SLE_CONDARR(NullStruct, null, SLE_FILE_U8 | SLE_VAR_NULL | SLF_CONFIG_NO, length, from, to)
+
+/* Translate values ingame to different values in the savegame and vv */
+#define SLE_WRITEBYTE(base, variable, value) SLE_GENERAL(SL_WRITEBYTE, base, variable, 0, 0, value, value)
+
+/* The same as the ones at the top, only the offset is given directly; used for unions */
+#define SLE_GENERALX(cmd, offset, type, length, param1, param2) {false, cmd, type, length, param1, param2, (void*)(offset)}
+#define SLE_CONDVARX(offset, type, from, to) SLE_GENERALX(SL_VAR, offset, type, 0, from, to)
+#define SLE_CONDARRX(offset, type, length, from, to) SLE_GENERALX(SL_ARR, offset, type, length, from, to)
+#define SLE_CONDREFX(offset, type, from, to) SLE_GENERALX(SL_REF, offset, type, 0, from, to)
+
+#define SLE_VARX(offset, type) SLE_CONDVARX(offset, type, 0, SL_MAX_VERSION)
+#define SLE_REFX(offset, type) SLE_CONDREFX(offset, type, 0, SL_MAX_VERSION)
+
+#define SLE_WRITEBYTEX(offset, something) SLE_GENERALX(SL_WRITEBYTE, offset, 0, 0, something, 0)
+#define SLE_VEH_INCLUDEX() SLE_GENERALX(SL_VEH_INCLUDE, 0, 0, 0, 0, SL_MAX_VERSION)
+
+/* End marker */
+#define SLE_END() {false, SL_END, 0, 0, 0, 0, NULL}
+
+/* Simple variables, references (pointers) and arrays, but for global variables */
+#define SLEG_GENERAL(cmd, variable, type, length, from, to) {true, cmd, type, length, from, to, (void*)&variable}
+
+#define SLEG_CONDVAR(variable, type, from, to) SLEG_GENERAL(SL_VAR, variable, type, 0, from, to)
+#define SLEG_CONDREF(variable, type, from, to) SLEG_GENERAL(SL_REF, variable, type, 0, from, to)
+#define SLEG_CONDARR(variable, type, length, from, to) SLEG_GENERAL(SL_ARR, variable, type, length, from, to)
+#define SLEG_CONDSTR(variable, type, length, from, to) SLEG_GENERAL(SL_STR, variable, type, length, from, to)
+#define SLEG_CONDLST(variable, type, from, to) SLEG_GENERAL(SL_LST, variable, type, 0, from, to)
+
+#define SLEG_VAR(variable, type) SLEG_CONDVAR(variable, type, 0, SL_MAX_VERSION)
+#define SLEG_REF(variable, type) SLEG_CONDREF(variable, type, 0, SL_MAX_VERSION)
+#define SLEG_ARR(variable, type) SLEG_CONDARR(variable, type, lengthof(variable), 0, SL_MAX_VERSION)
+#define SLEG_STR(variable, type) SLEG_CONDSTR(variable, type, lengthof(variable), 0, SL_MAX_VERSION)
+#define SLEG_LST(variable, type) SLEG_CONDLST(variable, type, 0, SL_MAX_VERSION)
+
+#define SLEG_CONDNULL(length, from, to) {true, SL_ARR, SLE_FILE_U8 | SLE_VAR_NULL | SLF_CONFIG_NO, length, from, to, (void*)NULL}
+
+#define SLEG_END() {true, SL_END, 0, 0, 0, 0, NULL}
+
+/** Checks if the savegame is below major.minor.
+ */
+static inline bool CheckSavegameVersionOldStyle(uint16 major, byte minor)
+{
+ extern uint16 _sl_version;
+ extern byte _sl_minor_version;
+ return (_sl_version < major) || (_sl_version == major && _sl_minor_version < minor);
+}
+
+/** Checks if the savegame is below version.
+ */
+static inline bool CheckSavegameVersion(uint16 version)
+{
+ extern uint16 _sl_version;
+ return _sl_version < version;
+}
+
+/** Checks if some version from/to combination falls within the range of the
+ * active savegame version */
+static inline bool SlIsObjectCurrentlyValid(uint16 version_from, uint16 version_to)
+{
+ extern const uint16 SAVEGAME_VERSION;
+ if (SAVEGAME_VERSION < version_from || SAVEGAME_VERSION > version_to) return false;
+
+ return true;
+}
+
+/* Get the NumberType of a setting. This describes the integer type
+ * as it is represented in memory
+ * @param type VarType holding information about the variable-type
+ * @return return the SLE_VAR_* part of a variable-type description */
+static inline VarType GetVarMemType(VarType type)
+{
+ return type & 0xF0; // GB(type, 4, 4) << 4;
+}
+
+/* Get the FileType of a setting. This describes the integer type
+ * as it is represented in a savegame/file
+ * @param type VarType holding information about the variable-type
+ * @param return the SLE_FILE_* part of a variable-type description */
+static inline VarType GetVarFileType(VarType type)
+{
+ return type & 0xF; // GB(type, 0, 4);
+}
+
+/** Get the address of the variable. Which one to pick depends on the object
+ * pointer. If it is NULL we are dealing with global variables so the address
+ * is taken. If non-null only the offset is stored in the union and we need
+ * to add this to the address of the object */
+static inline void *GetVariableAddress(const void *object, const SaveLoad *sld)
+{
+ return (byte*)(sld->global ? NULL : object) + (ptrdiff_t)sld->address;
+}
+
+int64 ReadValue(const void *ptr, VarType conv);
+void WriteValue(void *ptr, VarType conv, int64 val);
+
+void SlSetArrayIndex(uint index);
+int SlIterateArray();
+
+void SlAutolength(AutolengthProc *proc, void *arg);
+size_t SlGetFieldLength();
+void SlSetLength(size_t length);
+size_t SlCalcObjMemberLength(const void *object, const SaveLoad *sld);
+size_t SlCalcObjLength(const void *object, const SaveLoad *sld);
+
+byte SlReadByte();
+void SlWriteByte(byte b);
+
+void SlGlobList(const SaveLoadGlobVarList *sldg);
+void SlArray(void *array, size_t length, VarType conv);
+void SlObject(void *object, const SaveLoad *sld);
+bool SlObjectMember(void *object, const SaveLoad *sld);
+
+#endif /* SAVELOAD_H */
diff --git a/src/saveload/saveload_internal.h b/src/saveload/saveload_internal.h
new file mode 100644
index 000000000..7bb865d81
--- /dev/null
+++ b/src/saveload/saveload_internal.h
@@ -0,0 +1,40 @@
+/* $Id$ */
+
+/** @file saveload_internal.h Declaration of functions used in more save/load files */
+
+#ifndef SAVELOAD_INTERNAL_H
+#define SAVELOAD_INTERNAL_H
+
+#include "../strings_type.h"
+#include "../company_manager_face.h"
+#include "../order_base.h"
+
+void InitializeOldNames();
+StringID RemapOldStringID(StringID s);
+char *CopyFromOldName(StringID id);
+void ResetOldNames();
+
+void FixOldWaypoints();
+
+void AfterLoadWaypoints();
+void AfterLoadVehicles(bool part_of_load);
+void AfterLoadStations();
+void AfterLoadTown();
+void UpdateHousesAndTowns();
+
+void UpdateOldAircraft();
+
+void SaveViewportBeforeSaveGame();
+void ResetViewportAfterLoadGame();
+
+void ConvertOldMultiheadToNew();
+void ConnectMultiheadedTrains();
+
+extern int32 _saved_scrollpos_x;
+extern int32 _saved_scrollpos_y;
+
+CompanyManagerFace ConvertFromOldCompanyManagerFace(uint32 face);
+
+Order UnpackOldOrder(uint16 packed);
+
+#endif /* SAVELOAD_INTERNAL_H */
diff --git a/src/saveload/signs_sl.cpp b/src/saveload/signs_sl.cpp
new file mode 100644
index 000000000..ef4530dcd
--- /dev/null
+++ b/src/saveload/signs_sl.cpp
@@ -0,0 +1,49 @@
+/* $Id$ */
+
+/** @file signs_sl.cpp Code handling saving and loading of economy data */
+
+#include "../stdafx.h"
+#include "../strings_func.h"
+#include "../company_func.h"
+#include "../signs_base.h"
+#include "../signs_func.h"
+
+#include "saveload_internal.h"
+#include "saveload.h"
+
+static const SaveLoad _sign_desc[] = {
+ SLE_CONDVAR(Sign, name, SLE_NAME, 0, 83),
+ SLE_CONDSTR(Sign, name, SLE_STR, 0, 84, SL_MAX_VERSION),
+ SLE_CONDVAR(Sign, x, SLE_FILE_I16 | SLE_VAR_I32, 0, 4),
+ SLE_CONDVAR(Sign, y, SLE_FILE_I16 | SLE_VAR_I32, 0, 4),
+ SLE_CONDVAR(Sign, x, SLE_INT32, 5, SL_MAX_VERSION),
+ SLE_CONDVAR(Sign, y, SLE_INT32, 5, SL_MAX_VERSION),
+ SLE_CONDVAR(Sign, owner, SLE_UINT8, 6, SL_MAX_VERSION),
+ SLE_VAR(Sign, z, SLE_UINT8),
+ SLE_END()
+};
+
+/** Save all signs */
+static void Save_SIGN()
+{
+ Sign *si;
+
+ FOR_ALL_SIGNS(si) {
+ SlSetArrayIndex(si->index);
+ SlObject(si, _sign_desc);
+ }
+}
+
+/** Load all signs */
+static void Load_SIGN()
+{
+ int index;
+ while ((index = SlIterateArray()) != -1) {
+ Sign *si = new (index) Sign();
+ SlObject(si, _sign_desc);
+ }
+}
+
+extern const ChunkHandler _sign_chunk_handlers[] = {
+ { 'SIGN', Save_SIGN, Load_SIGN, CH_ARRAY | CH_LAST},
+};
diff --git a/src/saveload/station_sl.cpp b/src/saveload/station_sl.cpp
new file mode 100644
index 000000000..b03e59c7d
--- /dev/null
+++ b/src/saveload/station_sl.cpp
@@ -0,0 +1,227 @@
+/* $Id$ */
+
+/** @file station_sl.cpp Code handling saving and loading of economy data */
+
+#include "../stdafx.h"
+#include "../station_base.h"
+#include "../core/bitmath_func.hpp"
+#include "../core/alloc_func.hpp"
+#include "../variables.h"
+#include "../newgrf_station.h"
+
+#include "saveload.h"
+
+
+void AfterLoadStations()
+{
+ /* Update the speclists of all stations to point to the currently loaded custom stations. */
+ Station *st;
+ FOR_ALL_STATIONS(st) {
+ for (uint i = 0; i < st->num_specs; i++) {
+ if (st->speclist[i].grfid == 0) continue;
+
+ st->speclist[i].spec = GetCustomStationSpecByGrf(st->speclist[i].grfid, st->speclist[i].localidx);
+ }
+
+ for (CargoID c = 0; c < NUM_CARGO; c++) st->goods[c].cargo.InvalidateCache();
+
+ StationUpdateAnimTriggers(st);
+ }
+}
+
+static const SaveLoad _roadstop_desc[] = {
+ SLE_VAR(RoadStop, xy, SLE_UINT32),
+ SLE_CONDNULL(1, 0, 44),
+ SLE_VAR(RoadStop, status, SLE_UINT8),
+ /* Index was saved in some versions, but this is not needed */
+ SLE_CONDNULL(4, 0, 8),
+ SLE_CONDNULL(2, 0, 44),
+ SLE_CONDNULL(1, 0, 25),
+
+ SLE_REF(RoadStop, next, REF_ROADSTOPS),
+ SLE_CONDNULL(2, 0, 44),
+
+ SLE_CONDNULL(4, 0, 24),
+ SLE_CONDNULL(1, 25, 25),
+
+ SLE_END()
+};
+
+static const SaveLoad _station_desc[] = {
+ SLE_CONDVAR(Station, xy, SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
+ SLE_CONDVAR(Station, xy, SLE_UINT32, 6, SL_MAX_VERSION),
+ SLE_CONDNULL(4, 0, 5), ///< bus/lorry tile
+ SLE_CONDVAR(Station, train_tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
+ SLE_CONDVAR(Station, train_tile, SLE_UINT32, 6, SL_MAX_VERSION),
+ SLE_CONDVAR(Station, airport_tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
+ SLE_CONDVAR(Station, airport_tile, SLE_UINT32, 6, SL_MAX_VERSION),
+ SLE_CONDVAR(Station, dock_tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
+ SLE_CONDVAR(Station, dock_tile, SLE_UINT32, 6, SL_MAX_VERSION),
+ SLE_REF(Station, town, REF_TOWN),
+ SLE_VAR(Station, trainst_w, SLE_UINT8),
+ SLE_CONDVAR(Station, trainst_h, SLE_UINT8, 2, SL_MAX_VERSION),
+
+ SLE_CONDNULL(1, 0, 3), ///< alpha_order
+
+ SLE_VAR(Station, string_id, SLE_STRINGID),
+ SLE_CONDSTR(Station, name, SLE_STR, 0, 84, SL_MAX_VERSION),
+ SLE_CONDVAR(Station, indtype, SLE_UINT8, 103, SL_MAX_VERSION),
+ SLE_VAR(Station, had_vehicle_of_type, SLE_UINT16),
+
+ SLE_VAR(Station, time_since_load, SLE_UINT8),
+ SLE_VAR(Station, time_since_unload, SLE_UINT8),
+ SLE_VAR(Station, delete_ctr, SLE_UINT8),
+ SLE_VAR(Station, owner, SLE_UINT8),
+ SLE_VAR(Station, facilities, SLE_UINT8),
+ SLE_VAR(Station, airport_type, SLE_UINT8),
+
+ SLE_CONDNULL(2, 0, 5), ///< Truck/bus stop status
+ SLE_CONDNULL(1, 0, 4), ///< Blocked months
+
+ SLE_CONDVAR(Station, airport_flags, SLE_VAR_U64 | SLE_FILE_U16, 0, 2),
+ SLE_CONDVAR(Station, airport_flags, SLE_VAR_U64 | SLE_FILE_U32, 3, 45),
+ SLE_CONDVAR(Station, airport_flags, SLE_UINT64, 46, SL_MAX_VERSION),
+
+ SLE_CONDNULL(2, 0, 25), ///< last-vehicle
+ SLE_CONDVAR(Station, last_vehicle_type, SLE_UINT8, 26, SL_MAX_VERSION),
+
+ SLE_CONDNULL(2, 3, 25), ///< custom station class and id
+ SLE_CONDVAR(Station, build_date, SLE_FILE_U16 | SLE_VAR_I32, 3, 30),
+ SLE_CONDVAR(Station, build_date, SLE_INT32, 31, SL_MAX_VERSION),
+
+ SLE_CONDREF(Station, bus_stops, REF_ROADSTOPS, 6, SL_MAX_VERSION),
+ SLE_CONDREF(Station, truck_stops, REF_ROADSTOPS, 6, SL_MAX_VERSION),
+
+ /* Used by newstations for graphic variations */
+ SLE_CONDVAR(Station, random_bits, SLE_UINT16, 27, SL_MAX_VERSION),
+ SLE_CONDVAR(Station, waiting_triggers, SLE_UINT8, 27, SL_MAX_VERSION),
+ SLE_CONDVAR(Station, num_specs, SLE_UINT8, 27, SL_MAX_VERSION),
+
+ SLE_CONDLST(Station, loading_vehicles, REF_VEHICLE, 57, SL_MAX_VERSION),
+
+ /* reserve extra space in savegame here. (currently 32 bytes) */
+ SLE_CONDNULL(32, 2, SL_MAX_VERSION),
+
+ SLE_END()
+};
+
+static uint16 _waiting_acceptance;
+static uint16 _cargo_source;
+static uint32 _cargo_source_xy;
+static uint16 _cargo_days;
+static Money _cargo_feeder_share;
+
+static const SaveLoad _station_speclist_desc[] = {
+ SLE_CONDVAR(StationSpecList, grfid, SLE_UINT32, 27, SL_MAX_VERSION),
+ SLE_CONDVAR(StationSpecList, localidx, SLE_UINT8, 27, SL_MAX_VERSION),
+
+ SLE_END()
+};
+
+
+void SaveLoad_STNS(Station *st)
+{
+ static const SaveLoad _goods_desc[] = {
+ SLEG_CONDVAR( _waiting_acceptance, SLE_UINT16, 0, 67),
+ SLE_CONDVAR(GoodsEntry, acceptance_pickup, SLE_UINT8, 68, SL_MAX_VERSION),
+ SLE_CONDNULL(2, 51, 67),
+ SLE_VAR(GoodsEntry, days_since_pickup, SLE_UINT8),
+ SLE_VAR(GoodsEntry, rating, SLE_UINT8),
+ SLEG_CONDVAR( _cargo_source, SLE_FILE_U8 | SLE_VAR_U16, 0, 6),
+ SLEG_CONDVAR( _cargo_source, SLE_UINT16, 7, 67),
+ SLEG_CONDVAR( _cargo_source_xy, SLE_UINT32, 44, 67),
+ SLEG_CONDVAR( _cargo_days, SLE_UINT8, 0, 67),
+ SLE_VAR(GoodsEntry, last_speed, SLE_UINT8),
+ SLE_VAR(GoodsEntry, last_age, SLE_UINT8),
+ SLEG_CONDVAR( _cargo_feeder_share, SLE_FILE_U32 | SLE_VAR_I64, 14, 64),
+ SLEG_CONDVAR( _cargo_feeder_share, SLE_INT64, 65, 67),
+ SLE_CONDLST(GoodsEntry, cargo.packets, REF_CARGO_PACKET, 68, SL_MAX_VERSION),
+
+ SLE_END()
+ };
+
+
+ SlObject(st, _station_desc);
+
+ _waiting_acceptance = 0;
+
+ uint num_cargo = CheckSavegameVersion(55) ? 12 : NUM_CARGO;
+ for (CargoID i = 0; i < num_cargo; i++) {
+ GoodsEntry *ge = &st->goods[i];
+ SlObject(ge, _goods_desc);
+ if (CheckSavegameVersion(68)) {
+ SB(ge->acceptance_pickup, GoodsEntry::ACCEPTANCE, 1, HasBit(_waiting_acceptance, 15));
+ if (GB(_waiting_acceptance, 0, 12) != 0) {
+ /* Don't construct the packet with station here, because that'll fail with old savegames */
+ CargoPacket *cp = new CargoPacket();
+ /* In old versions, enroute_from used 0xFF as INVALID_STATION */
+ cp->source = (CheckSavegameVersion(7) && _cargo_source == 0xFF) ? INVALID_STATION : _cargo_source;
+ cp->count = GB(_waiting_acceptance, 0, 12);
+ cp->days_in_transit = _cargo_days;
+ cp->feeder_share = _cargo_feeder_share;
+ cp->source_xy = _cargo_source_xy;
+ cp->days_in_transit = _cargo_days;
+ cp->feeder_share = _cargo_feeder_share;
+ SB(ge->acceptance_pickup, GoodsEntry::PICKUP, 1, 1);
+ ge->cargo.Append(cp);
+ }
+ }
+ }
+
+ if (st->num_specs != 0) {
+ /* Allocate speclist memory when loading a game */
+ if (st->speclist == NULL) st->speclist = CallocT<StationSpecList>(st->num_specs);
+ for (uint i = 0; i < st->num_specs; i++) {
+ SlObject(&st->speclist[i], _station_speclist_desc);
+ }
+ }
+}
+
+static void Save_STNS()
+{
+ Station *st;
+ /* Write the stations */
+ FOR_ALL_STATIONS(st) {
+ SlSetArrayIndex(st->index);
+ SlAutolength((AutolengthProc*)SaveLoad_STNS, st);
+ }
+}
+
+static void Load_STNS()
+{
+ int index;
+ while ((index = SlIterateArray()) != -1) {
+ Station *st = new (index) Station();
+
+ SaveLoad_STNS(st);
+ }
+
+ /* This is to ensure all pointers are within the limits of _stations_size */
+ if (_station_tick_ctr > GetMaxStationIndex()) _station_tick_ctr = 0;
+}
+
+static void Save_ROADSTOP()
+{
+ RoadStop *rs;
+
+ FOR_ALL_ROADSTOPS(rs) {
+ SlSetArrayIndex(rs->index);
+ SlObject(rs, _roadstop_desc);
+ }
+}
+
+static void Load_ROADSTOP()
+{
+ int index;
+
+ while ((index = SlIterateArray()) != -1) {
+ RoadStop *rs = new (index) RoadStop(INVALID_TILE);
+
+ SlObject(rs, _roadstop_desc);
+ }
+}
+
+extern const ChunkHandler _station_chunk_handlers[] = {
+ { 'STNS', Save_STNS, Load_STNS, CH_ARRAY },
+ { 'ROAD', Save_ROADSTOP, Load_ROADSTOP, CH_ARRAY | CH_LAST},
+};
diff --git a/src/saveload/strings_sl.cpp b/src/saveload/strings_sl.cpp
new file mode 100644
index 000000000..f436bdedf
--- /dev/null
+++ b/src/saveload/strings_sl.cpp
@@ -0,0 +1,126 @@
+/* $Id$ */
+
+/** @file strings_sl.cpp Code handling saving and loading of strings */
+
+#include "../stdafx.h"
+#include "../strings_type.h"
+#include "../core/math_func.hpp"
+#include "../core/bitmath_func.hpp"
+#include "../core/alloc_func.hpp"
+#include "../string_func.h"
+
+#include "table/strings.h"
+
+#include "saveload.h"
+
+/**
+ * Remap a string ID from the old format to the new format
+ * @param s StringID that requires remapping
+ * @return translated ID
+ */
+StringID RemapOldStringID(StringID s)
+{
+ switch (s) {
+ case 0x0006: return STR_SV_EMPTY;
+ case 0x7000: return STR_SV_UNNAMED;
+ case 0x70E4: return SPECSTR_PLAYERNAME_ENGLISH;
+ case 0x70E9: return SPECSTR_PLAYERNAME_ENGLISH;
+ case 0x8864: return STR_SV_TRAIN_NAME;
+ case 0x902B: return STR_SV_ROADVEH_NAME;
+ case 0x9830: return STR_SV_SHIP_NAME;
+ case 0xA02F: return STR_SV_AIRCRAFT_NAME;
+
+ default:
+ if (IsInsideMM(s, 0x300F, 0x3030)) {
+ return s - 0x300F + STR_SV_STNAME;
+ } else {
+ return s;
+ }
+ }
+}
+
+/** Location to load the old names to. */
+char *_old_name_array = NULL;
+
+/**
+ * Copy and convert old custom names to UTF-8.
+ * They were all stored in a 512 by 32 long string array and are
+ * now stored with stations, waypoints and other places with names.
+ * @param id the StringID of the custom name to clone.
+ * @return the clones custom name.
+ */
+char *CopyFromOldName(StringID id)
+{
+ /* Is this name an (old) custom name? */
+ if (GB(id, 11, 5) != 15) return NULL;
+
+ if (CheckSavegameVersion(37)) {
+ /* Old names were 32 characters long, so 128 characters should be
+ * plenty to allow for expansion when converted to UTF-8. */
+ char tmp[128];
+ const char *strfrom = &_old_name_array[32 * GB(id, 0, 9)];
+ char *strto = tmp;
+
+ for (; *strfrom != '\0'; strfrom++) {
+ WChar c = (byte)*strfrom;
+
+ /* Map from non-ISO8859-15 characters to UTF-8. */
+ switch (c) {
+ case 0xA4: c = 0x20AC; break; // Euro
+ case 0xA6: c = 0x0160; break; // S with caron
+ case 0xA8: c = 0x0161; break; // s with caron
+ case 0xB4: c = 0x017D; break; // Z with caron
+ case 0xB8: c = 0x017E; break; // z with caron
+ case 0xBC: c = 0x0152; break; // OE ligature
+ case 0xBD: c = 0x0153; break; // oe ligature
+ case 0xBE: c = 0x0178; break; // Y with diaresis
+ default: break;
+ }
+
+ /* Check character will fit into our buffer. */
+ if (strto + Utf8CharLen(c) > lastof(tmp)) break;
+
+ strto += Utf8Encode(strto, c);
+ }
+
+ /* Terminate the new string and copy it back to the name array */
+ *strto = '\0';
+
+ return strdup(tmp);
+ } else {
+ /* Name will already be in UTF-8. */
+ return strdup(&_old_name_array[32 * GB(id, 0, 9)]);
+ }
+}
+
+/**
+ * Free the memory of the old names array.
+ * Should be called once the old names have all been converted.
+ */
+void ResetOldNames()
+{
+ free(_old_name_array);
+ _old_name_array = NULL;
+}
+
+/**
+ * Initialize the old names table memory.
+ */
+void InitializeOldNames()
+{
+ free(_old_name_array);
+ _old_name_array = CallocT<char>(512 * 32);
+}
+
+static void Load_NAME()
+{
+ int index;
+
+ while ((index = SlIterateArray()) != -1) {
+ SlArray(&_old_name_array[32 * index], SlGetFieldLength(), SLE_UINT8);
+ }
+}
+
+extern const ChunkHandler _name_chunk_handlers[] = {
+ { 'NAME', NULL, Load_NAME, CH_ARRAY | CH_LAST},
+};
diff --git a/src/saveload/subsidy_sl.cpp b/src/saveload/subsidy_sl.cpp
new file mode 100644
index 000000000..95fac25c7
--- /dev/null
+++ b/src/saveload/subsidy_sl.cpp
@@ -0,0 +1,43 @@
+/* $Id$ */
+
+/** @file subsidy_sl.cpp Code handling saving and loading of subsidies */
+
+#include "../stdafx.h"
+#include "../economy_func.h"
+
+#include "saveload.h"
+
+static const SaveLoad _subsidies_desc[] = {
+ SLE_VAR(Subsidy, cargo_type, SLE_UINT8),
+ SLE_VAR(Subsidy, age, SLE_UINT8),
+ SLE_CONDVAR(Subsidy, from, SLE_FILE_U8 | SLE_VAR_U16, 0, 4),
+ SLE_CONDVAR(Subsidy, from, SLE_UINT16, 5, SL_MAX_VERSION),
+ SLE_CONDVAR(Subsidy, to, SLE_FILE_U8 | SLE_VAR_U16, 0, 4),
+ SLE_CONDVAR(Subsidy, to, SLE_UINT16, 5, SL_MAX_VERSION),
+ SLE_END()
+};
+
+void Save_SUBS()
+{
+ int i;
+ Subsidy *s;
+
+ for (i = 0; i != lengthof(_subsidies); i++) {
+ s = &_subsidies[i];
+ if (s->cargo_type != CT_INVALID) {
+ SlSetArrayIndex(i);
+ SlObject(s, _subsidies_desc);
+ }
+ }
+}
+
+void Load_SUBS()
+{
+ int index;
+ while ((index = SlIterateArray()) != -1)
+ SlObject(&_subsidies[index], _subsidies_desc);
+}
+
+extern const ChunkHandler _subsidy_chunk_handlers[] = {
+ { 'SUBS', Save_SUBS, Load_SUBS, CH_ARRAY},
+};
diff --git a/src/saveload/town_sl.cpp b/src/saveload/town_sl.cpp
new file mode 100644
index 000000000..eaa3edaa3
--- /dev/null
+++ b/src/saveload/town_sl.cpp
@@ -0,0 +1,210 @@
+/* $Id$ */
+
+/** @file town_sl.cpp Code handling saving and loading of towns and houses */
+
+#include "../stdafx.h"
+#include "../town.h"
+#include "../newgrf_house.h"
+#include "../newgrf_commons.h"
+#include "../variables.h"
+#include "../tile_map.h"
+#include "../town_map.h"
+
+#include "saveload.h"
+
+extern uint _total_towns;
+
+/**
+ * Check and update town and house values.
+ *
+ * Checked are the HouseIDs. Updated are the
+ * town population the number of houses per
+ * town, the town radius and the max passengers
+ * of the town.
+ */
+void UpdateHousesAndTowns()
+{
+ Town *town;
+ InitializeBuildingCounts();
+
+ /* Reset town population and num_houses */
+ FOR_ALL_TOWNS(town) {
+ town->population = 0;
+ town->num_houses = 0;
+ }
+
+ for (TileIndex t = 0; t < MapSize(); t++) {
+ HouseID house_id;
+
+ if (!IsTileType(t, MP_HOUSE)) continue;
+
+ house_id = GetHouseType(t);
+ if (!GetHouseSpecs(house_id)->enabled && house_id >= NEW_HOUSE_OFFSET) {
+ /* The specs for this type of house are not available any more, so
+ * replace it with the substitute original house type. */
+ house_id = _house_mngr.GetSubstituteID(house_id);
+ SetHouseType(t, house_id);
+ }
+
+ town = GetTownByTile(t);
+ IncreaseBuildingCount(town, house_id);
+ if (IsHouseCompleted(t)) town->population += GetHouseSpecs(house_id)->population;
+
+ /* Increase the number of houses for every house, but only once. */
+ if (GetHouseNorthPart(house_id) == 0) town->num_houses++;
+ }
+
+ /* Update the population and num_house dependant values */
+ FOR_ALL_TOWNS(town) {
+ UpdateTownRadius(town);
+ UpdateTownMaxPass(town);
+ }
+}
+
+/** Save and load of towns. */
+static const SaveLoad _town_desc[] = {
+ SLE_CONDVAR(Town, xy, SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
+ SLE_CONDVAR(Town, xy, SLE_UINT32, 6, SL_MAX_VERSION),
+
+ SLE_CONDNULL(2, 0, 2), ///< population, no longer in use
+ SLE_CONDNULL(4, 3, 84), ///< population, no longer in use
+ SLE_CONDNULL(2, 0, 91), ///< num_houses, no longer in use
+
+ SLE_CONDVAR(Town, townnamegrfid, SLE_UINT32, 66, SL_MAX_VERSION),
+ SLE_VAR(Town, townnametype, SLE_UINT16),
+ SLE_VAR(Town, townnameparts, SLE_UINT32),
+ SLE_CONDSTR(Town, name, SLE_STR, 0, 84, SL_MAX_VERSION),
+
+ SLE_VAR(Town, flags12, SLE_UINT8),
+ SLE_CONDVAR(Town, statues, SLE_FILE_U8 | SLE_VAR_U16, 0, 103),
+ SLE_CONDVAR(Town, statues, SLE_UINT16, 104, SL_MAX_VERSION),
+
+ SLE_CONDNULL(1, 0, 1), ///< sort_index, no longer in use
+
+ SLE_CONDVAR(Town, have_ratings, SLE_FILE_U8 | SLE_VAR_U16, 0, 103),
+ SLE_CONDVAR(Town, have_ratings, SLE_UINT16, 104, SL_MAX_VERSION),
+ SLE_CONDARR(Town, ratings, SLE_INT16, 8, 0, 103),
+ SLE_CONDARR(Town, ratings, SLE_INT16, MAX_COMPANIES, 104, SL_MAX_VERSION),
+ /* failed bribe attempts are stored since savegame format 4 */
+ SLE_CONDARR(Town, unwanted, SLE_INT8, 8, 4, 103),
+ SLE_CONDARR(Town, unwanted, SLE_INT8, MAX_COMPANIES, 104, SL_MAX_VERSION),
+
+ SLE_CONDVAR(Town, max_pass, SLE_FILE_U16 | SLE_VAR_U32, 0, 8),
+ SLE_CONDVAR(Town, max_mail, SLE_FILE_U16 | SLE_VAR_U32, 0, 8),
+ SLE_CONDVAR(Town, new_max_pass, SLE_FILE_U16 | SLE_VAR_U32, 0, 8),
+ SLE_CONDVAR(Town, new_max_mail, SLE_FILE_U16 | SLE_VAR_U32, 0, 8),
+ SLE_CONDVAR(Town, act_pass, SLE_FILE_U16 | SLE_VAR_U32, 0, 8),
+ SLE_CONDVAR(Town, act_mail, SLE_FILE_U16 | SLE_VAR_U32, 0, 8),
+ SLE_CONDVAR(Town, new_act_pass, SLE_FILE_U16 | SLE_VAR_U32, 0, 8),
+ SLE_CONDVAR(Town, new_act_mail, SLE_FILE_U16 | SLE_VAR_U32, 0, 8),
+
+ SLE_CONDVAR(Town, max_pass, SLE_UINT32, 9, SL_MAX_VERSION),
+ SLE_CONDVAR(Town, max_mail, SLE_UINT32, 9, SL_MAX_VERSION),
+ SLE_CONDVAR(Town, new_max_pass, SLE_UINT32, 9, SL_MAX_VERSION),
+ SLE_CONDVAR(Town, new_max_mail, SLE_UINT32, 9, SL_MAX_VERSION),
+ SLE_CONDVAR(Town, act_pass, SLE_UINT32, 9, SL_MAX_VERSION),
+ SLE_CONDVAR(Town, act_mail, SLE_UINT32, 9, SL_MAX_VERSION),
+ SLE_CONDVAR(Town, new_act_pass, SLE_UINT32, 9, SL_MAX_VERSION),
+ SLE_CONDVAR(Town, new_act_mail, SLE_UINT32, 9, SL_MAX_VERSION),
+
+ SLE_VAR(Town, pct_pass_transported, SLE_UINT8),
+ SLE_VAR(Town, pct_mail_transported, SLE_UINT8),
+
+ SLE_VAR(Town, act_food, SLE_UINT16),
+ SLE_VAR(Town, act_water, SLE_UINT16),
+ SLE_VAR(Town, new_act_food, SLE_UINT16),
+ SLE_VAR(Town, new_act_water, SLE_UINT16),
+
+ SLE_CONDVAR(Town, time_until_rebuild, SLE_UINT8, 0, 53),
+ SLE_CONDVAR(Town, grow_counter, SLE_UINT8, 0, 53),
+ SLE_CONDVAR(Town, growth_rate, SLE_UINT8, 0, 53),
+
+ SLE_CONDVAR(Town, time_until_rebuild, SLE_UINT16, 54, SL_MAX_VERSION),
+ SLE_CONDVAR(Town, grow_counter, SLE_UINT16, 54, SL_MAX_VERSION),
+ SLE_CONDVAR(Town, growth_rate, SLE_INT16, 54, SL_MAX_VERSION),
+
+ SLE_VAR(Town, fund_buildings_months, SLE_UINT8),
+ SLE_VAR(Town, road_build_months, SLE_UINT8),
+
+ SLE_CONDVAR(Town, exclusivity, SLE_UINT8, 2, SL_MAX_VERSION),
+ SLE_CONDVAR(Town, exclusive_counter, SLE_UINT8, 2, SL_MAX_VERSION),
+
+ SLE_CONDVAR(Town, larger_town, SLE_BOOL, 56, SL_MAX_VERSION),
+
+ /* reserve extra space in savegame here. (currently 30 bytes) */
+ SLE_CONDNULL(30, 2, SL_MAX_VERSION),
+
+ SLE_END()
+};
+
+/* Save and load the mapping between the house id on the map, and the grf file
+ * it came from. */
+static const SaveLoad _house_id_mapping_desc[] = {
+ SLE_VAR(EntityIDMapping, grfid, SLE_UINT32),
+ SLE_VAR(EntityIDMapping, entity_id, SLE_UINT8),
+ SLE_VAR(EntityIDMapping, substitute_id, SLE_UINT8),
+ SLE_END()
+};
+
+static void Save_HOUSEIDS()
+{
+ uint j = _house_mngr.GetMaxMapping();
+
+ for (uint i = 0; i < j; i++) {
+ SlSetArrayIndex(i);
+ SlObject(&_house_mngr.mapping_ID[i], _house_id_mapping_desc);
+ }
+}
+
+static void Load_HOUSEIDS()
+{
+ int index;
+
+ _house_mngr.ResetMapping();
+ uint max_id = _house_mngr.GetMaxMapping();
+
+ while ((index = SlIterateArray()) != -1) {
+ if ((uint)index >= max_id) break;
+ SlObject(&_house_mngr.mapping_ID[index], _house_id_mapping_desc);
+ }
+}
+
+static void Save_TOWN()
+{
+ Town *t;
+
+ FOR_ALL_TOWNS(t) {
+ SlSetArrayIndex(t->index);
+ SlObject(t, _town_desc);
+ }
+}
+
+static void Load_TOWN()
+{
+ int index;
+
+ _total_towns = 0;
+
+ while ((index = SlIterateArray()) != -1) {
+ Town *t = new (index) Town();
+ SlObject(t, _town_desc);
+
+ _total_towns++;
+ }
+
+ /* This is to ensure all pointers are within the limits of
+ * the size of the TownPool */
+ if (_cur_town_ctr > GetMaxTownIndex())
+ _cur_town_ctr = 0;
+}
+
+void AfterLoadTown()
+{
+ Town *t;
+ FOR_ALL_TOWNS(t) t->InitializeLayout();
+}
+
+extern const ChunkHandler _town_chunk_handlers[] = {
+ { 'HIDS', Save_HOUSEIDS, Load_HOUSEIDS, CH_ARRAY },
+ { 'CITY', Save_TOWN, Load_TOWN, CH_ARRAY | CH_LAST},
+};
diff --git a/src/saveload/vehicle_sl.cpp b/src/saveload/vehicle_sl.cpp
new file mode 100644
index 000000000..33f043b45
--- /dev/null
+++ b/src/saveload/vehicle_sl.cpp
@@ -0,0 +1,670 @@
+/* $Id$ */
+
+/** @file vehicle_sl.cpp Code handling saving and loading of vehicles */
+
+#include "../stdafx.h"
+#include "../vehicle_base.h"
+#include "../vehicle_func.h"
+#include "../train.h"
+#include "../roadveh.h"
+#include "../ship.h"
+#include "../aircraft.h"
+#include "../effectvehicle_base.h"
+
+#include "saveload.h"
+
+#include <map>
+
+/*
+ * Link front and rear multiheaded engines to each other
+ * This is done when loading a savegame
+ */
+void ConnectMultiheadedTrains()
+{
+ Vehicle *v;
+
+ FOR_ALL_VEHICLES(v) {
+ if (v->type == VEH_TRAIN) {
+ v->u.rail.other_multiheaded_part = NULL;
+ }
+ }
+
+ FOR_ALL_VEHICLES(v) {
+ if (v->type == VEH_TRAIN && (IsFrontEngine(v) || IsFreeWagon(v))) {
+ /* Two ways to associate multiheaded parts to each other:
+ * sequential-matching: Trains shall be arranged to look like <..>..<..>..<..>..
+ * bracket-matching: Free vehicle chains shall be arranged to look like ..<..<..>..<..>..>..
+ *
+ * Note: Old savegames might contain chains which do not comply with these rules, e.g.
+ * - the front and read parts have invalid orders
+ * - different engine types might be combined
+ * - there might be different amounts of front and rear parts.
+ *
+ * Note: The multiheaded parts need to be matched exactly like they are matched on the server, else desyncs will occur.
+ * This is why two matching strategies are needed.
+ */
+
+ bool sequential_matching = IsFrontEngine(v);
+
+ for (Vehicle *u = v; u != NULL; u = GetNextVehicle(u)) {
+ if (u->u.rail.other_multiheaded_part != NULL) continue; // we already linked this one
+
+ if (IsMultiheaded(u)) {
+ if (!IsTrainEngine(u)) {
+ /* we got a rear car without a front car. We will convert it to a front one */
+ SetTrainEngine(u);
+ u->spritenum--;
+ }
+
+ /* Find a matching back part */
+ EngineID eid = u->engine_type;
+ Vehicle *w;
+ if (sequential_matching) {
+ for (w = GetNextVehicle(u); w != NULL; w = GetNextVehicle(w)) {
+ if (w->engine_type != eid || w->u.rail.other_multiheaded_part != NULL || !IsMultiheaded(w)) continue;
+
+ /* we found a car to partner with this engine. Now we will make sure it face the right way */
+ if (IsTrainEngine(w)) {
+ ClearTrainEngine(w);
+ w->spritenum++;
+ }
+ break;
+ }
+ } else {
+ uint stack_pos = 0;
+ for (w = GetNextVehicle(u); w != NULL; w = GetNextVehicle(w)) {
+ if (w->engine_type != eid || w->u.rail.other_multiheaded_part != NULL || !IsMultiheaded(w)) continue;
+
+ if (IsTrainEngine(w)) {
+ stack_pos++;
+ } else {
+ if (stack_pos == 0) break;
+ stack_pos--;
+ }
+ }
+ }
+
+ if (w != NULL) {
+ w->u.rail.other_multiheaded_part = u;
+ u->u.rail.other_multiheaded_part = w;
+ } else {
+ /* we got a front car and no rear cars. We will fake this one for forget that it should have been multiheaded */
+ ClearMultiheaded(u);
+ }
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Converts all trains to the new subtype format introduced in savegame 16.2
+ * It also links multiheaded engines or make them forget they are multiheaded if no suitable partner is found
+ */
+void ConvertOldMultiheadToNew()
+{
+ Vehicle *v;
+ FOR_ALL_VEHICLES(v) {
+ if (v->type == VEH_TRAIN) {
+ SetBit(v->subtype, 7); // indicates that it's the old format and needs to be converted in the next loop
+ }
+ }
+
+ FOR_ALL_VEHICLES(v) {
+ if (v->type == VEH_TRAIN) {
+ if (HasBit(v->subtype, 7) && ((v->subtype & ~0x80) == 0 || (v->subtype & ~0x80) == 4)) {
+ for (Vehicle *u = v; u != NULL; u = u->Next()) {
+ const RailVehicleInfo *rvi = RailVehInfo(u->engine_type);
+
+ ClrBit(u->subtype, 7);
+ switch (u->subtype) {
+ case 0: /* TS_Front_Engine */
+ if (rvi->railveh_type == RAILVEH_MULTIHEAD) SetMultiheaded(u);
+ SetFrontEngine(u);
+ SetTrainEngine(u);
+ break;
+
+ case 1: /* TS_Artic_Part */
+ u->subtype = 0;
+ SetArticulatedPart(u);
+ break;
+
+ case 2: /* TS_Not_First */
+ u->subtype = 0;
+ if (rvi->railveh_type == RAILVEH_WAGON) {
+ // normal wagon
+ SetTrainWagon(u);
+ break;
+ }
+ if (rvi->railveh_type == RAILVEH_MULTIHEAD && rvi->image_index == u->spritenum - 1) {
+ // rear end of a multiheaded engine
+ SetMultiheaded(u);
+ break;
+ }
+ if (rvi->railveh_type == RAILVEH_MULTIHEAD) SetMultiheaded(u);
+ SetTrainEngine(u);
+ break;
+
+ case 4: /* TS_Free_Car */
+ u->subtype = 0;
+ SetTrainWagon(u);
+ SetFreeWagon(u);
+ break;
+ default: NOT_REACHED(); break;
+ }
+ }
+ }
+ }
+ }
+}
+
+
+/** need to be called to load aircraft from old version */
+void UpdateOldAircraft()
+{
+ /* set airport_flags to 0 for all airports just to be sure */
+ Station *st;
+ FOR_ALL_STATIONS(st) {
+ st->airport_flags = 0; // reset airport
+ }
+
+ Vehicle *v_oldstyle;
+ FOR_ALL_VEHICLES(v_oldstyle) {
+ /* airplane has another vehicle with subtype 4 (shadow), helicopter also has 3 (rotor)
+ * skip those */
+ if (v_oldstyle->type == VEH_AIRCRAFT && IsNormalAircraft(v_oldstyle)) {
+ /* airplane in terminal stopped doesn't hurt anyone, so goto next */
+ if (v_oldstyle->vehstatus & VS_STOPPED && v_oldstyle->u.air.state == 0) {
+ v_oldstyle->u.air.state = HANGAR;
+ continue;
+ }
+
+ AircraftLeaveHangar(v_oldstyle); // make airplane visible if it was in a depot for example
+ v_oldstyle->vehstatus &= ~VS_STOPPED; // make airplane moving
+ v_oldstyle->u.air.state = FLYING;
+ AircraftNextAirportPos_and_Order(v_oldstyle); // move it to the entry point of the airport
+ GetNewVehiclePosResult gp = GetNewVehiclePos(v_oldstyle);
+ v_oldstyle->tile = 0; // aircraft in air is tile=0
+
+ /* correct speed of helicopter-rotors */
+ if (v_oldstyle->subtype == AIR_HELICOPTER) v_oldstyle->Next()->Next()->cur_speed = 32;
+
+ /* set new position x,y,z */
+ SetAircraftPosition(v_oldstyle, gp.x, gp.y, GetAircraftFlyingAltitude(v_oldstyle));
+ }
+ }
+}
+
+/** Called after load to update coordinates */
+void AfterLoadVehicles(bool part_of_load)
+{
+ Vehicle *v;
+
+ FOR_ALL_VEHICLES(v) {
+ /* Reinstate the previous pointer */
+ if (v->Next() != NULL) v->Next()->previous = v;
+ if (v->NextShared() != NULL) v->NextShared()->previous_shared = v;
+
+ v->UpdateDeltaXY(v->direction);
+
+ if (part_of_load) v->fill_percent_te_id = INVALID_TE_ID;
+ v->first = NULL;
+ if (v->type == VEH_TRAIN) v->u.rail.first_engine = INVALID_ENGINE;
+ if (v->type == VEH_ROAD) v->u.road.first_engine = INVALID_ENGINE;
+
+ v->cargo.InvalidateCache();
+ }
+
+ /* AfterLoadVehicles may also be called in case of NewGRF reload, in this
+ * case we may not convert orders again. */
+ if (part_of_load) {
+ /* Create shared vehicle chain for very old games (pre 5,2) and create
+ * OrderList from shared vehicle chains. For this to work correctly, the
+ * following conditions must be fulfilled:
+ * a) both next_shared and previous_shared are not set for pre 5,2 games
+ * b) both next_shared and previous_shared are set for later games
+ */
+ std::map<Order*, OrderList*> mapping;
+
+ FOR_ALL_VEHICLES(v) {
+ if (v->orders.old != NULL) {
+ if (CheckSavegameVersion(105)) { // Pre-105 didn't save an OrderList
+ if (mapping[v->orders.old] == NULL) {
+ /* This adds the whole shared vehicle chain for case b */
+ v->orders.list = mapping[v->orders.old] = new OrderList(v->orders.old, v);
+ } else {
+ v->orders.list = mapping[v->orders.old];
+ /* For old games (case a) we must create the shared vehicle chain */
+ if (CheckSavegameVersionOldStyle(5, 2)) {
+ v->AddToShared(v->orders.list->GetFirstSharedVehicle());
+ }
+ }
+ } else { // OrderList was saved as such, only recalculate not saved values
+ if (v->PreviousShared() == NULL) {
+ new (v->orders.list) OrderList(v->orders.list->GetFirstOrder(), v);
+ }
+ }
+ }
+ }
+ }
+
+ FOR_ALL_VEHICLES(v) {
+ /* Fill the first pointers */
+ if (v->Previous() == NULL) {
+ for (Vehicle *u = v; u != NULL; u = u->Next()) {
+ u->first = v;
+ }
+ }
+ }
+
+ FOR_ALL_VEHICLES(v) {
+ assert(v->first != NULL);
+
+ if (v->type == VEH_TRAIN && (IsFrontEngine(v) || IsFreeWagon(v))) {
+ if (IsFrontEngine(v)) v->u.rail.last_speed = v->cur_speed; // update displayed train speed
+ TrainConsistChanged(v, false);
+ } else if (v->type == VEH_ROAD && IsRoadVehFront(v)) {
+ RoadVehUpdateCache(v);
+ }
+ }
+
+ /* Stop non-front engines */
+ FOR_ALL_VEHICLES(v) {
+ if (v->type == VEH_TRAIN && IsTrainEngine(v) && !IsFrontEngine(v)) v->vehstatus |= VS_STOPPED;
+ }
+
+ FOR_ALL_VEHICLES(v) {
+ switch (v->type) {
+ case VEH_ROAD:
+ v->u.road.roadtype = HasBit(EngInfo(v->engine_type)->misc_flags, EF_ROAD_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD;
+ v->u.road.compatible_roadtypes = RoadTypeToRoadTypes(v->u.road.roadtype);
+ /* FALL THROUGH */
+ case VEH_TRAIN:
+ case VEH_SHIP:
+ v->cur_image = v->GetImage(v->direction);
+ break;
+
+ case VEH_AIRCRAFT:
+ if (IsNormalAircraft(v)) {
+ v->cur_image = v->GetImage(v->direction);
+
+ /* The plane's shadow will have the same image as the plane */
+ Vehicle *shadow = v->Next();
+ shadow->cur_image = v->cur_image;
+
+ /* In the case of a helicopter we will update the rotor sprites */
+ if (v->subtype == AIR_HELICOPTER) {
+ Vehicle *rotor = shadow->Next();
+ rotor->cur_image = GetRotorImage(v);
+ }
+
+ UpdateAircraftCache(v);
+ }
+ break;
+ default: break;
+ }
+
+ v->left_coord = INVALID_COORD;
+ VehiclePositionChanged(v);
+ }
+}
+
+static uint8 _cargo_days;
+static uint16 _cargo_source;
+static uint32 _cargo_source_xy;
+static uint16 _cargo_count;
+static uint16 _cargo_paid_for;
+static Money _cargo_feeder_share;
+static uint32 _cargo_loaded_at_xy;
+
+/**
+ * Make it possible to make the saveload tables "friends" of other classes.
+ * @param vt the vehicle type. Can be VEH_END for the common vehicle description data
+ * @return the saveload description
+ */
+const SaveLoad *GetVehicleDescription(VehicleType vt)
+{
+ /** Save and load of vehicles */
+ static const SaveLoad _common_veh_desc[] = {
+ SLE_VAR(Vehicle, subtype, SLE_UINT8),
+
+ SLE_REF(Vehicle, next, REF_VEHICLE_OLD),
+ SLE_CONDVAR(Vehicle, name, SLE_NAME, 0, 83),
+ SLE_CONDSTR(Vehicle, name, SLE_STR, 0, 84, SL_MAX_VERSION),
+ SLE_CONDVAR(Vehicle, unitnumber, SLE_FILE_U8 | SLE_VAR_U16, 0, 7),
+ SLE_CONDVAR(Vehicle, unitnumber, SLE_UINT16, 8, SL_MAX_VERSION),
+ SLE_VAR(Vehicle, owner, SLE_UINT8),
+ SLE_CONDVAR(Vehicle, tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
+ SLE_CONDVAR(Vehicle, tile, SLE_UINT32, 6, SL_MAX_VERSION),
+ SLE_CONDVAR(Vehicle, dest_tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
+ SLE_CONDVAR(Vehicle, dest_tile, SLE_UINT32, 6, SL_MAX_VERSION),
+
+ SLE_CONDVAR(Vehicle, x_pos, SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
+ SLE_CONDVAR(Vehicle, x_pos, SLE_UINT32, 6, SL_MAX_VERSION),
+ SLE_CONDVAR(Vehicle, y_pos, SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
+ SLE_CONDVAR(Vehicle, y_pos, SLE_UINT32, 6, SL_MAX_VERSION),
+ SLE_VAR(Vehicle, z_pos, SLE_UINT8),
+ SLE_VAR(Vehicle, direction, SLE_UINT8),
+
+ SLE_CONDNULL(2, 0, 57),
+ SLE_VAR(Vehicle, spritenum, SLE_UINT8),
+ SLE_CONDNULL(5, 0, 57),
+ SLE_VAR(Vehicle, engine_type, SLE_UINT16),
+
+ SLE_VAR(Vehicle, max_speed, SLE_UINT16),
+ SLE_VAR(Vehicle, cur_speed, SLE_UINT16),
+ SLE_VAR(Vehicle, subspeed, SLE_UINT8),
+ SLE_VAR(Vehicle, acceleration, SLE_UINT8),
+ SLE_VAR(Vehicle, progress, SLE_UINT8),
+
+ SLE_VAR(Vehicle, vehstatus, SLE_UINT8),
+ SLE_CONDVAR(Vehicle, last_station_visited, SLE_FILE_U8 | SLE_VAR_U16, 0, 4),
+ SLE_CONDVAR(Vehicle, last_station_visited, SLE_UINT16, 5, SL_MAX_VERSION),
+
+ SLE_VAR(Vehicle, cargo_type, SLE_UINT8),
+ SLE_CONDVAR(Vehicle, cargo_subtype, SLE_UINT8, 35, SL_MAX_VERSION),
+ SLEG_CONDVAR( _cargo_days, SLE_UINT8, 0, 67),
+ SLEG_CONDVAR( _cargo_source, SLE_FILE_U8 | SLE_VAR_U16, 0, 6),
+ SLEG_CONDVAR( _cargo_source, SLE_UINT16, 7, 67),
+ SLEG_CONDVAR( _cargo_source_xy, SLE_UINT32, 44, 67),
+ SLE_VAR(Vehicle, cargo_cap, SLE_UINT16),
+ SLEG_CONDVAR( _cargo_count, SLE_UINT16, 0, 67),
+ SLE_CONDLST(Vehicle, cargo, REF_CARGO_PACKET, 68, SL_MAX_VERSION),
+
+ SLE_VAR(Vehicle, day_counter, SLE_UINT8),
+ SLE_VAR(Vehicle, tick_counter, SLE_UINT8),
+ SLE_CONDVAR(Vehicle, running_ticks, SLE_UINT8, 88, SL_MAX_VERSION),
+
+ SLE_VAR(Vehicle, cur_order_index, SLE_UINT8),
+ /* num_orders is now part of OrderList and is not saved but counted */
+ SLE_CONDNULL(1, 0, 104),
+
+ /* This next line is for version 4 and prior compatibility.. it temporarily reads
+ type and flags (which were both 4 bits) into type. Later on this is
+ converted correctly */
+ SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, type), SLE_UINT8, 0, 4),
+ SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, dest), SLE_FILE_U8 | SLE_VAR_U16, 0, 4),
+
+ /* Orders for version 5 and on */
+ SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, type), SLE_UINT8, 5, SL_MAX_VERSION),
+ SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, flags), SLE_UINT8, 5, SL_MAX_VERSION),
+ SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, dest), SLE_UINT16, 5, SL_MAX_VERSION),
+
+ /* Refit in current order */
+ SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, refit_cargo), SLE_UINT8, 36, SL_MAX_VERSION),
+ SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, refit_subtype), SLE_UINT8, 36, SL_MAX_VERSION),
+
+ /* Timetable in current order */
+ SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, wait_time), SLE_UINT16, 67, SL_MAX_VERSION),
+ SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, travel_time), SLE_UINT16, 67, SL_MAX_VERSION),
+
+ SLE_CONDREF(Vehicle, orders, REF_ORDER, 0, 104),
+ SLE_CONDREF(Vehicle, orders, REF_ORDERLIST, 105, SL_MAX_VERSION),
+
+ SLE_CONDVAR(Vehicle, age, SLE_FILE_U16 | SLE_VAR_I32, 0, 30),
+ SLE_CONDVAR(Vehicle, age, SLE_INT32, 31, SL_MAX_VERSION),
+ SLE_CONDVAR(Vehicle, max_age, SLE_FILE_U16 | SLE_VAR_I32, 0, 30),
+ SLE_CONDVAR(Vehicle, max_age, SLE_INT32, 31, SL_MAX_VERSION),
+ SLE_CONDVAR(Vehicle, date_of_last_service, SLE_FILE_U16 | SLE_VAR_I32, 0, 30),
+ SLE_CONDVAR(Vehicle, date_of_last_service, SLE_INT32, 31, SL_MAX_VERSION),
+ SLE_CONDVAR(Vehicle, service_interval, SLE_FILE_U16 | SLE_VAR_I32, 0, 30),
+ SLE_CONDVAR(Vehicle, service_interval, SLE_INT32, 31, SL_MAX_VERSION),
+ SLE_VAR(Vehicle, reliability, SLE_UINT16),
+ SLE_VAR(Vehicle, reliability_spd_dec, SLE_UINT16),
+ SLE_VAR(Vehicle, breakdown_ctr, SLE_UINT8),
+ SLE_VAR(Vehicle, breakdown_delay, SLE_UINT8),
+ SLE_VAR(Vehicle, breakdowns_since_last_service, SLE_UINT8),
+ SLE_VAR(Vehicle, breakdown_chance, SLE_UINT8),
+ SLE_CONDVAR(Vehicle, build_year, SLE_FILE_U8 | SLE_VAR_I32, 0, 30),
+ SLE_CONDVAR(Vehicle, build_year, SLE_INT32, 31, SL_MAX_VERSION),
+
+ SLE_VAR(Vehicle, load_unload_time_rem, SLE_UINT16),
+ SLEG_CONDVAR( _cargo_paid_for, SLE_UINT16, 45, SL_MAX_VERSION),
+ SLE_CONDVAR(Vehicle, vehicle_flags, SLE_UINT8, 40, SL_MAX_VERSION),
+
+ SLE_CONDVAR(Vehicle, profit_this_year, SLE_FILE_I32 | SLE_VAR_I64, 0, 64),
+ SLE_CONDVAR(Vehicle, profit_this_year, SLE_INT64, 65, SL_MAX_VERSION),
+ SLE_CONDVAR(Vehicle, profit_last_year, SLE_FILE_I32 | SLE_VAR_I64, 0, 64),
+ SLE_CONDVAR(Vehicle, profit_last_year, SLE_INT64, 65, SL_MAX_VERSION),
+ SLEG_CONDVAR( _cargo_feeder_share, SLE_FILE_I32 | SLE_VAR_I64, 51, 64),
+ SLEG_CONDVAR( _cargo_feeder_share, SLE_INT64, 65, 67),
+ SLEG_CONDVAR( _cargo_loaded_at_xy, SLE_UINT32, 51, 67),
+ SLE_CONDVAR(Vehicle, value, SLE_FILE_I32 | SLE_VAR_I64, 0, 64),
+ SLE_CONDVAR(Vehicle, value, SLE_INT64, 65, SL_MAX_VERSION),
+
+ SLE_CONDVAR(Vehicle, random_bits, SLE_UINT8, 2, SL_MAX_VERSION),
+ SLE_CONDVAR(Vehicle, waiting_triggers, SLE_UINT8, 2, SL_MAX_VERSION),
+
+ SLE_CONDREF(Vehicle, next_shared, REF_VEHICLE, 2, SL_MAX_VERSION),
+ SLE_CONDNULL(2, 2, 68),
+ SLE_CONDNULL(4, 69, 100),
+
+ SLE_CONDVAR(Vehicle, group_id, SLE_UINT16, 60, SL_MAX_VERSION),
+
+ SLE_CONDVAR(Vehicle, current_order_time, SLE_UINT32, 67, SL_MAX_VERSION),
+ SLE_CONDVAR(Vehicle, lateness_counter, SLE_INT32, 67, SL_MAX_VERSION),
+
+ /* reserve extra space in savegame here. (currently 10 bytes) */
+ SLE_CONDNULL(10, 2, SL_MAX_VERSION),
+
+ SLE_END()
+ };
+
+
+ static const SaveLoad _train_desc[] = {
+ SLE_WRITEBYTE(Vehicle, type, VEH_TRAIN),
+ SLE_VEH_INCLUDEX(),
+ SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRail, crash_anim_pos), SLE_UINT16),
+ SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRail, force_proceed), SLE_UINT8),
+ SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRail, railtype), SLE_UINT8),
+ SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRail, track), SLE_UINT8),
+
+ SLE_CONDVARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRail, flags), SLE_FILE_U8 | SLE_VAR_U16, 2, 99),
+ SLE_CONDVARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRail, flags), SLE_UINT16, 100, SL_MAX_VERSION),
+ SLE_CONDNULL(2, 2, 59),
+
+ SLE_CONDNULL(2, 2, 19),
+ /* reserve extra space in savegame here. (currently 11 bytes) */
+ SLE_CONDNULL(11, 2, SL_MAX_VERSION),
+
+ SLE_END()
+ };
+
+ static const SaveLoad _roadveh_desc[] = {
+ SLE_WRITEBYTE(Vehicle, type, VEH_ROAD),
+ SLE_VEH_INCLUDEX(),
+ SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRoad, state), SLE_UINT8),
+ SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRoad, frame), SLE_UINT8),
+ SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRoad, blocked_ctr), SLE_UINT16),
+ SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRoad, overtaking), SLE_UINT8),
+ SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRoad, overtaking_ctr), SLE_UINT8),
+ SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRoad, crashed_ctr), SLE_UINT16),
+ SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRoad, reverse_ctr), SLE_UINT8),
+
+ SLE_CONDREFX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRoad, slot), REF_ROADSTOPS, 6, SL_MAX_VERSION),
+ SLE_CONDNULL(1, 6, SL_MAX_VERSION),
+ SLE_CONDVARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRoad, slot_age), SLE_UINT8, 6, SL_MAX_VERSION),
+ /* reserve extra space in savegame here. (currently 16 bytes) */
+ SLE_CONDNULL(16, 2, SL_MAX_VERSION),
+
+ SLE_END()
+ };
+
+ static const SaveLoad _ship_desc[] = {
+ SLE_WRITEBYTE(Vehicle, type, VEH_SHIP),
+ SLE_VEH_INCLUDEX(),
+ SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleShip, state), SLE_UINT8),
+
+ /* reserve extra space in savegame here. (currently 16 bytes) */
+ SLE_CONDNULL(16, 2, SL_MAX_VERSION),
+
+ SLE_END()
+ };
+
+ static const SaveLoad _aircraft_desc[] = {
+ SLE_WRITEBYTE(Vehicle, type, VEH_AIRCRAFT),
+ SLE_VEH_INCLUDEX(),
+ SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleAir, crashed_counter), SLE_UINT16),
+ SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleAir, pos), SLE_UINT8),
+
+ SLE_CONDVARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleAir, targetairport), SLE_FILE_U8 | SLE_VAR_U16, 0, 4),
+ SLE_CONDVARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleAir, targetairport), SLE_UINT16, 5, SL_MAX_VERSION),
+
+ SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleAir, state), SLE_UINT8),
+
+ SLE_CONDVARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleAir, previous_pos), SLE_UINT8, 2, SL_MAX_VERSION),
+
+ /* reserve extra space in savegame here. (currently 15 bytes) */
+ SLE_CONDNULL(15, 2, SL_MAX_VERSION),
+
+ SLE_END()
+ };
+
+ static const SaveLoad _special_desc[] = {
+ SLE_WRITEBYTE(Vehicle, type, VEH_EFFECT),
+
+ SLE_VAR(Vehicle, subtype, SLE_UINT8),
+
+ SLE_CONDVAR(Vehicle, tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
+ SLE_CONDVAR(Vehicle, tile, SLE_UINT32, 6, SL_MAX_VERSION),
+
+ SLE_CONDVAR(Vehicle, x_pos, SLE_FILE_I16 | SLE_VAR_I32, 0, 5),
+ SLE_CONDVAR(Vehicle, x_pos, SLE_INT32, 6, SL_MAX_VERSION),
+ SLE_CONDVAR(Vehicle, y_pos, SLE_FILE_I16 | SLE_VAR_I32, 0, 5),
+ SLE_CONDVAR(Vehicle, y_pos, SLE_INT32, 6, SL_MAX_VERSION),
+ SLE_VAR(Vehicle, z_pos, SLE_UINT8),
+
+ SLE_VAR(Vehicle, cur_image, SLE_UINT16),
+ SLE_CONDNULL(5, 0, 57),
+ SLE_VAR(Vehicle, progress, SLE_UINT8),
+ SLE_VAR(Vehicle, vehstatus, SLE_UINT8),
+
+ SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleEffect, animation_state), SLE_UINT16),
+ SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleEffect, animation_substate), SLE_UINT8),
+
+ SLE_CONDVAR(Vehicle, spritenum, SLE_UINT8, 2, SL_MAX_VERSION),
+
+ /* reserve extra space in savegame here. (currently 15 bytes) */
+ SLE_CONDNULL(15, 2, SL_MAX_VERSION),
+
+ SLE_END()
+ };
+
+ static const SaveLoad _disaster_desc[] = {
+ SLE_WRITEBYTE(Vehicle, type, VEH_DISASTER),
+
+ SLE_REF(Vehicle, next, REF_VEHICLE_OLD),
+
+ SLE_VAR(Vehicle, subtype, SLE_UINT8),
+ SLE_CONDVAR(Vehicle, tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
+ SLE_CONDVAR(Vehicle, tile, SLE_UINT32, 6, SL_MAX_VERSION),
+ SLE_CONDVAR(Vehicle, dest_tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
+ SLE_CONDVAR(Vehicle, dest_tile, SLE_UINT32, 6, SL_MAX_VERSION),
+
+ SLE_CONDVAR(Vehicle, x_pos, SLE_FILE_I16 | SLE_VAR_I32, 0, 5),
+ SLE_CONDVAR(Vehicle, x_pos, SLE_INT32, 6, SL_MAX_VERSION),
+ SLE_CONDVAR(Vehicle, y_pos, SLE_FILE_I16 | SLE_VAR_I32, 0, 5),
+ SLE_CONDVAR(Vehicle, y_pos, SLE_INT32, 6, SL_MAX_VERSION),
+ SLE_VAR(Vehicle, z_pos, SLE_UINT8),
+ SLE_VAR(Vehicle, direction, SLE_UINT8),
+
+ SLE_CONDNULL(5, 0, 57),
+ SLE_VAR(Vehicle, owner, SLE_UINT8),
+ SLE_VAR(Vehicle, vehstatus, SLE_UINT8),
+ SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, dest), SLE_FILE_U8 | SLE_VAR_U16, 0, 4),
+ SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, dest), SLE_UINT16, 5, SL_MAX_VERSION),
+
+ SLE_VAR(Vehicle, cur_image, SLE_UINT16),
+ SLE_CONDVAR(Vehicle, age, SLE_FILE_U16 | SLE_VAR_I32, 0, 30),
+ SLE_CONDVAR(Vehicle, age, SLE_INT32, 31, SL_MAX_VERSION),
+ SLE_VAR(Vehicle, tick_counter, SLE_UINT8),
+
+ SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleDisaster, image_override), SLE_UINT16),
+ SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleDisaster, big_ufo_destroyer_target), SLE_UINT16),
+
+ /* reserve extra space in savegame here. (currently 16 bytes) */
+ SLE_CONDNULL(16, 2, SL_MAX_VERSION),
+
+ SLE_END()
+ };
+
+
+ static const SaveLoad *_veh_descs[] = {
+ _train_desc,
+ _roadveh_desc,
+ _ship_desc,
+ _aircraft_desc,
+ _special_desc,
+ _disaster_desc,
+ _common_veh_desc,
+ };
+
+ return _veh_descs[vt];
+}
+
+/** Will be called when the vehicles need to be saved. */
+static void Save_VEHS()
+{
+ Vehicle *v;
+ /* Write the vehicles */
+ FOR_ALL_VEHICLES(v) {
+ SlSetArrayIndex(v->index);
+ SlObject(v, GetVehicleDescription(v->type));
+ }
+}
+
+/** Will be called when vehicles need to be loaded. */
+void Load_VEHS()
+{
+ int index;
+
+ _cargo_count = 0;
+
+ while ((index = SlIterateArray()) != -1) {
+ Vehicle *v;
+ VehicleType vtype = (VehicleType)SlReadByte();
+
+ switch (vtype) {
+ case VEH_TRAIN: v = new (index) Train(); break;
+ case VEH_ROAD: v = new (index) RoadVehicle(); break;
+ case VEH_SHIP: v = new (index) Ship(); break;
+ case VEH_AIRCRAFT: v = new (index) Aircraft(); break;
+ case VEH_EFFECT: v = new (index) EffectVehicle(); break;
+ case VEH_DISASTER: v = new (index) DisasterVehicle(); break;
+ case VEH_INVALID: v = new (index) InvalidVehicle(); break;
+ default: NOT_REACHED();
+ }
+
+ SlObject(v, GetVehicleDescription(vtype));
+
+ if (_cargo_count != 0 && IsCompanyBuildableVehicleType(v)) {
+ /* Don't construct the packet with station here, because that'll fail with old savegames */
+ CargoPacket *cp = new CargoPacket();
+ cp->source = _cargo_source;
+ cp->source_xy = _cargo_source_xy;
+ cp->count = _cargo_count;
+ cp->days_in_transit = _cargo_days;
+ cp->feeder_share = _cargo_feeder_share;
+ cp->loaded_at_xy = _cargo_loaded_at_xy;
+ v->cargo.Append(cp);
+ }
+
+ /* Old savegames used 'last_station_visited = 0xFF' */
+ if (CheckSavegameVersion(5) && v->last_station_visited == 0xFF)
+ v->last_station_visited = INVALID_STATION;
+
+ if (CheckSavegameVersion(5)) {
+ /* Convert the current_order.type (which is a mix of type and flags, because
+ * in those versions, they both were 4 bits big) to type and flags */
+ v->current_order.flags = GB(v->current_order.type, 4, 4);
+ v->current_order.type &= 0x0F;
+ }
+
+ /* Advanced vehicle lists got added */
+ if (CheckSavegameVersion(60)) v->group_id = DEFAULT_GROUP;
+ }
+}
+
+extern const ChunkHandler _veh_chunk_handlers[] = {
+ { 'VEHS', Save_VEHS, Load_VEHS, CH_SPARSE_ARRAY | CH_LAST},
+};
diff --git a/src/saveload/waypoint_sl.cpp b/src/saveload/waypoint_sl.cpp
new file mode 100644
index 000000000..e8a8bf949
--- /dev/null
+++ b/src/saveload/waypoint_sl.cpp
@@ -0,0 +1,96 @@
+/* $Id$ */
+
+/** @file waypoint_sl.cpp Code handling saving and loading of waypoints */
+
+#include "../stdafx.h"
+#include "../waypoint.h"
+#include "../newgrf_station.h"
+#include "../town.h"
+
+#include "table/strings.h"
+
+#include "saveload.h"
+
+/**
+ * Update waypoint graphics id against saved GRFID/localidx.
+ * This is to ensure the chosen graphics are correct if GRF files are changed.
+ */
+void AfterLoadWaypoints()
+{
+ Waypoint *wp;
+
+ FOR_ALL_WAYPOINTS(wp) {
+ uint i;
+
+ if (wp->grfid == 0) continue;
+
+ for (i = 0; i < GetNumCustomStations(STAT_CLASS_WAYP); i++) {
+ const StationSpec *statspec = GetCustomStationSpec(STAT_CLASS_WAYP, i);
+ if (statspec != NULL && statspec->grffile->grfid == wp->grfid && statspec->localidx == wp->localidx) {
+ wp->stat_id = i;
+ break;
+ }
+ }
+ }
+}
+
+/**
+ * Fix savegames which stored waypoints in their old format
+ */
+void FixOldWaypoints()
+{
+ Waypoint *wp;
+
+ /* Convert the old 'town_or_string', to 'string' / 'town' / 'town_cn' */
+ FOR_ALL_WAYPOINTS(wp) {
+ wp->town_index = ClosestTownFromTile(wp->xy, UINT_MAX)->index;
+ wp->town_cn = 0;
+ if (wp->string & 0xC000) {
+ wp->town_cn = wp->string & 0x3F;
+ wp->string = STR_NULL;
+ }
+ }
+}
+
+static const SaveLoad _waypoint_desc[] = {
+ SLE_CONDVAR(Waypoint, xy, SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
+ SLE_CONDVAR(Waypoint, xy, SLE_UINT32, 6, SL_MAX_VERSION),
+ SLE_CONDVAR(Waypoint, town_index, SLE_UINT16, 12, SL_MAX_VERSION),
+ SLE_CONDVAR(Waypoint, town_cn, SLE_FILE_U8 | SLE_VAR_U16, 12, 88),
+ SLE_CONDVAR(Waypoint, town_cn, SLE_UINT16, 89, SL_MAX_VERSION),
+ SLE_CONDVAR(Waypoint, string, SLE_STRINGID, 0, 83),
+ SLE_CONDSTR(Waypoint, name, SLE_STR, 0, 84, SL_MAX_VERSION),
+ SLE_VAR(Waypoint, deleted, SLE_UINT8),
+
+ SLE_CONDVAR(Waypoint, build_date, SLE_FILE_U16 | SLE_VAR_I32, 3, 30),
+ SLE_CONDVAR(Waypoint, build_date, SLE_INT32, 31, SL_MAX_VERSION),
+ SLE_CONDVAR(Waypoint, localidx, SLE_UINT8, 3, SL_MAX_VERSION),
+ SLE_CONDVAR(Waypoint, grfid, SLE_UINT32, 17, SL_MAX_VERSION),
+ SLE_CONDVAR(Waypoint, owner, SLE_UINT8, 101, SL_MAX_VERSION),
+
+ SLE_END()
+};
+
+static void Save_WAYP()
+{
+ Waypoint *wp;
+
+ FOR_ALL_WAYPOINTS(wp) {
+ SlSetArrayIndex(wp->index);
+ SlObject(wp, _waypoint_desc);
+ }
+}
+
+static void Load_WAYP()
+{
+ int index;
+
+ while ((index = SlIterateArray()) != -1) {
+ Waypoint *wp = new (index) Waypoint();
+ SlObject(wp, _waypoint_desc);
+ }
+}
+
+extern const ChunkHandler _waypoint_chunk_handlers[] = {
+ { 'CHKP', Save_WAYP, Load_WAYP, CH_ARRAY | CH_LAST},
+};