summaryrefslogtreecommitdiff
path: root/src/saveload
diff options
context:
space:
mode:
authorsmatz <smatz@openttd.org>2009-01-23 02:35:17 +0000
committersmatz <smatz@openttd.org>2009-01-23 02:35:17 +0000
commit6dffd8ae82e78ed98b66a3f2fcd4cdccbe57ee72 (patch)
treea9ff58b51d34b7c243c907d8eed0d99110f3ce45 /src/saveload
parent83e1288c6a327f711f047aff1e03e5f4a9dfc058 (diff)
downloadopenttd-6dffd8ae82e78ed98b66a3f2fcd4cdccbe57ee72.tar.xz
(svn r15216) -Feature: native support for Transport Tycoon (Original) savegames. Based on SVXConverter's source code and documentation by Roman Vetter
Diffstat (limited to 'src/saveload')
-rw-r--r--src/saveload/afterload.cpp3
-rw-r--r--src/saveload/oldloader.cpp98
-rw-r--r--src/saveload/oldloader.h23
-rw-r--r--src/saveload/oldloader_sl.cpp (renamed from src/saveload/ttdloader.cpp)912
-rw-r--r--src/saveload/saveload.h4
-rw-r--r--src/saveload/saveload_internal.h4
-rw-r--r--src/saveload/strings_sl.cpp12
7 files changed, 839 insertions, 217 deletions
diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp
index bbff67644..acfe903dd 100644
--- a/src/saveload/afterload.cpp
+++ b/src/saveload/afterload.cpp
@@ -327,7 +327,7 @@ static void FixOwnerOfRailTrack(TileIndex t)
if (IsLevelCrossingTile(t)) {
/* else change the crossing to normal road (road vehicles won't care) */
MakeRoadNormal(t, GetCrossingRoadBits(t), GetRoadTypes(t), GetTownIndex(t),
- GetRoadOwner(t, ROADTYPE_ROAD), GetRoadOwner(t, ROADTYPE_TRAM), GetRoadOwner(t, ROADTYPE_HWAY));
+ GetRoadOwner(t, ROADTYPE_ROAD), GetRoadOwner(t, ROADTYPE_TRAM), GetRoadOwner(t, ROADTYPE_HWAY));
return;
}
@@ -356,7 +356,6 @@ bool AfterLoadGame()
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);
diff --git a/src/saveload/oldloader.cpp b/src/saveload/oldloader.cpp
index 4b7af3a42..2411a675e 100644
--- a/src/saveload/oldloader.cpp
+++ b/src/saveload/oldloader.cpp
@@ -17,12 +17,13 @@
#include "oldloader.h"
enum {
- HEADER_SIZE = 49,
+ TTO_HEADER_SIZE = 41,
+ TTD_HEADER_SIZE = 49,
};
uint32 _bump_assert_value;
-static inline OldChunkType GetOldChunkType(OldChunkType type) {return (OldChunkType)GB(type, 0, 8);}
+static inline OldChunkType GetOldChunkType(OldChunkType type) {return (OldChunkType)GB(type, 0, 4);}
static inline OldChunkType GetOldChunkVarType(OldChunkType type) {return (OldChunkType)(GB(type, 8, 8) << 8);}
static inline OldChunkType GetOldChunkFileType(OldChunkType type) {return (OldChunkType)(GB(type, 16, 8) << 16);}
@@ -101,12 +102,17 @@ byte ReadByte(LoadgameState *ls)
*/
bool LoadChunk(LoadgameState *ls, void *base, const OldChunks *chunks)
{
- const OldChunks *chunk = chunks;
byte *base_ptr = (byte*)base;
- while (chunk->type != OC_END) {
+ for (const OldChunks *chunk = chunks; chunk->type != OC_END; chunk++) {
+ if (((chunk->type & OC_TTD) && (_savegame_type == SGT_TTO)) ||
+ ((chunk->type & OC_TTO) && (_savegame_type != SGT_TTO))) {
+ /* TTD(P)-only chunk, but TTO savegame || TTO-only chunk, but TTD/TTDP savegame */
+ continue;
+ }
+
byte *ptr = (byte*)chunk->ptr;
- if ((chunk->type & OC_DEREFERENCE_POINTER) != 0) ptr = *(byte**)ptr;
+ if (chunk->type & OC_DEREFERENCE_POINTER) ptr = *(byte**)ptr;
for (uint i = 0; i < chunk->amount; i++) {
if (ls->failed) return false;
@@ -164,8 +170,6 @@ bool LoadChunk(LoadgameState *ls, void *base, const OldChunks *chunks)
if (chunk->amount > 1 && chunk->ptr != NULL) ptr += CalcOldVarLen(chunk->type);
}
}
-
- chunk++;
}
return true;
@@ -194,30 +198,72 @@ static void InitLoading(LoadgameState *ls)
_settings_game.construction.freeform_edges = false; // disable so we can convert map array (SetTileType is still used)
}
-
-
/**
* Verifies the title has a valid checksum
* @param title title and checksum
* @return true iff the title is valid
- * @note the title (incl. checksum) has to be at least 49 (HEADER_SIZE) bytes long!
+ * @note the title (incl. checksum) has to be at least 41/49 (HEADER_SIZE) bytes long!
*/
-static bool VerifyOldNameChecksum(char *title)
+static bool VerifyOldNameChecksum(char *title, uint len)
{
uint16 sum = 0;
- for (uint i = 0; i < HEADER_SIZE - 2; i++) {
+ for (uint i = 0; i < len - 2; i++) {
sum += title[i];
sum = ROL(sum, 1);
}
sum ^= 0xAAAA; // computed checksum
- uint16 sum2 = title[HEADER_SIZE - 2]; // checksum in file
- SB(sum2, 8, 8, title[HEADER_SIZE - 1]);
+ uint16 sum2 = title[len - 2]; // checksum in file
+ SB(sum2, 8, 8, title[len - 1]);
return sum == sum2;
}
+static inline bool CheckOldSavegameType(FILE *f, char *temp, const char *last, uint len)
+{
+ assert(last - temp + 1 >= len);
+
+ fseek(f, 0, SEEK_SET);
+ if (fread(temp, 1, len, f) != len) {
+ temp[0] = '\0'; // if reading failed, make the name empty
+ return false;
+ }
+
+ bool ret = VerifyOldNameChecksum(temp, len);
+ temp[len - 2] = '\0'; // name is nul-terminated in savegame, but it's better to be sure
+
+ return ret;
+}
+
+assert_compile(TTD_HEADER_SIZE >= TTO_HEADER_SIZE);
+static SavegameType DetermineOldSavegameType(FILE *f, char *title, const char *last)
+{
+ char temp[TTD_HEADER_SIZE];
+
+ SavegameType type = SGT_TTO;
+
+ if (!CheckOldSavegameType(f, temp, lastof(temp), TTO_HEADER_SIZE)) {
+ type = SGT_TTD;
+ if (!CheckOldSavegameType(f, temp, lastof(temp), TTD_HEADER_SIZE)) {
+ type = SGT_INVALID;
+ }
+ }
+
+ if (title != NULL) {
+ switch (type) {
+ case SGT_TTO: title = strecpy(title, "(TTO) ", last); break;
+ case SGT_TTD: title = strecpy(title, "(TTD) ", last); break;
+ default: title = strecpy(title, "(broken) ", last); break;
+ }
+ title = strecpy(title, temp, last);
+ }
+
+ return type;
+}
+
+typedef bool LoadOldMainProc(LoadgameState *ls);
+
bool LoadOldSaveGame(const char *file)
{
LoadgameState ls;
@@ -234,8 +280,19 @@ bool LoadOldSaveGame(const char *file)
return false;
}
- char temp[HEADER_SIZE];
- if (fread(temp, 1, HEADER_SIZE, ls.file) != HEADER_SIZE || !VerifyOldNameChecksum(temp) || !LoadOldMain(&ls)) {
+ SavegameType type = DetermineOldSavegameType(ls.file, NULL, NULL);
+
+ LoadOldMainProc *proc = NULL;
+
+ switch (type) {
+ case SGT_TTO: proc = &LoadTTOMain; break;
+ case SGT_TTD: proc = &LoadTTDMain; break;
+ default: break;
+ }
+
+ _savegame_type = type;
+
+ if (proc == NULL || !proc(&ls)) {
SetSaveLoadError(STR_GAME_SAVELOAD_ERROR_DATA_INTEGRITY_CHECK_FAILED);
fclose(ls.file);
return false;
@@ -249,23 +306,16 @@ bool LoadOldSaveGame(const char *file)
void GetOldSaveGameName(const char *path, const char *file, char *title, const char *last)
{
char filename[MAX_PATH];
- char temp[HEADER_SIZE];
snprintf(filename, lengthof(filename), "%s" PATHSEP "%s", path, file);
FILE *f = fopen(filename, "rb");
- temp[0] = '\0'; // name is nul-terminated in savegame ...
if (f == NULL) {
*title = '\0';
return;
}
- bool broken = (fread(temp, 1, HEADER_SIZE, f) != HEADER_SIZE || !VerifyOldNameChecksum(temp));
-
- temp[HEADER_SIZE - 2] = '\0'; // ... but it's better to be sure
-
- if (broken) title = strecpy(title, "(broken) ", last);
- title = strecpy(title, temp, last);
+ DetermineOldSavegameType(f, title, last);
fclose(f);
}
diff --git a/src/saveload/oldloader.h b/src/saveload/oldloader.h
index 5cde06cc5..6bee5d5f6 100644
--- a/src/saveload/oldloader.h
+++ b/src/saveload/oldloader.h
@@ -34,7 +34,11 @@ enum OldChunkType {
OC_NULL = 1,
OC_CHUNK = 2,
OC_ASSERT = 3,
- /* 8 bits allocated (256 max) */
+ /* 4 bits allocated (16 max) */
+
+ OC_TTD = 1 << 4, ///< chunk is valid ONLY for TTD savegames
+ OC_TTO = 1 << 5, ///< -//- TTO (default in neither of these)
+ /* 4 bits allocated */
OC_VAR_I8 = 1 << 8,
OC_VAR_U8 = 2 << 8,
@@ -91,7 +95,8 @@ extern uint _bump_assert_value;
byte ReadByte(LoadgameState *ls);
bool LoadChunk(LoadgameState *ls, void *base, const OldChunks *chunks);
-bool LoadOldMain(LoadgameState *ls);
+bool LoadTTDMain(LoadgameState *ls);
+bool LoadTTOMain(LoadgameState *ls);
static inline uint16 ReadUint16(LoadgameState *ls)
{
@@ -114,11 +119,13 @@ static inline uint32 ReadUint32(LoadgameState *ls)
* - 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 }
+#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_CNULL(type, amount) { OC_NULL | type, amount, NULL, 0, NULL }
+#define OCL_CCHUNK(type, amount, proc) { OC_CHUNK | type, amount, NULL, 0, proc }
+#define OCL_ASSERT(type, size) { OC_ASSERT | type, 1, NULL, size, NULL }
+#define OCL_NULL(amount) OCL_CNULL((OldChunkType)0, amount)
+#define OCL_CHUNK(amount, proc) OCL_CCHUNK((OldChunkType)0, amount, proc)
#endif /* OLDLOADER_H */
diff --git a/src/saveload/ttdloader.cpp b/src/saveload/oldloader_sl.cpp
index d239a7968..0b449ca90 100644
--- a/src/saveload/ttdloader.cpp
+++ b/src/saveload/oldloader_sl.cpp
@@ -1,6 +1,6 @@
/* $Id$ */
-/** @file oldloader.cpp Loading of old TTD(patch) savegames. */
+/** @file oldloader_sl.cpp Chunks and fix-ups for TTO/TTD/TTDP savegames. TTO loader code is based on SVXConverter by Roman Vetter. */
#include "../stdafx.h"
#include "../station_map.h"
@@ -24,21 +24,27 @@
#include "../string_func.h"
#include "../core/mem_func.hpp"
#include "../core/alloc_type.hpp"
-
-#include "table/strings.h"
-
-#include "oldloader.h"
#include "saveload_internal.h"
+#include "oldloader.h"
+#include "table/strings.h"
+#include "../table/engines.h"
-extern SavegameType _savegame_type;
-extern uint32 _ttdp_version;
static bool _read_ttdpatch_flags;
static uint8 *_old_map3;
void FixOldMapArray()
{
+ /* TTO/TTD/TTDP savegames could have buoys at tile 0
+ * (without assigned station struct) */
+ MemSetT(&_m[0], 0);
+ SetTileType(0, MP_WATER);
+ SetTileOwner(0, OWNER_WATER);
+}
+
+static void FixTTDMapArray()
+{
/* _old_map3 is moved to _m::m3 and _m::m4 */
for (TileIndex t = 0; t < OLD_MAP_SIZE; t++) {
_m[t].m3 = _old_map3[t * 2];
@@ -89,11 +95,7 @@ void FixOldMapArray()
}
}
- /* Some old TTD(Patch) savegames could have buoys at tile 0
- * (without assigned station struct) */
- MemSetT(&_m[0], 0);
- SetTileType(0, MP_WATER);
- SetTileOwner(0, OWNER_WATER);
+ FixOldMapArray();
}
extern uint32 GetOldTownName(uint32 townnameparts, byte old_town_name_type);
@@ -111,7 +113,7 @@ void FixOldTowns()
}
}
-StringID *_old_vehicle_names = NULL;
+StringID *_old_vehicle_names;
void FixOldVehicles()
{
@@ -153,13 +155,271 @@ void FixOldVehicles()
}
}
+static bool FixTTOMapArray()
+{
+ for (TileIndex t = 0; t < OLD_MAP_SIZE; t++) {
+ TileType tt = GetTileType(t);
+
+ switch (tt) {
+ case MP_CLEAR:
+ break;
+
+ case MP_RAILWAY:
+ case 11: // monorail
+ if (tt == 11) {
+ _m[t].m3 = 1; // rail type = monorail
+ _m[t].type_height &= 0x1F; // -> MP_RAILWAY
+ _m[t].m2 = 1; // set monorail ground to RAIL_GROUND_GRASS
+ }
+ switch (GB(_m[t].m5, 6, 2)) {
+ case 0: // RAIL_TILE_NORMAL
+ break;
+ case 1: // RAIL_TILE_SIGNALS
+ _m[t].m4 = (~_m[t].m5 & 1) << 2; // signal variant (present only in OTTD)
+ SB(_m[t].m2, 6, 2, GB(_m[t].m5, 3, 2)); // signal status
+ _m[t].m3 |= 0xC0; // both signals are present
+ _m[t].m5 = HasBit(_m[t].m5, 5) ? 2 : 1; // track direction (only X or Y)
+ _m[t].m5 |= 0x40; // RAIL_TILE_SIGNALS
+ break;
+ case 3: // RAIL_TILE_DEPOT
+ _m[t].m2 = 0;
+ break;
+ default:
+ return false;
+ }
+ break;
+
+ case MP_ROAD: // road (depot) or level crossing
+ switch (GB(_m[t].m5, 4, 4)) {
+ case 0: // ROAD_TILE_NORMAL
+ if (_m[t].m2 == 4) _m[t].m2 = 5; // 'small trees' -> ROADSIDE_TREES
+ break;
+ case 1: // ROAD_TILE_CROSSING (there aren't monorail crossings in TTO)
+ _m[t].m3 = _m[t].m1; // set owner of road = owner of rail
+ break;
+ case 2: // ROAD_TILE_DEPOT
+ break;
+ default:
+ return false;
+ }
+ break;
+
+ case MP_HOUSE:
+ _m[t].m3 = _m[t].m2 & 0xC0; // construction stage
+ _m[t].m2 &= 0x3F; // building type
+ if (_m[t].m2 >= 5) _m[t].m2++; // skip "large office block on snow"
+ break;
+
+ case MP_TREES:
+ _m[t].m3 = GB(_m[t].m5, 3, 3); // type of trees
+ _m[t].m5 &= 0xC7; // number of trees and growth status
+ break;
+
+ case MP_STATION:
+ _m[t].m3 = (_m[t].m5 >= 0x08 && _m[t].m5 <= 0x0F) ? 1 : 0; // monorail -> 1, others 0 (rail, road, airport, dock)
+ if (_m[t].m5 >= 8) _m[t].m5 -= 8; // shift for monorail
+ if (_m[t].m5 >= 0x42) _m[t].m5++; // skip heliport
+ break;
-/* 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
+ case MP_WATER:
+ _m[t].m3 = _m[t].m2 = 0;
+ break;
+
+ case MP_VOID:
+ _m[t].m2 = _m[t].m3 = _m[t].m5 = 0;
+ break;
+
+ case MP_INDUSTRY:
+ _m[t].m3 = 0;
+ switch (_m[t].m5) {
+ case 0x24: // farm silo
+ _m[t].m5 = 0x25;
+ break;
+ case 0x25: case 0x27: // farm
+ case 0x28: case 0x29: case 0x2A: case 0x2B: // factory
+ _m[t].m5--;
+ break;
+ default:
+ if (_m[t].m5 >= 0x2C) _m[t].m5 += 3; // iron ore mine, steel mill or bank
+ break;
+ }
+ break;
+
+ case MP_TUNNELBRIDGE:
+ if (HasBit(_m[t].m5, 7)) { // bridge
+ byte m5 = _m[t].m5;
+ _m[t].m5 = m5 & 0xE1; // copy bits 7..5, 1
+ if (GB(m5, 1, 2) == 1) _m[t].m5 |= 0x02; // road bridge
+ if (GB(m5, 1, 2) == 3) _m[t].m2 |= 0xA0; // monorail bridge -> tubular, steel bridge
+ if (!HasBit(m5, 6)) { // bridge head
+ _m[t].m3 = (GB(m5, 1, 2) == 3) ? 1 : 0; // track subtype (1 for monorail, 0 for others)
+ } else { // middle bridge part
+ _m[t].m3 = HasBit(m5, 2) ? 0x10 : 0; // track subtype on bridge
+ if (GB(m5, 3, 2) == 3) _m[t].m3 |= 1; // track subtype under bridge
+ if (GB(m5, 3, 2) == 1) _m[t].m5 |= 0x08; // set for road/water under (0 for rail/clear)
+ }
+ } else { // tunnel entrance/exit
+ _m[t].m2 = 0;
+ _m[t].m3 = HasBit(_m[t].m5, 3); // monorail
+ _m[t].m5 &= HasBit(_m[t].m5, 3) ? 0x03 : 0x07 ; // direction, transport type (== 0 for rail)
+ }
+ break;
+
+ case MP_UNMOVABLE:
+ _m[t].m2 = 0;
+ _m[t].m3 = 0;
+ break;
+
+ default:
+ return false;
+
+ }
+ }
+
+ FixOldMapArray();
+
+ return true;
+}
+
+static Engine *_old_engines;
+
+static bool FixTTOEngines()
+{
+ /** TTD->TTO remapping of engines; 255 means there is no equivalent. SVXConverter uses (almost) the same table. */
+ static const EngineID ttd_to_tto[] = {
+ 0, 255, 255, 255, 255, 255, 255, 255, 5, 7, 8, 9, 10, 11, 12, 13,
+ 255, 255, 255, 255, 255, 255, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 31, 255, 32, 33, 34, 35, 36, 37, 38,
+ 39, 40, 41, 42, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 44, 45, 46, 255, 255, 255, 255, 47, 48, 255, 49, 50,
+ 255, 255, 255, 255, 51, 52, 255, 53, 54, 255, 55, 56, 255, 57, 58, 255,
+ 59, 60, 255,61, 62, 255, 63, 64, 255, 65, 66, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 67, 68, 69, 70,
+ 71, 255, 255, 76, 77, 255, 255, 78, 79, 80, 81, 82, 83, 84, 85, 86,
+ 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 102, 255, 255
+ };
+
+ /** TTO->TTD remapping of engines. SVXConverter uses the same table. */
+ static const EngineID tto_to_ttd[] = {
+ 0, 0, 8, 8, 8, 8, 8, 9, 10, 11, 12, 13, 14, 15, 15, 22,
+ 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 55,
+ 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 116, 116, 117, 118, 123,
+ 124, 126, 127, 132, 133, 135, 136, 138, 139, 141, 142, 144, 145, 147, 148, 150,
+ 151, 153, 154, 204, 205, 206, 207, 208, 211, 212, 211, 212, 211, 212, 215, 216,
+ 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232,
+ 233, 234, 235, 236, 237, 238, 253
+ };
+
+ Vehicle *v;
+ FOR_ALL_VEHICLES(v) {
+ if (v->engine_type >= lengthof(tto_to_ttd)) return false;
+ v->engine_type = tto_to_ttd[v->engine_type];
+ }
+
+ /* Load the default engine set. Many of them will be overriden later */
+ uint j = 0;
+ for (uint i = 0; i < lengthof(_orig_rail_vehicle_info); i++, j++) new (GetTempDataEngine(j)) Engine(VEH_TRAIN, i);
+ for (uint i = 0; i < lengthof(_orig_road_vehicle_info); i++, j++) new (GetTempDataEngine(j)) Engine(VEH_ROAD, i);
+ for (uint i = 0; i < lengthof(_orig_ship_vehicle_info); i++, j++) new (GetTempDataEngine(j)) Engine(VEH_SHIP, i);
+ for (uint i = 0; i < lengthof(_orig_aircraft_vehicle_info); i++, j++) new (GetTempDataEngine(j)) Engine(VEH_AIRCRAFT, i);
+
+ Date aging_date = min(_date + DAYS_TILL_ORIGINAL_BASE_YEAR, ConvertYMDToDate(2050, 0, 1));
+
+ for (EngineID i = 0; i < 256; i++) {
+ int oi = ttd_to_tto[i];
+ Engine *e = GetTempDataEngine(i);
+
+ if (oi == 255) {
+ /* Default engine is used */
+ _date += DAYS_TILL_ORIGINAL_BASE_YEAR;
+ StartupOneEngine(e, aging_date);
+ e->intro_date -= DAYS_TILL_ORIGINAL_BASE_YEAR;
+ _date -= DAYS_TILL_ORIGINAL_BASE_YEAR;
+
+ /* Make sure for example monorail and maglev are available when they should be */
+ if (_date >= e->intro_date && HasBit(e->info.climates, 0)) {
+ e->flags |= ENGINE_AVAILABLE;
+ e->company_avail = (CompanyMask)0xFF;
+ e->age = _date > e->intro_date ? (_date - e->intro_date) / 30 : 0;
+ }
+ } else {
+ /* Using data from TTO savegame */
+ Engine *oe = &_old_engines[oi];
+
+ e->intro_date = oe->intro_date;
+ e->age = oe->age;
+ e->reliability = oe->reliability;
+ e->reliability_spd_dec = oe->reliability_spd_dec;
+ e->reliability_start = oe->reliability_start;
+ e->reliability_max = oe->reliability_max;
+ e->reliability_final = oe->reliability_final;
+ e->duration_phase_1 = oe->duration_phase_1;
+ e->duration_phase_2 = oe->duration_phase_2;
+ e->duration_phase_3 = oe->duration_phase_3;
+ e->lifelength = oe->lifelength;
+ e->flags = oe->flags;
+
+ e->company_avail = 0;
+
+ /* One or more engines were remapped to this one. Make this engine available
+ * if at least one of them was available. */
+ for (uint j = 0; j < lengthof(tto_to_ttd); j++) {
+ if (tto_to_ttd[j] == i && _old_engines[j].company_avail != 0) {
+ e->company_avail = (CompanyMask)0xFF;
+ e->flags |= ENGINE_AVAILABLE;
+ break;
+ }
+ }
+
+ e->preview_company_rank = 0;
+ e->preview_wait = 0;
+ e->name = NULL;
+
+ e->info.climates = 1;
+ }
+ }
+
+ return true;
+}
+
+static void FixTTOCompanies()
+{
+ Company *c;
+ FOR_ALL_COMPANIES(c) {
+ c->cur_economy.company_value = CalculateCompanyValue(c); // company value history is zeroed
+ }
+}
+
+static inline byte RemapTTOColour(byte tto)
+{
+ /** Lossy remapping of TTO colours to TTD colours. SVXConverter uses the same conversion. */
+ static const byte tto_colour_remap[] = {
+ COLOUR_DARK_BLUE, COLOUR_GREY, COLOUR_YELLOW, COLOUR_RED,
+ COLOUR_PURPLE, COLOUR_DARK_GREEN, COLOUR_ORANGE, COLOUR_PALE_GREEN,
+ COLOUR_BLUE, COLOUR_GREEN, COLOUR_CREAM, COLOUR_BROWN,
+ COLOUR_WHITE, COLOUR_LIGHT_BLUE, COLOUR_MAUVE, COLOUR_PINK
+ };
+
+ if ((size_t)tto >= lengthof(tto_colour_remap)) return COLOUR_GREY; // this shouldn't happen
+
+ return tto_colour_remap[tto];
+}
+
+static inline uint RemapTownIndex(uint x)
+{
+ return _savegame_type == SGT_TTO ? (x - 0x264) / 78 : (x - 0x264) / 94;
+}
+
+static inline uint RemapOrderIndex(uint x)
+{
+ return _savegame_type == SGT_TTO ? (x - 0x1AC4) / 2 : (x - 0x1C18) / 2;
+}
extern TileIndex *_animated_tile_list;
extern uint _animated_tile_count;
@@ -177,6 +437,11 @@ static void ReadTTDPatchFlags()
_read_ttdpatch_flags = true;
+ if (_savegame_type == SGT_TTO) {
+ _old_vehicle_multiplier = 1;
+ return;
+ }
+
/* 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
@@ -249,24 +514,32 @@ static const OldChunks town_chunk[] = {
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_TTD | OC_UINT16, Town, new_act_food ),
+ OCL_SVAR( OC_TTD | OC_UINT16, Town, new_act_water ),
+ OCL_SVAR( OC_TTD | OC_UINT16, Town, act_food ),
+ OCL_SVAR( OC_TTD | 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_CNULL( OC_TTD, 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;
+ if (t->xy != 0) {
+ if (_savegame_type == SGT_TTO) {
+ /* 0x10B6 is auto-generated name, others are custom names */
+ t->townnametype = t->townnametype == 0x10B6 ? 0x20C1 : t->townnametype + 0x2A00;
+ }
+ } else {
+ t->xy = INVALID_TILE;
+ }
return true;
}
@@ -283,11 +556,11 @@ static bool LoadOldOrder(LoadgameState *ls, int num)
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())
+ /* Relink the orders to eachother (in 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;
}
@@ -317,7 +590,7 @@ static bool LoadOldAnimTileList(LoadgameState *ls, int num)
static const OldChunks depot_chunk[] = {
OCL_SVAR( OC_TILE, Depot, xy ),
- OCL_VAR ( OC_UINT32, 1, &_old_town_index ),
+ OCL_VAR ( OC_UINT32, 1, &_old_town_index ),
OCL_END()
};
@@ -327,7 +600,7 @@ static bool LoadOldDepot(LoadgameState *ls, int num)
if (!LoadChunk(ls, d, depot_chunk)) return false;
if (d->xy != 0) {
- GetDepot(num)->town_index = REMAP_TOWN_IDX(_old_town_index);
+ d->town_index = RemapTownIndex(_old_town_index);
} else {
d->xy = INVALID_TILE;
}
@@ -345,10 +618,24 @@ static const OldChunks price_chunk[] = {
static bool LoadOldPrice(LoadgameState *ls, int num)
{
+ if (_savegame_type == SGT_TTO && num == 25) {
+ /* clear_fields == build_road_depot (TTO didn't have this price) */
+ ((Money*)&_price)[25] = ((Money*)&_price)[6];
+ _price_frac[25] = _price_frac[6];
+ return true;
+ }
+
if (!LoadChunk(ls, NULL, price_chunk)) return false;
+ if (_savegame_type == SGT_TTO) {
+ /* base prices are different in these two cases */
+ if (num == 15) _old_price = ClampToI32(((Money)_old_price) * 20 / 3); // build_railvehicle
+ if (num == 17) _old_price = ClampToI32(((Money)_old_price) * 10); // aircraft_base
+ }
+
+
/* 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 */
+ * so just access the struct as an array of int32s */
((Money*)&_price)[num] = _old_price;
_price_frac[num] = _old_price_frac;
@@ -365,15 +652,33 @@ static const OldChunks cargo_payment_rate_chunk[] = {
static bool LoadOldCargoPaymentRate(LoadgameState *ls, int num)
{
+ if (_savegame_type == SGT_TTO && num == 11) { // TTD has 1 more cargo type
+ _cargo_payment_rates[num] = _cargo_payment_rates[9];
+ _cargo_payment_rates_frac[num] = _cargo_payment_rates_frac[9];
+ return true;
+ }
+
if (!LoadChunk(ls, NULL, cargo_payment_rate_chunk)) return false;
+ if (_savegame_type == SGT_TTO) {
+ /* SVXConverter about cargo payment rates correction:
+ * "increase them to compensate for the faster time advance in TTD compared to TTO
+ * which otherwise would cause much less income while the annual running costs of
+ * the vehicles stay the same" */
+
+ Money m = ((((Money)_old_price) << 16) + _old_price_frac) * 124 / 74;
+
+ _old_price = m >> 16;
+ _old_price_frac = GB(m, 0, 16);
+ }
+
_cargo_payment_rates[num] = -_old_price;
_cargo_payment_rates_frac[num] = _old_price_frac;
return true;
}
-static uint _current_station_id;
+VehicleID _current_station_id;
static uint16 _waiting_acceptance;
static uint8 _cargo_source;
static uint8 _cargo_days;
@@ -392,10 +697,13 @@ static const OldChunks goods_chunk[] = {
static bool LoadOldGood(LoadgameState *ls, int num)
{
+ /* for TTO games, 12th (num == 11) goods entry is created in the Station constructor */
+ if (_savegame_type == SGT_TTO && num == 11) return true;
+
Station *st = GetStation(_current_station_id);
GoodsEntry *ge = &st->goods[num];
- bool ret = LoadChunk(ls, ge, goods_chunk);
- if (!ret) return false;
+
+ if (!LoadChunk(ls, ge, goods_chunk)) return false;
SB(ge->acceptance_pickup, GoodsEntry::ACCEPTANCE, 1, HasBit(_waiting_acceptance, 15));
SB(ge->acceptance_pickup, GoodsEntry::PICKUP, 1, _cargo_source != 0xFF);
@@ -406,7 +714,8 @@ static bool LoadOldGood(LoadgameState *ls, int num)
cp->days_in_transit = _cargo_days;
ge->cargo.Append(cp);
}
- return ret;
+
+ return true;
}
static const OldChunks station_chunk[] = {
@@ -435,30 +744,44 @@ static const OldChunks station_chunk[] = {
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_SVAR( OC_TTD | OC_UINT8, Station, airport_type ),
+ OCL_SVAR( OC_TTO | OC_FILE_U16 | OC_VAR_U32, Station, airport_flags ),
+ OCL_NULL( 3 ), ///< bus/truck status, blocked months, no longer in use
+ OCL_CNULL( OC_TTD, 1 ), ///< unknown
+ OCL_SVAR( OC_TTD | OC_FILE_U16 | OC_VAR_U32, Station, airport_flags ),
+ OCL_CNULL( OC_TTD, 2 ), ///< last_vehicle. now last_vehicle_type
+ OCL_CNULL( OC_TTD, 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 (!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);
+ st->town = GetTown(RemapTownIndex(_old_town_index));
+
+ if (_savegame_type == SGT_TTO) {
+ if (IsInsideBS(_old_string_id, 0x180F, 32)) {
+ st->string_id = STR_SV_STNAME + (_old_string_id - 0x180F); // automatic name
+ } else {
+ st->string_id = _old_string_id + 0x2800; // custom name
+ }
+
+ if (HasBit(st->airport_flags, 8)) {
+ st->airport_type = 1; // large airport
+ } else if (HasBit(st->airport_flags, 6)) {
+ st->airport_type = 3; // oil rig
+ } else {
+ st->airport_type = 0; // small airport
+ }
+ } else {
+ st->string_id = RemapOldStringID(_old_string_id);
+ }
} else {
st->xy = INVALID_TILE;
}
@@ -473,8 +796,10 @@ static const OldChunks industry_chunk[] = {
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_TTD | OC_UINT16, Industry, produced_cargo_waiting[0] ),
+ OCL_SVAR( OC_TTD | OC_UINT16, Industry, produced_cargo_waiting[1] ),
+ OCL_SVAR( OC_TTO | OC_FILE_U8 | OC_VAR_U16, Industry, produced_cargo_waiting[0] ),
+ OCL_SVAR( OC_TTO | OC_FILE_U8 | OC_VAR_U16, Industry, produced_cargo_waiting[1] ),
OCL_SVAR( OC_UINT8, Industry, production_rate[0] ),
OCL_SVAR( OC_UINT8, Industry, production_rate[1] ),
@@ -497,13 +822,14 @@ static const OldChunks industry_chunk[] = {
OCL_SVAR( OC_UINT16, Industry, last_month_transported[1] ),
OCL_SVAR( OC_UINT8, Industry, type ),
+ OCL_SVAR( OC_TTO | OC_FILE_U8 | OC_VAR_U16, Industry, counter ),
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_SVAR( OC_TTD | OC_FILE_U8 | OC_VAR_I32, Industry, last_prod_year ),
+ OCL_SVAR( OC_TTD | OC_UINT16, Industry, counter ),
+ OCL_SVAR( OC_TTD | OC_UINT8, Industry, was_cargo_delivered ),
- OCL_NULL( 9 ), ///< Random junk at the end of this chunk
+ OCL_CNULL( OC_TTD, 9 ), ///< Random junk at the end of this chunk
OCL_END()
};
@@ -514,7 +840,19 @@ static bool LoadOldIndustry(LoadgameState *ls, int num)
if (!LoadChunk(ls, i, industry_chunk)) return false;
if (i->xy != 0) {
- i->town = GetTown(REMAP_TOWN_IDX(_old_town_index));
+ i->town = GetTown(RemapTownIndex(_old_town_index));
+
+ if (_savegame_type == SGT_TTO) {
+ if (i->type > 0x06) i->type++; // Printing Works were added
+ if (i->type == 0x0A) i->type = 0x12; // Iron Ore Mine has different ID
+
+ YearMonthDay ymd;
+ ConvertDateToYMD(_date, &ymd);
+ i->last_prod_year = ymd.year;
+
+ i->random_color = RemapTTOColour(i->random_color);
+ }
+
IncIndustryTypeCount(i->type);
} else {
i->xy = INVALID_TILE;
@@ -523,7 +861,7 @@ static bool LoadOldIndustry(LoadgameState *ls, int num)
return true;
}
-static CompanyID _current_company_id;
+CompanyID _current_company_id;
static int32 _old_yearly;
static const OldChunks _company_yearly_chunk[] = {
@@ -531,13 +869,16 @@ static const OldChunks _company_yearly_chunk[] = {
OCL_END()
};
-static bool OldCompanyYearly(LoadgameState *ls, int num)
+static bool LoadOldCompanyYearly(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;
+ for (uint i = 0; i < 13; i++) {
+ if (_savegame_type == SGT_TTO && i == 6) {
+ _old_yearly = 0; // property maintenance
+ } else {
+ if (!LoadChunk(ls, NULL, _company_yearly_chunk)) return false;
+ }
c->yearly_expenses[num][i] = _old_yearly;
}
@@ -550,14 +891,13 @@ static const OldChunks _company_economy_chunk[] = {
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_SVAR( OC_TTD | OC_FILE_I32 | OC_VAR_I64, CompanyEconomyEntry, company_value ),
OCL_END()
};
-static bool OldCompanyEconomy(LoadgameState *ls, int num)
+static bool LoadOldCompanyEconomy(LoadgameState *ls, int num)
{
- int i;
Company *c = GetCompany(_current_company_id);
if (!LoadChunk(ls, &c->cur_economy, _company_economy_chunk)) return false;
@@ -566,7 +906,7 @@ static bool OldCompanyEconomy(LoadgameState *ls, int num)
c->cur_economy.income = -c->cur_economy.income;
c->cur_economy.expenses = -c->cur_economy.expenses;
- for (i = 0; i < 24; i++) {
+ for (uint 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;
@@ -593,10 +933,11 @@ static const OldChunks _company_chunk[] = {
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_SVAR( OC_TTD | OC_UINT32, Company, cargo_types ),
+ OCL_SVAR( OC_TTO | OC_FILE_U16 | OC_VAR_U32, Company, cargo_types ),
- OCL_CHUNK( 3, OldCompanyYearly ),
- OCL_CHUNK( 1, OldCompanyEconomy ),
+ OCL_CHUNK( 3, LoadOldCompanyYearly ),
+ OCL_CHUNK( 1, LoadOldCompanyEconomy ),
OCL_SVAR( OC_FILE_U16 | OC_VAR_I32, Company, inaugurated_year),
OCL_SVAR( OC_TILE, Company, last_build_coordinate ),
@@ -605,15 +946,15 @@ static const OldChunks _company_chunk[] = {
OCL_NULL( 230 ), // Old AI
OCL_SVAR( OC_UINT8, Company, block_preview ),
- OCL_NULL( 1 ), // Old AI
- OCL_SVAR( OC_UINT8, Company, avail_railtypes ),
+ OCL_CNULL( OC_TTD, 1 ), // Old AI
+ OCL_SVAR( OC_TTD | 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_SVAR( OC_TTD | OC_UINT8, Company, share_owners[0] ),
+ OCL_SVAR( OC_TTD | OC_UINT8, Company, share_owners[1] ),
+ OCL_SVAR( OC_TTD | OC_UINT8, Company, share_owners[2] ),
+ OCL_SVAR( OC_TTD | OC_UINT8, Company, share_owners[3] ),
- OCL_NULL( 8 ), ///< junk at end of chunk
+ OCL_CNULL( OC_TTD, 8 ), ///< junk at end of chunk
OCL_END()
};
@@ -631,25 +972,55 @@ static bool LoadOldCompany(LoadgameState *ls, int num)
return true;
}
- c->name_1 = RemapOldStringID(_old_string_id);
- c->president_name_1 = RemapOldStringID(_old_string_id_2);
+ if (_savegame_type == SGT_TTO) {
+ /* adjust manager's face */
+ if (HasBit(c->face, 27) && GB(c->face, 26, 1) == GB(c->face, 19, 1)) {
+ /* if face would be black in TTD, adjust tie color and thereby face color */
+ ClrBit(c->face, 27);
+ }
- 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;
+ /* Company name */
+ if (_old_string_id == 0 || _old_string_id == 0x4C00) {
+ _old_string_id = STR_SV_UNNAMED; // "Unnamed"
+ } else if (GB(_old_string_id, 8, 8) == 0x52) {
+ _old_string_id += 0x2A00; // Custom name
+ } else {
+ _old_string_id = RemapOldStringID(_old_string_id += 0x240D); // Automatic name
+ }
+ c->name_1 = _old_string_id;
+
+ /* Manager name */
+ switch (_old_string_id_2) {
+ case 0x4CDA: _old_string_id_2 = SPECSTR_PRESIDENT_NAME; break; // automatic name
+ case 0x0006: _old_string_id_2 = STR_SV_EMPTY; break; // empty name
+ default: _old_string_id_2 = _old_string_id_2 + 0x2A00; break; // custom name
+ }
+ c->president_name_1 = _old_string_id_2;
+
+ c->colour = RemapTTOColour(c->colour);
+
+ if (num != 0) c->is_ai = true;
} 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;
- }
+ c->name_1 = RemapOldStringID(_old_string_id);
+ c->president_name_1 = RemapOldStringID(_old_string_id_2);
+
+ 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;
+ /* 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;
@@ -659,7 +1030,7 @@ static bool LoadOldCompany(LoadgameState *ls, int num)
static uint32 _old_order_ptr;
static uint16 _old_next_ptr;
-static uint32 _current_vehicle_id;
+VehicleID _current_vehicle_id;
static const OldChunks vehicle_train_chunk[] = {
OCL_SVAR( OC_UINT8, VehicleRail, track ),
@@ -773,8 +1144,9 @@ static const OldChunks vehicle_chunk[] = {
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_TTD | OC_UINT8, Vehicle, tick_counter ),
+ OCL_SVAR( OC_TTD | OC_UINT16, Vehicle, max_speed ),
+ OCL_SVAR( OC_TTO | OC_FILE_U8 | OC_VAR_U16, 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 ),
@@ -791,23 +1163,29 @@ static const OldChunks vehicle_chunk[] = {
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_TTD | OC_UINT16, Vehicle, cur_speed ),
+ OCL_SVAR( OC_TTO | OC_FILE_U8 | OC_VAR_U16, 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_SVAR( OC_TTD | OC_UINT16, Vehicle, cargo_cap ),
+ OCL_SVAR( OC_TTO | OC_FILE_U8 | OC_VAR_U16, Vehicle, cargo_cap ),
+ OCL_VAR ( OC_TTD | OC_UINT16, 1, &_cargo_count ),
+ OCL_VAR ( OC_TTO | OC_FILE_U8 | OC_VAR_U16, 1, &_cargo_count ),
OCL_VAR ( OC_UINT8, 1, &_cargo_source ),
OCL_VAR ( OC_UINT8, 1, &_cargo_days ),
+ OCL_SVAR( OC_TTO | OC_UINT8, Vehicle, tick_counter ),
+
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_TTD | OC_UINT16, Vehicle, engine_type ),
+ OCL_SVAR( OC_TTO | OC_FILE_U8 | OC_VAR_U16, Vehicle, engine_type ),
OCL_SVAR( OC_UINT8, Vehicle, spritenum ),
OCL_SVAR( OC_UINT8, Vehicle, day_counter ),
@@ -817,6 +1195,8 @@ static const OldChunks vehicle_chunk[] = {
OCL_SVAR( OC_UINT8, Vehicle, breakdown_delay ),
OCL_SVAR( OC_UINT8, Vehicle, breakdown_chance ),
+ OCL_CNULL( OC_TTO, 1 ),
+
OCL_SVAR( OC_UINT16, Vehicle, reliability ),
OCL_SVAR( OC_UINT16, Vehicle, reliability_spd_dec ),
@@ -831,7 +1211,8 @@ static const OldChunks vehicle_chunk[] = {
OCL_CHUNK( 1, LoadOldVehicleUnion ),
- OCL_NULL( 20 ), ///< Junk at end of struct (TTDPatch has some data in it)
+ OCL_CNULL( OC_TTO, 24 ), ///< junk
+ OCL_CNULL( OC_TTD, 20 ), ///< junk at end of struct (TTDPatch has some data in it)
OCL_END()
};
@@ -844,40 +1225,116 @@ bool LoadOldVehicle(LoadgameState *ls, int num)
for (uint 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 (_savegame_type == SGT_TTO) {
+ uint type = ReadByte(ls);
+ switch (type) {
+ default: return false;
+ case 0x00 /*VEH_INVALID */: v = new (_current_vehicle_id) InvalidVehicle(); break;
+ case 0x25 /* MONORAIL */:
+ case 0x20 /*VEH_TRAIN */: v = new (_current_vehicle_id) Train(); break;
+ case 0x21 /*VEH_ROAD */: v = new (_current_vehicle_id) RoadVehicle(); break;
+ case 0x22 /*VEH_SHIP */: v = new (_current_vehicle_id) Ship(); break;
+ case 0x23 /*VEH_AIRCRAFT*/: v = new (_current_vehicle_id) Aircraft(); break;
+ case 0x24 /*VEH_EFFECT */: v = new (_current_vehicle_id) EffectVehicle(); break;
+ case 0x26 /*VEH_DISASTER*/: v = new (_current_vehicle_id) DisasterVehicle(); break;
+ }
+
+ if (!LoadChunk(ls, v, vehicle_chunk)) return false;
+
+ SpriteID sprite = v->cur_image;
+ /* no need to override other sprites */
+ if (IsInsideMM(sprite, 1460, 1465)) {
+ sprite += 580; // aircraft smoke puff
+ } else if (IsInsideMM(sprite, 2096, 2115)) {
+ sprite += 977; // special effects part 1
+ } else if (IsInsideMM(sprite, 2396, 2436)) {
+ sprite += 1305; // special effects part 2
+ } else if (IsInsideMM(sprite, 2516, 2539)) {
+ sprite += 1385; // rotor or disaster-related vehicles
+ }
+ v->cur_image = sprite;
+
+ switch (v->type) {
+ case VEH_TRAIN: {
+ static const byte spriteset_rail[] = {
+ 0, 2, 4, 4, 8, 10, 12, 14, 16, 18, 20, 22, 40, 42, 44, 46,
+ 48, 52, 54, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 120, 122,
+ 124, 126, 128, 130, 132, 134, 136, 138, 140
+ };
+ if (v->spritenum / 2 >= lengthof(spriteset_rail)) return false;
+ v->spritenum = spriteset_rail[v->spritenum / 2]; // adjust railway sprite set offset
+ v->u.rail.railtype = type == 0x25 ? 1 : 0; // monorail / rail
+ break;
+ }
+
+ case VEH_ROAD:
+ if (v->spritenum >= 22) v->spritenum += 12;
+ break;
+
+ case VEH_SHIP:
+ v->spritenum += 2;
+
+ switch (v->spritenum) {
+ case 2: // oil tanker && cargo type != oil
+ if (v->cargo_type != CT_OIL) v->spritenum = 0; // make it a coal/goods ship
+ break;
+ case 4: // passenger ship && cargo type == mail
+ if (v->cargo_type == CT_MAIL) v->spritenum = 0; // make it a mail ship
+ break;
+ default:
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ switch (_old_string_id) {
+ case 0x0000: break; // empty (invalid vehicles)
+ case 0x0006: _old_string_id = STR_SV_EMPTY; break; // empty (special vehicles)
+ case 0x8495: _old_string_id = STR_SV_TRAIN_NAME; break; // "Train X"
+ case 0x8842: _old_string_id = STR_SV_ROADVEH_NAME; break; // "Road Vehicle X"
+ case 0x8C3B: _old_string_id = STR_SV_SHIP_NAME; break; // "Ship X"
+ case 0x9047: _old_string_id = STR_SV_AIRCRAFT_NAME; break; // "Aircraft X"
+ default: _old_string_id += 0x2A00; break; // custom name
+ }
+
+ _old_vehicle_names[_current_vehicle_id] = _old_string_id;
+ } else {
+ /* Read the vehicle type and allocate the right vehicle */
+ 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;
+
+ _old_vehicle_names[_current_vehicle_id] = RemapOldStringID(_old_string_id);
+
+ /* 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);
+ uint max = _savegame_type == SGT_TTO ? 3000 : 5000;
+ uint old_id = RemapOrderIndex(_old_order_ptr);
+ if (old_id < max) v->orders.old = GetOrder(old_id); // don't accept orders > max number of orders
}
v->current_order.AssignOrder(UnpackOldOrder(_old_order));
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);
-
if (_cargo_count != 0) {
CargoPacket *cp = new CargoPacket((_cargo_source == 0xFF) ? INVALID_STATION : _cargo_source, _cargo_count);
cp->days_in_transit = _cargo_days;
@@ -905,8 +1362,11 @@ static bool LoadOldSign(LoadgameState *ls, int num)
if (!LoadChunk(ls, si, sign_chunk)) return false;
if (_old_string_id != 0) {
- _old_string_id = RemapOldStringID(_old_string_id);
- si->name = CopyFromOldName(_old_string_id);
+ if (_savegame_type == SGT_TTO) {
+ if (_old_string_id != 0x140A) si->name = CopyFromOldName(_old_string_id + 0x2A00);
+ } else {
+ si->name = CopyFromOldName(RemapOldStringID(_old_string_id));
+ }
si->owner = OWNER_NONE;
}
@@ -931,14 +1391,14 @@ static const OldChunks engine_chunk[] = {
OCL_SVAR( OC_UINT8, Engine, preview_company_rank ),
OCL_SVAR( OC_UINT8, Engine, preview_wait ),
- OCL_NULL( 2 ), ///< railtype + junk
+ OCL_CNULL( OC_TTD, 2 ), ///< railtype + junk
OCL_END()
};
static bool LoadOldEngine(LoadgameState *ls, int num)
{
- Engine *e = GetTempDataEngine(num);
+ Engine *e = _savegame_type == SGT_TTO ? &_old_engines[num] : GetTempDataEngine(num);
return LoadChunk(ls, e, engine_chunk);
}
@@ -958,7 +1418,7 @@ static const OldChunks subsidy_chunk[] = {
OCL_END()
};
-static inline bool LoadOldSubsidy(LoadgameState *ls, int num)
+static bool LoadOldSubsidy(LoadgameState *ls, int num)
{
return LoadChunk(ls, &_subsidies[num], subsidy_chunk);
}
@@ -984,7 +1444,7 @@ static const OldChunks game_difficulty_chunk[] = {
OCL_END()
};
-static inline bool LoadOldGameDifficulty(LoadgameState *ls, int num)
+static bool LoadOldGameDifficulty(LoadgameState *ls, int num)
{
bool ret = LoadChunk(ls, &_settings_game.difficulty, game_difficulty_chunk);
_settings_game.difficulty.max_loan *= 1000;
@@ -994,24 +1454,30 @@ static inline bool LoadOldGameDifficulty(LoadgameState *ls, int num)
static bool LoadOldMapPart1(LoadgameState *ls, int num)
{
- uint i;
+ if (_savegame_type == SGT_TTO) {
+ MemSetT(_m, 0, OLD_MAP_SIZE);
+ MemSetT(_me, 0, OLD_MAP_SIZE);
+ }
- for (i = 0; i < OLD_MAP_SIZE; i++) {
+ for (uint i = 0; i < OLD_MAP_SIZE; i++) {
_m[i].m1 = ReadByte(ls);
}
- for (i = 0; i < OLD_MAP_SIZE; i++) {
+ for (uint 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);
+
+ if (_savegame_type != SGT_TTO) {
+ for (uint i = 0; i < OLD_MAP_SIZE; i++) {
+ _old_map3[i * 2] = ReadByte(ls);
+ _old_map3[i * 2 + 1] = ReadByte(ls);
+ }
+ for (uint 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;
@@ -1090,23 +1556,38 @@ static bool LoadTTDPatchExtraChunks(LoadgameState *ls, int num)
extern TileIndex _cur_tileloop_tile;
static uint32 _old_cur_town_ctr;
static const OldChunks main_chunk[] = {
- OCL_ASSERT( 0 ),
+ OCL_ASSERT( OC_TTD, 0 ),
+ OCL_ASSERT( OC_TTO, 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_ASSERT( OC_TTD, 0x264 ),
+ OCL_ASSERT( OC_TTO, 0x264 ),
+
+ OCL_CCHUNK( OC_TTD, 70, LoadOldTown ),
+ OCL_CCHUNK( OC_TTO, 80, LoadOldTown ),
+
+ OCL_ASSERT( OC_TTD, 0x1C18 ),
+ OCL_ASSERT( OC_TTO, 0x1AC4 ),
+
+ OCL_CCHUNK( OC_TTD, 5000, LoadOldOrder ),
+ OCL_CCHUNK( OC_TTO, 3000, LoadOldOrder ),
+
+ OCL_ASSERT( OC_TTD, 0x4328 ),
+ OCL_ASSERT( OC_TTO, 0x3234 ),
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_ASSERT( OC_TTO, 0x3438 ),
+
+ OCL_CCHUNK( OC_TTD, 255, LoadOldDepot ),
+ OCL_CCHUNK( OC_TTO, 252, LoadOldDepot ),
+
+ OCL_ASSERT( OC_TTD, 0x4B26 ),
+ OCL_ASSERT( OC_TTO, 0x3A20 ),
OCL_VAR ( OC_UINT32, 1, &_old_cur_town_ctr ),
OCL_NULL( 2 ), ///< timer_counter, no longer in use
@@ -1116,37 +1597,69 @@ static const OldChunks main_chunk[] = {
OCL_VAR ( OC_UINT16, 1, &_tick_counter ),
OCL_VAR ( OC_TILE, 1, &_cur_tileloop_tile ),
+ OCL_ASSERT( OC_TTO, 0x3A2E ),
+
OCL_CHUNK( 49, LoadOldPrice ),
+
+ OCL_ASSERT( OC_TTO, 0x3B4E ),
+
OCL_CHUNK( 12, LoadOldCargoPaymentRate ),
- OCL_ASSERT( 0x4CBA ),
+ OCL_ASSERT( OC_TTD, 0x4CBA ),
+ OCL_ASSERT( OC_TTO, 0x3BA6 ),
OCL_CHUNK( 1, LoadOldMapPart1 ),
- OCL_ASSERT( 0x48CBA ),
+ OCL_ASSERT( OC_TTD, 0x48CBA ),
+ OCL_ASSERT( OC_TTO, 0x23BA6 ),
+
+ OCL_CCHUNK( OC_TTD, 250, LoadOldStation ),
+ OCL_CCHUNK( OC_TTO, 200, LoadOldStation ),
+
+ OCL_ASSERT( OC_TTO, 0x29E16 ),
+
+ OCL_CCHUNK( OC_TTD, 90, LoadOldIndustry ),
+ OCL_CCHUNK( OC_TTO, 100, LoadOldIndustry ),
+
+ OCL_ASSERT( OC_TTO, 0x2ADB6 ),
- OCL_CHUNK(250, LoadOldStation ),
- OCL_CHUNK( 90, LoadOldIndustry ),
OCL_CHUNK( 8, LoadOldCompany ),
- OCL_ASSERT( 0x547F2 ),
+ OCL_ASSERT( OC_TTD, 0x547F2 ),
+ OCL_ASSERT( OC_TTO, 0x2C746 ),
+
+ OCL_CCHUNK( OC_TTD, 850, LoadOldVehicle ),
+ OCL_CCHUNK( OC_TTO, 800, LoadOldVehicle ),
- OCL_CHUNK( 850, LoadOldVehicle ),
+ OCL_ASSERT( OC_TTD, 0x6F0F2 ),
+ OCL_ASSERT( OC_TTO, 0x45746 ),
- OCL_ASSERT( 0x6F0F2 ),
+ OCL_VAR ( OC_TTD | OC_UINT8 | OC_DEREFERENCE_POINTER, 32 * 500, &_old_name_array ),
+ OCL_VAR ( OC_TTO | OC_UINT8 | OC_DEREFERENCE_POINTER, 24 * 200, &_old_name_array ),
- OCL_VAR ( OC_UINT8 | OC_DEREFERENCE_POINTER, 32 * 500, &_old_name_array ),
+ OCL_ASSERT( OC_TTO, 0x46A06 ),
OCL_NULL( 0x2000 ), ///< Old hash-table, no longer in use
OCL_CHUNK( 40, LoadOldSign ),
- OCL_CHUNK(256, LoadOldEngine ),
+
+ OCL_ASSERT( OC_TTO, 0x48C36 ),
+
+ OCL_CCHUNK( OC_TTD, 256, LoadOldEngine ),
+ OCL_CCHUNK( OC_TTO, 103, LoadOldEngine ),
+
+ OCL_ASSERT( OC_TTO, 0x496AC ),
OCL_VAR ( OC_UINT16, 1, &_vehicle_id_ctr_day ),
OCL_CHUNK( 8, LoadOldSubsidy ),
+ OCL_ASSERT( OC_TTO, 0x496CE ),
+
OCL_VAR ( OC_FILE_U16 | OC_VAR_U32, 1, &_next_competitor_start ),
+
+ OCL_CNULL( OC_TTO, 2 ), ///< available monorail bitmask
+
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 ),
@@ -1157,11 +1670,13 @@ static const OldChunks main_chunk[] = {
OCL_VAR ( OC_UINT16, 1, &_disaster_delay ),
- OCL_NULL( 144 ), ///< cargo-stuff, calculated in InitializeLandscapeVariables
+ OCL_ASSERT( OC_TTO, 0x496E4 ),
+
+ OCL_CNULL( OC_TTD, 144 ), ///< cargo-stuff, calculated in InitializeLandscapeVariables
- OCL_CHUNK(256, LoadOldEngineName ),
+ OCL_CCHUNK( OC_TTD, 256, LoadOldEngineName ),
- OCL_NULL( 144 ), ///< AI cargo-stuff, calculated in InitializeLandscapeVariables
+ OCL_CNULL( OC_TTD, 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 ),
@@ -1182,23 +1697,26 @@ static const OldChunks main_chunk[] = {
OCL_CHUNK( 1, LoadOldGameDifficulty ),
- OCL_ASSERT( 0x77130 ),
+ OCL_ASSERT( OC_TTD, 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_VAR ( OC_TTD | OC_UINT8, 1, &_settings_game.game_creation.landscape ),
+ OCL_VAR ( OC_TTD | OC_UINT8, 1, &_trees_tick_ctr ),
- OCL_NULL( 32 ), ///< new_industry_randtable, no longer used (because of new design)
- OCL_NULL( 36 ), ///< cargo-stuff, calculated in InitializeLandscapeVariables
+ OCL_CNULL( OC_TTD, 1 ), ///< Custom vehicle types yes/no, no longer used
+ OCL_VAR ( OC_TTD | OC_UINT8, 1, &_settings_game.game_creation.snow_line ),
- OCL_ASSERT( 0x77179 ),
+ OCL_CNULL( OC_TTD, 32 ), ///< new_industry_randtable, no longer used (because of new design)
+ OCL_CNULL( OC_TTD, 36 ), ///< cargo-stuff, calculated in InitializeLandscapeVariables
+
+ OCL_ASSERT( OC_TTD, 0x77179 ),
+ OCL_ASSERT( OC_TTO, 0x4971D ),
OCL_CHUNK( 1, LoadOldMapPart2 ),
- OCL_ASSERT( 0x97179 ),
+ OCL_ASSERT( OC_TTD, 0x97179 ),
+ OCL_ASSERT( OC_TTO, 0x6971D ),
/* Below any (if available) extra chunks from TTDPatch can follow */
OCL_CHUNK(1, LoadTTDPatchExtraChunks),
@@ -1206,11 +1724,10 @@ static const OldChunks main_chunk[] = {
OCL_END()
};
-bool LoadOldMain(LoadgameState *ls)
+bool LoadTTDMain(LoadgameState *ls)
{
- _savegame_type = SGT_TTD;
- _ttdp_version = 0;
_read_ttdpatch_flags = false;
+ _ttdp_version = 0;
DEBUG(oldloader, 3, "Reading main chunk...");
/* Load the biggest chunk */
@@ -1224,13 +1741,13 @@ bool LoadOldMain(LoadgameState *ls)
}
DEBUG(oldloader, 3, "Done, converting game data...");
- FixOldMapArray();
+ FixTTDMapArray();
/* 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);
+ _cur_town_ctr = RemapTownIndex(_old_cur_town_ctr);
/* Fix the game to be compatible with OpenTTD */
FixOldTowns();
@@ -1246,3 +1763,44 @@ bool LoadOldMain(LoadgameState *ls)
return true;
}
+
+bool LoadTTOMain(LoadgameState *ls)
+{
+ DEBUG(oldloader, 3, "Reading main chunk...");
+
+ SmallStackSafeStackAlloc<byte, 103 * sizeof(Engine)> engines; // we don't want to call Engine constructor here
+ _old_engines = (Engine *)engines.data;
+ SmallStackSafeStackAlloc<StringID, 800> vehnames;
+ _old_vehicle_names = vehnames.data;
+
+ /* Load the biggest chunk */
+ if (!LoadChunk(ls, NULL, main_chunk)) {
+ DEBUG(oldloader, 0, "Loading failed");
+ return false;
+ }
+ DEBUG(oldloader, 3, "Done, converting game data...");
+
+ if (_settings_game.game_creation.town_name != 0) _settings_game.game_creation.town_name++;
+
+ _settings_game.game_creation.landscape = 0;
+ _trees_tick_ctr = 0xFF;
+
+ _cur_town_ctr = RemapTownIndex(_old_cur_town_ctr);
+
+ if (!FixTTOMapArray() || !FixTTOEngines()) {
+ DEBUG(oldloader, 0, "Conversion failed");
+ return false;
+ }
+
+ FixOldTowns();
+ FixOldVehicles();
+ FixTTOCompanies();
+
+ /* 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, "TTO savegame successfully converted");
+
+ return true;
+}
diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h
index f71b0dec4..ad79eb907 100644
--- a/src/saveload/saveload.h
+++ b/src/saveload/saveload.h
@@ -32,7 +32,9 @@ 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
+ SGT_OTTD, ///< OTTD savegame
+ SGT_TTO, ///< TTO savegame
+ SGT_INVALID = 0xFF ///< broken savegame (used internally)
};
void GenerateDefaultSaveName(char *buf, const char *last);
diff --git a/src/saveload/saveload_internal.h b/src/saveload/saveload_internal.h
index 246f9e697..d364bebab 100644
--- a/src/saveload/saveload_internal.h
+++ b/src/saveload/saveload_internal.h
@@ -9,6 +9,7 @@
#include "../company_manager_face.h"
#include "../order_base.h"
#include "../engine_type.h"
+#include "saveload.h"
void InitializeOldNames();
StringID RemapOldStringID(StringID s);
@@ -37,6 +38,9 @@ void CopyTempEngineData();
extern int32 _saved_scrollpos_x;
extern int32 _saved_scrollpos_y;
+extern SavegameType _savegame_type;
+extern uint32 _ttdp_version;
+
CompanyManagerFace ConvertFromOldCompanyManagerFace(uint32 face);
Order UnpackOldOrder(uint16 packed);
diff --git a/src/saveload/strings_sl.cpp b/src/saveload/strings_sl.cpp
index f436bdedf..e220d687a 100644
--- a/src/saveload/strings_sl.cpp
+++ b/src/saveload/strings_sl.cpp
@@ -8,6 +8,7 @@
#include "../core/bitmath_func.hpp"
#include "../core/alloc_func.hpp"
#include "../string_func.h"
+#include "saveload_internal.h"
#include "table/strings.h"
@@ -44,8 +45,8 @@ 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.
+ * They were all stored in a 512 by 32 (200 by 24 for TTO) 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.
*/
@@ -55,10 +56,11 @@ char *CopyFromOldName(StringID id)
if (GB(id, 11, 5) != 15) return NULL;
if (CheckSavegameVersion(37)) {
- /* Old names were 32 characters long, so 128 characters should be
+ /* Old names were 24/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)];
+ uint offs = _savegame_type == SGT_TTO ? 24 * GB(id, 0, 8) : 32 * GB(id, 0, 9);
+ const char *strfrom = &_old_name_array[offs];
char *strto = tmp;
for (; *strfrom != '\0'; strfrom++) {
@@ -109,7 +111,7 @@ void ResetOldNames()
void InitializeOldNames()
{
free(_old_name_array);
- _old_name_array = CallocT<char>(512 * 32);
+ _old_name_array = CallocT<char>(512 * 32); // 200 * 24 would be enough for TTO savegames
}
static void Load_NAME()