summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--depot.c2
-rw-r--r--economy.c6
-rw-r--r--engine.c2
-rw-r--r--industry_cmd.c2
-rw-r--r--misc.c2
-rw-r--r--misc_cmd.c1
-rw-r--r--network_gui.c1
-rw-r--r--order_cmd.c2
-rw-r--r--players.c8
-rw-r--r--saveload.c920
-rw-r--r--saveload.h181
-rw-r--r--sdl.c2
-rw-r--r--signs.c2
-rw-r--r--station_cmd.c6
-rw-r--r--stdafx.h10
-rw-r--r--texteff.c7
-rw-r--r--town_cmd.c2
-rw-r--r--ttd.c1
-rw-r--r--vehicle.c14
-rw-r--r--waypoint.c2
-rw-r--r--win32.c9
21 files changed, 649 insertions, 533 deletions
diff --git a/depot.c b/depot.c
index f52bd2573..289ef7a27 100644
--- a/depot.c
+++ b/depot.c
@@ -103,7 +103,7 @@ void InitializeDepot(void)
}
-static const byte _depot_desc[] = {
+static const SaveLoad _depot_desc[] = {
SLE_CONDVAR(Depot, xy, SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
SLE_CONDVAR(Depot, xy, SLE_UINT32, 6, 255),
SLE_VAR(Depot,town_index, SLE_UINT16),
diff --git a/economy.c b/economy.c
index ccee21343..bd42320f3 100644
--- a/economy.c
+++ b/economy.c
@@ -1065,7 +1065,7 @@ no_add:;
InvalidateWindow(WC_SUBSIDIES_LIST, 0);
}
-static const byte _subsidies_desc[] = {
+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),
@@ -1650,7 +1650,7 @@ static void SaveLoad_CAPR(void)
SlArray(&_cargo_payment_rates_frac, NUM_CARGO, SLE_UINT16);
}
-static const byte _economy_desc[] = {
+static const SaveLoad _economy_desc[] = {
SLE_VAR(Economy,max_loan, SLE_INT32),
SLE_VAR(Economy,max_loan_unround, SLE_INT32),
SLE_VAR(Economy,fluct, SLE_FILE_I16 | SLE_VAR_I32),
@@ -1663,7 +1663,7 @@ static const byte _economy_desc[] = {
// Economy variables
static void SaveLoad_ECMY(void)
{
- SlObject(&_economy, &_economy_desc);
+ SlObject(&_economy, _economy_desc);
}
const ChunkHandler _economy_chunk_handlers[] = {
diff --git a/engine.c b/engine.c
index f06d958b1..de1f4a089 100644
--- a/engine.c
+++ b/engine.c
@@ -927,7 +927,7 @@ int GetPlayerMaxRailtype(int p)
}
-static const byte _engine_desc[] = {
+static const SaveLoad _engine_desc[] = {
SLE_VAR(Engine,intro_date, SLE_UINT16),
SLE_VAR(Engine,age, SLE_UINT16),
SLE_VAR(Engine,reliability, SLE_UINT16),
diff --git a/industry_cmd.c b/industry_cmd.c
index 1b5dfb2b1..441d6447e 100644
--- a/industry_cmd.c
+++ b/industry_cmd.c
@@ -1945,7 +1945,7 @@ const TileTypeProcs _tile_type_industry_procs = {
GetSlopeTileh_Industry, /* get_slope_tileh_proc */
};
-static const byte _industry_desc[] = {
+static const SaveLoad _industry_desc[] = {
SLE_CONDVAR(Industry, xy, SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
SLE_CONDVAR(Industry, xy, SLE_UINT32, 6, 255),
SLE_VAR(Industry,width, SLE_UINT8),
diff --git a/misc.c b/misc.c
index 9b40ace81..1d8a709b0 100644
--- a/misc.c
+++ b/misc.c
@@ -766,7 +766,7 @@ static void Load_NAME(void)
}
}
-static const byte _game_opt_desc[] = {
+static const SaveLoad _game_opt_desc[] = {
// added a new difficulty option (town attitude) in version 4
SLE_CONDARR(GameOptions,diff, SLE_FILE_I16 | SLE_VAR_I32, 17, 0, 3),
SLE_CONDARR(GameOptions,diff, SLE_FILE_I16 | SLE_VAR_I32, 18, 4, 255),
diff --git a/misc_cmd.c b/misc_cmd.c
index 5a49a0c67..a8b357ec7 100644
--- a/misc_cmd.c
+++ b/misc_cmd.c
@@ -7,7 +7,6 @@
#include "gfx.h"
#include "window.h"
#include "gui.h"
-#include "saveload.h"
#include "economy.h"
#include "network.h"
diff --git a/network_gui.c b/network_gui.c
index 9f8ff15f2..4f8025713 100644
--- a/network_gui.c
+++ b/network_gui.c
@@ -4,7 +4,6 @@
#include "strings.h"
#include "table/sprites.h"
#include "network.h"
-#include "saveload.h"
#include "hal.h" // for file list
diff --git a/order_cmd.c b/order_cmd.c
index 188c210be..b0f8a3c67 100644
--- a/order_cmd.c
+++ b/order_cmd.c
@@ -1088,7 +1088,7 @@ void InitializeOrders(void)
_backup_orders_tile = 0;
}
-static const byte _order_desc[] = {
+static const SaveLoad _order_desc[] = {
SLE_VAR(Order,type, SLE_UINT8),
SLE_VAR(Order,flags, SLE_UINT8),
SLE_VAR(Order,station, SLE_UINT16),
diff --git a/players.c b/players.c
index fe0721846..17a4b54ee 100644
--- a/players.c
+++ b/players.c
@@ -951,7 +951,7 @@ void LoadFromHighScore(void)
}
// Save/load of players
-static const byte _player_desc[] = {
+static const SaveLoad _player_desc[] = {
SLE_VAR(Player,name_2, SLE_UINT32),
SLE_VAR(Player,name_1, SLE_STRINGID),
@@ -1000,7 +1000,7 @@ static const byte _player_desc[] = {
SLE_END()
};
-static const byte _player_economy_desc[] = {
+static const SaveLoad _player_economy_desc[] = {
// these were changed to 64-bit in savegame format 2
SLE_CONDVAR(PlayerEconomyEntry,income, SLE_INT32, 0, 1),
SLE_CONDVAR(PlayerEconomyEntry,expenses, SLE_INT32, 0, 1),
@@ -1015,7 +1015,7 @@ static const byte _player_economy_desc[] = {
SLE_END()
};
-static const byte _player_ai_desc[] = {
+static const SaveLoad _player_ai_desc[] = {
SLE_VAR(PlayerAI,state, SLE_UINT8),
SLE_VAR(PlayerAI,tick, SLE_UINT8),
SLE_CONDVAR(PlayerAI,state_counter, SLE_FILE_U16 | SLE_VAR_U32, 0, 12),
@@ -1059,7 +1059,7 @@ static const byte _player_ai_desc[] = {
SLE_END()
};
-static const byte _player_ai_build_rec_desc[] = {
+static const SaveLoad _player_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, 255),
SLE_CONDVAR(AiBuildRec,use_tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
diff --git a/saveload.c b/saveload.c
index 659a42a06..19402f6c9 100644
--- a/saveload.c
+++ b/saveload.c
@@ -1,3 +1,18 @@
+/** @file
+ * 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>
+ * @see ChunkHandler
+ * @see SaveLoad
+ */
#include "stdafx.h"
#include "ttd.h"
#include "debug.h"
@@ -14,15 +29,13 @@ enum {
SAVEGAME_LOADABLE_VERSION = (SAVEGAME_MAJOR_VERSION << 8) + SAVEGAME_MINOR_VERSION
};
-/******************************************************/
-/******************************************************/
-/******************************************************/
-
-enum NeedLengthValues { NL_NONE = 0,NL_WANTLENGTH = 1,NL_CALCLENGTH = 2};
+enum NeedLengthValues {NL_NONE = 0, NL_WANTLENGTH = 1, NL_CALCLENGTH = 2};
SaverLoader _sl;
-// fill the input buffer
+/**
+ * Fill the input buffer by reading from the file with the given reader
+ */
static void SlReadFill(void)
{
uint len = _sl.read_bytes();
@@ -33,129 +46,147 @@ static void SlReadFill(void)
_sl.offs_base += len;
}
-static uint32 SlGetOffs(void)
-{
- return _sl.offs_base - (_sl.bufe - _sl.bufp);
-}
+static inline uint32 SlGetOffs(void) {return _sl.offs_base - (_sl.bufe - _sl.bufp);}
-// flush the output buffer
+/** 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(void)
{
- // flush current buffer?
+ // 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);
}
- // setup next buffer
+ /* 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;
}
-// error handler, calls longjmp to simulate an exception.
+/** 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(const char *msg)
{
_sl.excpt_msg = msg;
longjmp(_sl.excpt, 0);
}
+/** 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 int SlReadByteInternal(void)
{
if (_sl.bufp == _sl.bufe) SlReadFill();
return *_sl.bufp++;
}
-int SlReadByte(void)
-{
- return SlReadByteInternal();
-}
+/** Wrapper for SlReadByteInternal */
+int SlReadByte(void) {return SlReadByteInternal();}
-void SlWriteByte(byte v)
+/** 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++ = v;
+ *_sl.bufp++ = b;
}
-static int SlReadUint16(void)
+/** Wrapper for SlWriteByteInternal */
+void SlWriteByte(byte b) {SlWriteByteInternal(b);}
+
+static inline int SlReadUint16(void)
{
int x = SlReadByte() << 8;
return x | SlReadByte();
}
-static uint32 SlReadUint32(void)
+static inline uint32 SlReadUint32(void)
{
uint32 x = SlReadUint16() << 16;
return x | SlReadUint16();
}
-static uint64 SlReadUint64(void)
+static inline uint64 SlReadUint64(void)
{
uint32 x = SlReadUint32();
uint32 y = SlReadUint32();
return (uint64)x << 32 | y;
}
-static void SlWriteUint16(uint16 v)
+static inline void SlWriteUint16(VarType v)
{
SlWriteByte((byte)(v >> 8));
SlWriteByte((byte)v);
}
-static void SlWriteUint32(uint32 v)
+static inline void SlWriteUint32(uint32 v)
{
SlWriteUint16((uint16)(v >> 16));
SlWriteUint16((uint16)v);
}
-static void SlWriteUint64(uint64 x)
+static inline void SlWriteUint64(uint64 x)
{
SlWriteUint32((uint32)(x >> 32));
SlWriteUint32((uint32)x);
}
-static int SlReadSimpleGamma(void)
+/**
+ * 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 bytes to the right, 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(void)
{
- int x = SlReadByte();
- if (x & 0x80)
- x = ((x&0x7F) << 8) + SlReadByte();
- return x;
+ uint i = SlReadByte();
+ if (HASBIT(i, 7)) {
+ i = (i << 8) + SlReadByte();
+ CLRBIT(i, 15);
+ }
+ return i;
}
+/**
+ * Write the header descriptor of an object or an array.
+ * If the element is bigger than 128, 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. The length is fixed to a
+ * maximum of 16384 since any higher value will have bit 15 set
+ * and the notice, would obfuscate the real value
+ * @param i Index being written
+ * @todo the maximum of 16384 can easily be reached with vehicles, so raise this artificial limit
+ */
static void SlWriteSimpleGamma(uint i)
{
assert(i < (1 << 14));
- if (i >= 0x80) {
- SlWriteByte((byte)(0x80|(i >> 8)));
+
+ if (i >= (1 << 7)) {
+ SlWriteByte((byte)((1 << 7) | (i >> 8)));
SlWriteByte((byte)i);
- } else {
+ } else
SlWriteByte(i);
- }
-}
-
-static uint SlGetGammaLength(uint i) {
- return (i>=0x80) ? 2 : 1;
-}
-
-static inline int SlReadSparseIndex(void)
-{
- return SlReadSimpleGamma();
}
-static inline void SlWriteSparseIndex(uint index)
-{
- SlWriteSimpleGamma(index);
-}
+/** Return if the length will use up 1 or two bytes in a savegame */
+static inline uint SlGetGammaLength(uint i) {return (i >= (1 << 7)) ? 2 : 1;}
-static inline int SlReadArrayLength(void)
-{
- return SlReadSimpleGamma();
-}
+static inline int SlReadSparseIndex(void) {return SlReadSimpleGamma();}
+static inline void SlWriteSparseIndex(uint index) {SlWriteSimpleGamma(index);}
-static inline void SlWriteArrayLength(uint length)
-{
- SlWriteSimpleGamma(length);
-}
+static inline int SlReadArrayLength(void) {return SlReadSimpleGamma();}
+static inline void SlWriteArrayLength(uint length) {SlWriteSimpleGamma(length);}
void SlSetArrayIndex(uint index)
{
@@ -163,50 +194,58 @@ void SlSetArrayIndex(uint index)
_sl.array_index = index;
}
+/**
+ * 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(void)
{
- int ind;
+ int index;
static uint32 next_offs;
- // Must be at end of current block.
+ /* 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. */
assert(next_offs == 0 || SlGetOffs() == next_offs);
- while(true) {
- uint len = SlReadArrayLength();
- if (len == 0) {
+ while (true) {
+ uint length = SlReadArrayLength();
+ if (length == 0) {
next_offs = 0;
return -1;
}
- _sl.obj_len = --len;
- next_offs = SlGetOffs() + len;
+ _sl.obj_len = --length;
+ next_offs = SlGetOffs() + length;
- switch(_sl.block_mode) {
- case CH_SPARSE_ARRAY: ind = SlReadSparseIndex(); break;
- case CH_ARRAY: ind = _sl.array_index++; break;
+ switch (_sl.block_mode) {
+ case CH_SPARSE_ARRAY: index = SlReadSparseIndex(); break;
+ case CH_ARRAY: index = _sl.array_index++; break;
default:
- DEBUG(misc, 0) ("SlIterateArray: error\n");
+ DEBUG(misc, 0) ("SlIterateArray: error");
return -1; // error
}
- if (len != 0)
- return ind;
+ if (length != 0) return index;
}
}
-// Sets the length of either a RIFF object or the number of items in an array.
-void SlSetLength(uint length)
+/**
+ * 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)
{
- switch(_sl.need_length) {
+ switch (_sl.need_length) {
case NL_WANTLENGTH:
_sl.need_length = NL_NONE;
- switch(_sl.block_mode) {
+ switch (_sl.block_mode) {
case CH_RIFF:
// Really simple to write a RIFF length :)
SlWriteUint32(length);
- break;
+ break;
case CH_ARRAY:
- assert(_sl.array_index >= _sl.last_array_index);
+ assert(_sl.last_array_index <= _sl.array_index);
while (++_sl.last_array_index <= _sl.array_index)
SlWriteArrayLength(1);
SlWriteArrayLength(length + 1);
@@ -216,132 +255,152 @@ void SlSetLength(uint length)
SlWriteSparseIndex(_sl.array_index);
break;
default: NOT_REACHED();
- }
- break;
+ } break;
case NL_CALCLENGTH:
_sl.obj_len += 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) {
- while(length) {
- SlWriteByte(*p++);
- length--;
- }
+ for (; length != 0; length--) {SlWriteByteInternal(*p++);}
} else {
- while(length) {
- *p++ = SlReadByteInternal();
- length--;
- }
+ for (; length != 0; length--) {*p++ = SlReadByteInternal();}
}
}
#if 0
-static void SlSkipBytes(size_t length)
+/**
+ * Read in bytes from the file/data structure but don't do
+ * anything with them
+ * NOTICE: currently unused
+ * @param length The amount of bytes that is being treated this way
+ */
+static inline void SlSkipBytes(size_t length)
{
- while (length) {
+ for (; length != 0; length--)
SlReadByte();
- length--;
- }
}
#endif
-uint SlGetFieldLength(void)
-{
- return _sl.obj_len;
-}
-
-
-static void SlSaveLoadConv(void *ptr, uint conv)
+/* Get the length of the current object */
+uint SlGetFieldLength(void) {return _sl.obj_len;}
+
+/**
+ * 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) {
- // Read a value from the struct. These ARE endian safe.
- switch((conv >> 4)&0xf) {
- case SLE_VAR_I8>>4: x = *(int8*)ptr; break;
- case SLE_VAR_U8>>4: x = *(byte*)ptr; break;
- case SLE_VAR_I16>>4: x = *(int16*)ptr; break;
- case SLE_VAR_U16>>4: x = *(uint16*)ptr; break;
- case SLE_VAR_I32>>4: x = *(int32*)ptr; break;
- case SLE_VAR_U32>>4: x = *(uint32*)ptr; break;
- case SLE_VAR_I64>>4: x = *(int64*)ptr; break;
- case SLE_VAR_U64>>4: x = *(uint64*)ptr; break;
- case SLE_VAR_NULL>>4: x = 0; break;
- default:
- NOT_REACHED();
+ if (_sl.save) { /* SAVE values */
+ /* Read a value from the struct. These ARE endian safe. */
+ switch ((conv >> 4) & 0xF) {
+ case SLE_VAR_I8 >> 4: x = *(int8*)ptr; break;
+ case SLE_VAR_U8 >> 4: x = *(byte*)ptr; break;
+ case SLE_VAR_I16 >> 4: x = *(int16*)ptr; break;
+ case SLE_VAR_U16 >> 4: x = *(uint16*)ptr; break;
+ case SLE_VAR_I32 >> 4: x = *(int32*)ptr; break;
+ case SLE_VAR_U32 >> 4: x = *(uint32*)ptr; break;
+ case SLE_VAR_I64 >> 4: x = *(int64*)ptr; break;
+ case SLE_VAR_U64 >> 4: x = *(uint64*)ptr; break;
+ case SLE_VAR_NULL >> 4: x = 0; break;
+ default: NOT_REACHED();
}
- // Write it to the file
- switch(conv & 0xF) {
- case SLE_FILE_I8: assert(x >= -128 && x <= 127); SlWriteByte(x);break;
- case SLE_FILE_U8: assert(x >= 0 && x <= 255); SlWriteByte(x);break;
+ // Write the value to the file and check if its value is in the desired range
+ switch (conv & 0xF) {
+ 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:
- assert(0);
- NOT_REACHED();
+ 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 {
+ } else { /* LOAD values */
// Read a value from the file
- switch(conv & 0xF) {
- case SLE_FILE_I8: x = (int8)SlReadByte(); break;
- case SLE_FILE_U8: x = (byte)SlReadByte(); break;
- case SLE_FILE_I16: x = (int16)SlReadUint16(); break;
+ switch (conv & 0xF) {
+ 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_I32: x = (int32)SlReadUint32(); break;
case SLE_FILE_U32: x = (uint32)SlReadUint32(); break;
- case SLE_FILE_I64: x = (int64)SlReadUint64(); 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:
- assert(0);
- NOT_REACHED();
+ default: NOT_REACHED();
}
- // Write it to the struct, these ARE endian safe.
- switch((conv >> 4)&0xf) {
- case SLE_VAR_I8>>4: *(int8*)ptr = x; break;
- case SLE_VAR_U8>>4: *(byte*)ptr = x; break;
- case SLE_VAR_I16>>4: *(int16*)ptr = x; break;
- case SLE_VAR_U16>>4: *(uint16*)ptr = x; break;
- case SLE_VAR_I32>>4: *(int32*)ptr = x; break;
- case SLE_VAR_U32>>4: *(uint32*)ptr = x; break;
- case SLE_VAR_I64>>4: *(int64*)ptr = x; break;
- case SLE_VAR_U64>>4: *(uint64*)ptr = x; break;
- case SLE_VAR_NULL: break;
- default:
- NOT_REACHED();
+ /* Write The value to the struct. These ARE endian safe. */
+ switch ((conv >> 4) & 0xF) {
+ case SLE_VAR_I8 >> 4: *(int8*)ptr = x; break;
+ case SLE_VAR_U8 >> 4: *(byte*)ptr = x; break;
+ case SLE_VAR_I16 >> 4: *(int16*)ptr = x; break;
+ case SLE_VAR_U16 >> 4: *(uint16*)ptr = x; break;
+ case SLE_VAR_I32 >> 4: *(int32*)ptr = x; break;
+ case SLE_VAR_U32 >> 4: *(uint32*)ptr = x; break;
+ case SLE_VAR_I64 >> 4: *(int64*)ptr = x; break;
+ case SLE_VAR_U64 >> 4: *(uint64*)ptr = x; break;
+ case SLE_VAR_NULL >> 4: break;
+ default: NOT_REACHED();
}
}
}
-static const byte _conv_lengths[] = {1,1,2,2,4,4,8,8,2};
-
-static uint SlCalcConvLen(uint conv, void *p)
+/* Length in bytes of the various datatypes in a savefile. These
+ * sizes are guaranteed by assert_compiles in stdafx.h */
+static const byte _conv_lengths[] = {1, 1, 2, 2, 4, 4, 8, 8, 2};
+
+/**
+ * Return the size in bytes of a certain type of normal/atomic variable
+ * @param var The variable the size is being asked of (NOTICE: unused)
+ * @param conv @VarType type of variable that is used for calculating the size
+ * @return Return the size of this type in byes
+ */
+static inline size_t SlCalcConvLen(const void *var, VarType conv) {return _conv_lengths[conv & 0xF];}
+
+/**
+ * Return the size in bytes of a reference (pointer)
+ */
+static inline size_t SlCalcRefLen(void) {return 2;}
+
+/**
+ * Return the size in bytes of a certain type of atomic array
+ * @param array The variable the size is being asked of (NOTICE: unused)
+ * @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(const void *array, uint length, VarType conv) {return _conv_lengths[conv & 0xF] * 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, uint length, VarType conv)
{
- return _conv_lengths[conv & 0xF];
-}
+ static const byte conv_mem_size[] = {1, 1, 2, 2, 4, 4, 8, 8, 0};
-static uint SlCalcArrayLen(void *array, uint length, uint conv)
-{
- return _conv_lengths[conv & 0xF] * length;
-}
-
-static const byte _conv_mem_size[9] = {1,1,2,2,4,4,8,8,0};
-void SlArray(void *array, uint length, uint conv)
-{
// Automatically calculate the length?
if (_sl.need_length != NL_NONE) {
SlSetLength(SlCalcArrayLen(array, length, conv));
@@ -350,160 +409,148 @@ void SlArray(void *array, uint length, uint conv)
return;
}
- // handle buggy stuff
+ /* 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) {
if (conv == SLE_INT16 || conv == SLE_UINT16 || conv == SLE_STRINGID) {
- length *= 2;
+ length *= 2; // int16, uint16 and StringID are 2 bytes in size
conv = SLE_INT8;
} else if (conv == SLE_INT32 || conv == SLE_UINT32) {
- length *= 4;
+ length *= 4; // int32 and uint32 are 4 bytes in size
conv = SLE_INT8;
}
}
- // Optimized cases when input equals output.
- switch(conv) {
- case SLE_INT8:
- case SLE_UINT8:SlCopyBytes(array, length);break;
- default: {
- // Default "slow" case.
+ /* If the size of elements is 1 byte, no special conversion is needed,
+ * use specialized copy-to-copy function to speed up things */
+ if (conv == SLE_INT8 || conv == SLE_UINT8) {
+ SlCopyBytes(array, length);
+ } else {
byte *a = (byte*)array;
- while (length) {
+ for (; length != 0; length --) {
SlSaveLoadConv(a, conv);
- a += _conv_mem_size[(conv >> 4)&0xf];
- length--;
- }
+ a += conv_mem_size[(conv >> 4) & 0xF]; // get size
}
}
}
-// Calculate the size of an object.
-static size_t SlCalcObjLength(void *object, const void *desc)
+/**
+ * Calculate the size of an object.
+ * @param object Object that needs its length calculated
+ * @param sld The @SaveLoad description of the object so we know how to manipulate it
+ */
+static size_t SlCalcObjLength(void *object, const SaveLoad *sld)
{
size_t length = 0;
- uint cmd,conv;
- const byte *d = (const byte*)desc;
// Need to determine the length and write a length tag.
- while (true) {
- cmd = (d[0] >> 4);
- if (cmd < 8) {
- conv = d[2];
- d += 3;
- if (cmd&4) {
- d += 2;
- // check if the field is of the right version
- if (_sl.version < d[-2] || _sl.version > d[-1]) {
- if ((cmd & 3) == 2) d++;
+ for (; sld->cmd != SL_END; sld++) {
+ if (sld->cmd < SL_WRITEBYTE) {
+ if (HASBIT(sld->cmd, 2)) {
+ // check if the field is used in the current savegame version
+ if (_sl.version < sld->version_from || _sl.version > sld->version_to)
continue;
- }
}
- switch(cmd&3) {
- // Normal variable
- case 0: length += SlCalcConvLen(conv, NULL);break;
- // Reference
- case 1: length += 2; break;
- // Array
- case 2: length += SlCalcArrayLen(NULL, *d++, conv); break;
- default:NOT_REACHED();
+ switch (sld->cmd) {
+ case SL_VAR: case SL_CONDVAR: /* Normal Variable */
+ length += SlCalcConvLen(NULL, sld->type); break;
+ case SL_REF: case SL_CONDREF: /* Reference variable */
+ length += SlCalcRefLen(); break;
+ case SL_ARR: case SL_CONDARR: /* Array */
+ length += SlCalcArrayLen(NULL, sld->length, sld->type); break;
+ default: NOT_REACHED();
}
- } else if (cmd == 8) {
- length++;
- d += 4;
- } else if (cmd == 9) {
- length += SlCalcObjLength(NULL, _sl.includes[d[2]]);
- d += 3;
- } else if (cmd == 15)
- break;
- else
- assert(0);
+ } else if (sld->cmd == SL_WRITEBYTE) {
+ length++; // a byte is logically of size 1
+ } else if (sld->cmd == SL_INCLUDE) {
+ length += SlCalcObjLength(NULL, _sl.includes[sld->version_from]);
+ } else
+ assert(sld->cmd == SL_END);
}
return length;
}
-void SlObject(void *object, const void *desc)
+/**
+ * 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)
{
- const byte *d = (const byte*)desc;
- void *ptr;
- uint cmd,conv;
-
// Automatically calculate the length?
if (_sl.need_length != NL_NONE) {
- SlSetLength(SlCalcObjLength(object, d));
+ SlSetLength(SlCalcObjLength(object, sld));
if (_sl.need_length == NL_CALCLENGTH)
return;
}
- while (true) {
- // Currently it only supports up to 4096 byte big objects
- ptr = (byte*)object + (d[0] & 0xF) + (d[1] << 4);
-
- cmd = d[0] >> 4;
-
- if (cmd < 8) {
- conv = d[2];
- d += 3;
+ for (; sld->cmd != SL_END; sld++) {
+ void *ptr = (byte*)object + sld->offset;
- if (cmd&4) {
- d += 2;
- // check if the field is of the right version
- if (_sl.version < d[-2] || _sl.version > d[-1]) {
- if ((cmd & 3) == 2) d++;
+ if (sld->cmd < SL_WRITEBYTE) {
+ /* CONDITIONAL saveload types depend on the savegame version */
+ if (HASBIT(sld->cmd, 2)) {
+ // check if the field is of the right version, if not, proceed to next one
+ if (_sl.version < sld->version_from || _sl.version > sld->version_to)
continue;
- }
}
- switch(cmd&3) {
- // Normal variable
- case 0: SlSaveLoadConv(ptr, conv); break;
- // Reference
- case 1:
- if (_sl.save) {
- SlWriteUint16(_sl.ref_to_int_proc(*(void**)ptr, conv));
- } else {
- *(void**)ptr = _sl.int_to_ref_proc(SlReadUint16(), conv);
- }
+ switch (sld->cmd) {
+ case SL_VAR: case SL_CONDVAR: /* Normal variable */
+ SlSaveLoadConv(ptr, sld->type); break;
+ case SL_REF: case SL_CONDREF: /* Reference variable, translate */
+ /// @todo XXX - another artificial limitof 65K elements of pointers?
+ if (_sl.save) { // XXX - read/write pointer as uint16? What is with higher indeces?
+ SlWriteUint16(_sl.ref_to_int_proc(*(void**)ptr, sld->type));
+ } else
+ *(void**)ptr = _sl.int_to_ref_proc(SlReadUint16(), sld->type);
break;
- // Array
- case 2: SlArray(ptr, *d++, conv); break;
- default:NOT_REACHED();
+ case SL_ARR: case SL_CONDARR: /* Array */
+ SlArray(ptr, sld->length, sld->type); break;
+ default: NOT_REACHED();
}
- // Write byte.
- } else if (cmd == 8) {
+ /* SL_WRITEBYTE translates a value of a variable to another one upon
+ * saving or loading.
+ * XXX - variable renaming abuse
+ * g_value: the value of the variable ingame is abused by sld->version_from
+ * f_value: the value of the variable in the savegame is abused by sld->version_to */
+ } else if (sld->cmd == SL_WRITEBYTE) {
if (_sl.save) {
- SlWriteByte(d[3]);
- } else {
- *(byte*)ptr = d[2];
- }
- d += 4;
-
- // Include
- } else if (cmd == 9) {
- SlObject(ptr, _sl.includes[d[2]]);
- d += 3;
- } else if (cmd == 15)
- break;
- else
- assert(0);
+ SlWriteByte(sld->version_to);
+ } else
+ *(byte*)ptr = sld->version_from;
+ /* SL_INCLUDE loads common code for a type
+ * XXX - variable renaming abuse
+ * include_index: common code to include from _desc_includes[], abused by sld->version_from */
+ } else if (sld->cmd == SL_INCLUDE) {
+ SlObject(ptr, _sl.includes[sld->version_from]);
+ } else
+ assert(sld->cmd == SL_END);
}
}
-
+/** Calculate the length of global variables
+ * @param desc The global variable that we want to know the size of
+ * @return Returns the length of the sought global object
+ */
static size_t SlCalcGlobListLength(const SaveLoadGlobVarList *desc)
{
size_t length = 0;
- while (desc->address) {
- if(_sl.version >= desc->from_version && _sl.version <= desc->to_version)
- length += SlCalcConvLen(desc->conv, NULL);
- desc++;
+ for (; desc->address != NULL; desc++) {
+ // Of course the global variable must exist in the sought savegame version
+ if (_sl.version >= desc->from_version && _sl.version <= desc->to_version)
+ length += SlCalcConvLen(NULL, desc->conv);
}
return length;
}
-// Save/Load a list of global variables
+/**
+ * Save or Load (a list of) global variables
+ * @param desc The global variable that is being loaded or saved
+ */
void SlGlobList(const SaveLoadGlobVarList *desc)
{
if (_sl.need_length != NL_NONE) {
@@ -511,17 +558,18 @@ void SlGlobList(const SaveLoadGlobVarList *desc)
if (_sl.need_length == NL_CALCLENGTH)
return;
}
- while (true) {
- void *ptr = desc->address;
- if (ptr == NULL)
- break;
- if(_sl.version >= desc->from_version && _sl.version <= desc->to_version)
- SlSaveLoadConv(ptr, desc->conv);
- desc++;
+
+ for (; desc->address != NULL; desc++) {
+ if (_sl.version >= desc->from_version && _sl.version <= desc->to_version)
+ SlSaveLoadConv(desc->address, desc->conv);
}
}
-
+/**
+ * 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)
{
uint32 offs;
@@ -545,6 +593,10 @@ void SlAutolength(AutolengthProc *proc, void *arg)
assert(offs == SlGetOffs());
}
+/**
+ * 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();
@@ -554,16 +606,14 @@ static void SlLoadChunk(const ChunkHandler *ch)
_sl.block_mode = m;
_sl.obj_len = 0;
- switch(m) {
+ switch (m) {
case CH_ARRAY:
_sl.array_index = 0;
ch->load_proc();
break;
-
case CH_SPARSE_ARRAY:
ch->load_proc();
break;
-
case CH_RIFF:
// Read length
len = SlReadByte() << 16;
@@ -573,30 +623,25 @@ static void SlLoadChunk(const ChunkHandler *ch)
ch->load_proc();
assert(SlGetOffs() == endoffs);
break;
- default:
- assert(0);
+ default: NOT_REACHED();
}
}
+/* 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(void) {SlAutolength(SlStubSaveProc2, NULL);}
-static void SlStubSaveProc2(void *arg)
-{
- _tmp_proc_1();
-}
-
-static void SlStubSaveProc(void)
-{
- 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;
+ ChunkSaveLoadProc *proc = ch->save_proc;
SlWriteUint32(ch->id);
- proc = ch->save_proc;
if (ch->flags & CH_AUTO_LENGTH) {
// Need to calculate the length. Solve that by calling SlAutoLength in the save_proc.
_tmp_proc_1 = proc;
@@ -604,7 +649,7 @@ static void SlSaveChunk(const ChunkHandler *ch)
}
_sl.block_mode = ch->flags & CH_TYPE_MASK;
- switch(ch->flags & CH_TYPE_MASK) {
+ switch (ch->flags & CH_TYPE_MASK) {
case CH_RIFF:
_sl.need_length = NL_WANTLENGTH;
proc();
@@ -620,20 +665,20 @@ static void SlSaveChunk(const ChunkHandler *ch)
proc();
SlWriteArrayLength(0); // Terminate arrays
break;
- default:
- NOT_REACHED();
+ default: NOT_REACHED();
}
}
+/** Save all chunks */
static void SlSaveChunks(void)
{
const ChunkHandler *ch;
- const ChunkHandler * const * chsc;
+ const ChunkHandler* const *chsc;
uint p;
- for(p=0; p!=CH_NUM_PRI_LEVELS; p++) {
- for(chsc=_sl.chs;(ch=*chsc++) != NULL;) {
- while(true) {
+ 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)
@@ -647,11 +692,16 @@ static void SlSaveChunks(void)
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;) {
+ const ChunkHandler *const *chsc;
+ for (chsc = _sl.chs; (ch=*chsc++) != NULL;) {
while(true) {
if (ch->id == id)
return ch;
@@ -663,18 +713,15 @@ static const ChunkHandler *SlFindChunkHandler(uint32 id)
return NULL;
}
+/** Load all chunks */
static void SlLoadChunks(void)
{
uint32 id;
const ChunkHandler *ch;
- while(true) {
- id = SlReadUint32();
- if (id == 0)
- return;
-#if 0
- printf("Loading chunk %c%c%c%c\n", id >> 24, id>>16, id>>8,id);
-#endif
+ for (id = SlReadUint32(); id != 0; id = SlReadUint32()) {
+ DEBUG(misc, 1) ("Loading chunk %c%c%c%c", id >> 24, id>>16, id>>8, id);
+
ch = SlFindChunkHandler(id);
if (ch == NULL) SlError("found unknown tag in savegame (sync error)");
SlLoadChunk(ch);
@@ -745,9 +792,9 @@ static void UninitLZO(void)
free(_sl.buf);
}
-//*******************************************
-//******** START OF NOCOMP CODE *************
-//*******************************************
+//*********************************************
+//******** START OF NOCOMP CODE (uncompressed)*
+//*********************************************
static uint ReadNoComp(void)
{
return fread(_sl.buf, 1, LZO_SIZE, _sl.fh);
@@ -863,7 +910,7 @@ static void UninitWriteZlib(void)
free(_sl.buf);
}
-#endif //WITH_ZLIB
+#endif /* WITH_ZLIB */
//*******************************************
//************* END OF CODE *****************
@@ -902,126 +949,146 @@ static const ChunkHandler * const _chunk_handlers[] = {
};
// used to include a vehicle desc in another desc.
-extern const byte _common_veh_desc[];
-static const byte * const _desc_includes[] = {
+extern const SaveLoad _common_veh_desc[];
+static const SaveLoad* const _desc_includes[] = {
_common_veh_desc
};
-/* We can't save pointers to a savegame, so this functions get's
- 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
- indexes have + 1, so vehicle 0 is saved as index 1. */
-static uint ReferenceToInt(void *v, uint t)
+/**
+ * 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 (v == NULL)
- return 0;
+ if (obj == NULL) return 0;
- switch (t) {
+ switch (rt) {
case REF_VEHICLE_OLD: // Old vehicles we save as new onces
- case REF_VEHICLE: return ((Vehicle *)v)->index + 1;
- case REF_STATION: return ((Station *)v)->index + 1;
- case REF_TOWN: return ((Town *)v)->index + 1;
- case REF_ORDER: return ((Order *)v)->index + 1;
- case REF_ROADSTOPS: return ((RoadStop *)v)->index + 1;
-
- default:
- NOT_REACHED();
+ case REF_VEHICLE: return ((Vehicle *)obj)->index + 1;
+ case REF_STATION: return ((Station *)obj)->index + 1;
+ case REF_TOWN: return ((Town *)obj)->index + 1;
+ case REF_ORDER: return ((Order *)obj)->index + 1;
+ case REF_ROADSTOPS: return ((RoadStop *)obj)->index + 1;
+ default: NOT_REACHED();
}
- return 0;
+ return 0; // avoid compiler warning
}
-static void *IntToReference(uint r, uint t)
+/**
+ * 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)
{
- /* From version 4.3 REF_VEHICLE_OLD is saved as REF_VEHICLE, and should be loaded
- like that */
- if (t == REF_VEHICLE_OLD &&
- _sl.full_version >= 0x404)
- t = REF_VEHICLE;
+ /* After version 4.3 REF_VEHICLE_OLD is saved as REF_VEHICLE,
+ * and should be loaded like that */
+ if (rt == REF_VEHICLE_OLD && _sl.full_version >= ((4 << 8) | 4))
+ rt = REF_VEHICLE;
- if (t != REF_VEHICLE_OLD && r == 0)
+ /* No need to look up NULL pointers, just return immediately */
+ if (rt != REF_VEHICLE_OLD && index == 0)
return NULL;
- switch (t) {
+ index--; // correct for the NULL index
+
+ switch (rt) {
case REF_ORDER: {
- if (!AddBlockIfNeeded(&_order_pool, r - 1))
+ if (!AddBlockIfNeeded(&_order_pool, index))
error("Orders: failed loading savegame: too many orders");
- return GetOrder(r - 1);
+ return GetOrder(index);
}
case REF_VEHICLE: {
- if (!AddBlockIfNeeded(&_vehicle_pool, r - 1))
+ if (!AddBlockIfNeeded(&_vehicle_pool, index))
error("Vehicles: failed loading savegame: too many vehicles");
- return GetVehicle(r - 1);
+ return GetVehicle(index);
}
case REF_STATION: {
- if (!AddBlockIfNeeded(&_station_pool, r - 1))
+ if (!AddBlockIfNeeded(&_station_pool, index))
error("Stations: failed loading savegame: too many stations");
- return GetStation(r - 1);
+ return GetStation(index);
}
case REF_TOWN: {
- if (!AddBlockIfNeeded(&_town_pool, r - 1))
+ if (!AddBlockIfNeeded(&_town_pool, index))
error("Towns: failed loading savegame: too many towns");
- return GetTown(r - 1);
+ return GetTown(index);
}
case REF_ROADSTOPS: {
- if (!AddBlockIfNeeded(&_roadstop_pool, r - 1))
+ if (!AddBlockIfNeeded(&_roadstop_pool, index))
error("RoadStops: failed loading savegame: too many RoadStops");
- return GetRoadStop(r - 1);
+ return GetRoadStop(index);
}
case REF_VEHICLE_OLD: {
- /* Old vehicles were saved differently: invalid vehicle was 0xFFFF,
- and the index was not - 1.. correct for this */
- if (r == INVALID_VEHICLE)
+ /* 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 (!AddBlockIfNeeded(&_vehicle_pool, r))
+ if (!AddBlockIfNeeded(&_vehicle_pool, index))
error("Vehicles: failed loading savegame: too many vehicles");
- return GetVehicle(r);
+ return GetVehicle(index);
}
- default:
- NOT_REACHED();
+ default: NOT_REACHED();
}
return NULL;
}
+/** The format for a reader/writer type of a savegame */
typedef struct {
- const char *name;
- uint32 tag;
+ 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)(void);
- ReaderProc *reader;
- void (*uninit_read)(void);
-
- bool (*init_write)(void);
- WriterProc *writer;
- void (*uninit_write)(void);
+ bool (*init_read)(void); /// function executed upon initalization of the loader
+ ReaderProc *reader; /// function that loads the data from the file
+ void (*uninit_read)(void); /// function executed when reading is finished
+ bool (*init_write)(void); /// function executed upon intialization of the saver
+ WriterProc *writer; /// function that saves the data to the file
+ void (*uninit_write)(void); /// function executed when writing is done
} SaveLoadFormat;
static const SaveLoadFormat _saveload_formats[] = {
- {"lzo", TO_BE32X('OTTD'), InitLZO,ReadLZO, UninitLZO, InitLZO, WriteLZO, UninitLZO},
- {"none", TO_BE32X('OTTN'), InitNoComp,ReadNoComp, UninitNoComp, InitNoComp, WriteNoComp, UninitNoComp},
+ {"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},
+ {"zlib", TO_BE32X('OTTZ'), InitReadZlib, ReadZlib, UninitReadZlib, InitWriteZlib, WriteZlib, UninitWriteZlib},
#else
- {"zlib", TO_BE32X('OTTZ'), NULL,NULL,NULL,NULL,NULL,NULL}
+ {"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
+ * @return Pointer to @SaveLoadFormat struct giving all characteristics of this type of savegame
+ */
static const SaveLoadFormat *GetSavegameFormat(const char *s)
{
- const SaveLoadFormat *def;
+ const SaveLoadFormat *def = endof(_saveload_formats) - 1;
int i;
- // find default savegame format
- def = endof(_saveload_formats) - 1;
+ // find default savegame format, the highest one with which files can be written
while (!def->init_write) def--;
if (_savegame_format[0]) {
- for(i = 0; i!=lengthof(_saveload_formats); i++)
+ for (i = 0; i != lengthof(_saveload_formats); i++)
if (_saveload_formats[i].init_write && !strcmp(s, _saveload_formats[i].name))
return _saveload_formats + i;
@@ -1036,24 +1103,41 @@ extern bool AfterLoadGame(uint version);
extern void BeforeSaveGame(void);
extern bool LoadOldSaveGame(const char *file);
-// Save or Load files SL_LOAD, SL_SAVE, SL_OLD_LOAD
+/** Small helper function to close the to be loaded savegame an signal error */
+static inline int AbortSaveLoad(void)
+{
+ if (_sl.fh != NULL) fclose(_sl.fh);
+
+ _sl.fh = NULL;
+ return SL_ERROR;
+}
+
+/**
+ * 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)
+ */
int SaveOrLoad(const char *filename, int mode)
{
uint32 hdr[2];
const SaveLoadFormat *fmt;
uint version;
- // old style load
+ /* Load a TTDLX or TTDPatch game */
if (mode == SL_OLD_LOAD) {
- InitializeGame(8, 8);
+ InitializeGame(8, 8); // set a mapsize of 256x256 for TTDPatch games or it might get confused
if (!LoadOldSaveGame(filename)) return SL_REINIT;
AfterLoadGame(0);
return SL_OK;
}
- _sl.fh = fopen(filename, mode?"wb":"rb");
- if (_sl.fh == NULL)
+ _sl.fh = fopen(filename, (mode == SL_SAVE) ? "wb" : "rb");
+ if (_sl.fh == NULL) {
+ DEBUG(misc, 0) ("Cannot open savegame for saving/loading.");
return SL_ERROR;
+ }
_sl.bufe = _sl.bufp = NULL;
_sl.offs_base = 0;
@@ -1063,16 +1147,14 @@ int SaveOrLoad(const char *filename, int mode)
_sl.includes = _desc_includes;
_sl.chs = _chunk_handlers;
- // setup setjmp error handler
+ /* Setup setjmp error handler, if it fails don't even bother loading the game */
if (setjmp(_sl.excpt)) {
- // close file handle.
- fclose(_sl.fh); _sl.fh = NULL;
+ AbortSaveLoad();
// deinitialize compressor.
_sl.excpt_uninit();
- // a saver/loader exception!!
- // reinitialize all variables to prevent crash!
+ /* A saver/loader exception!! reinitialize all variables to prevent crash! */
if (mode == SL_LOAD) {
ShowInfoF("Load game failed: %s.", _sl.excpt_msg);
return SL_REINIT;
@@ -1082,20 +1164,23 @@ int SaveOrLoad(const char *filename, int mode)
}
}
- // we first initialize here to avoid: "warning: variable `version' might
- // be clobbered by `longjmp' or `vfork'"
+ /* We first initialize here to avoid: "warning: variable `version' might
+ * be clobbered by `longjmp' or `vfork'" */
version = 0;
- if (mode != SL_LOAD) {
+ if (mode == SL_SAVE) { /* SAVE game */
fmt = GetSavegameFormat(_savegame_format);
_sl.write_bytes = fmt->writer;
_sl.excpt_uninit = fmt->uninit_write;
- if (!fmt->init_write()) goto init_err;
+ if (!fmt->init_write()) {
+ DEBUG(misc, 0) ("Initializing writer %s failed.", fmt->name);
+ return AbortSaveLoad();
+ }
hdr[0] = fmt->tag;
hdr[1] = TO_BE32((SAVEGAME_MAJOR_VERSION << 16) + (SAVEGAME_MINOR_VERSION << 8));
- if (fwrite(hdr, sizeof(hdr), 1, _sl.fh) != 1) SlError("file write failed");
+ if (fwrite(hdr, sizeof(hdr), 1, _sl.fh) != 1) SlError("Writing savegame header failed");
_sl.version = SAVEGAME_MAJOR_VERSION;
@@ -1104,33 +1189,33 @@ int SaveOrLoad(const char *filename, int mode)
SlWriteFill(); // flush the save buffer
fmt->uninit_write();
- } else {
+ } else { /* LOAD game */
if (fread(hdr, sizeof(hdr), 1, _sl.fh) != 1) {
-read_err:
- printf("Savegame is obsolete or invalid format.\n");
-init_err:
- fclose(_sl.fh);
- return SL_ERROR;
+ DEBUG(misc, 0) ("Cannot read Savegame header, aborting.");
+ return AbortSaveLoad();
}
// see if we have any loader for this type.
- for(fmt = _saveload_formats; ; fmt++) {
+ for (fmt = _saveload_formats; ; fmt++) {
+ /* No loader found, treat as version 0 and use LZO format */
if (fmt == endof(_saveload_formats)) {
- printf("Unknown savegame type, trying to load it as the buggy format.\n");
+ DEBUG(misc, 0) ("Unknown savegame type, trying to load it as the buggy format.");
rewind(_sl.fh);
- _sl.version = 0;
+ _sl.version = version = 0;
_sl.full_version = 0;
- version = 0;
- fmt = _saveload_formats + 0; // lzo
+ fmt = _saveload_formats + 0; // LZO
break;
}
+
if (fmt->tag == hdr[0]) {
// check version number
version = TO_BE32(hdr[1]) >> 8;
/* Is the version higher than the current? */
- if (version > SAVEGAME_LOADABLE_VERSION)
- goto read_err;
+ if (version > SAVEGAME_LOADABLE_VERSION) {
+ DEBUG(misc, 0) ("Savegame version invalid.");
+ return AbortSaveLoad();
+ }
_sl.version = (version >> 8);
_sl.full_version = version;
@@ -1144,14 +1229,17 @@ init_err:
// loader for this savegame type is not implemented?
if (fmt->init_read == NULL) {
ShowInfoF("Loader for '%s' is not available.", fmt->name);
- fclose(_sl.fh);
- return SL_ERROR;
+ return AbortSaveLoad();
}
- if (!fmt->init_read()) goto init_err;
- // Clear everything
- /* Set the current map to 256x256, in case of an old map.
- * Else MAPS will read the right information */
+ if (!fmt->init_read()) {
+ DEBUG(misc, 0) ("Initializing loader %s failed.", fmt->name);
+ return AbortSaveLoad();
+ }
+
+ /* XXX - ??? Set the current map to 256x256, in case of an old map.
+ * Else MAPS will read the wrong information. This should initialize
+ * to savegame mapsize no?? */
InitializeGame(8, 8);
SlLoadChunks();
@@ -1160,31 +1248,30 @@ init_err:
fclose(_sl.fh);
- if (mode == SL_LOAD) {
- if (!AfterLoadGame(version))
+ /* After loading fix up savegame for any internal changes that
+ * might've occured since then. If it fails, load back the old game */
+ if (mode == SL_LOAD && !AfterLoadGame(version))
return SL_REINIT;
- }
return SL_OK;
}
-#ifdef WIN32
-bool EmergencySave(void)
-{
- SaveOrLoad("crash.sav", SL_SAVE);
- return true;
-}
-#endif
-
+/** Do a save when exiting the game (patch option) _patches.autosave_on_exit */
void DoExitSave(void)
{
char buf[200];
- sprintf(buf, "%s%sexit.sav", _path.autosave_dir, PATHSEP);
+ snprintf(buf, sizeof(buf), "%s%sexit.sav", _path.autosave_dir, PATHSEP);
SaveOrLoad(buf, SL_SAVE);
}
-// not used right now, but could be used if extensions of savegames are garbled
-/*int GetSavegameType(char *file)
+#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;
@@ -1208,4 +1295,5 @@ void DoExitSave(void)
fclose(f);
return mode;
-}*/
+}
+#endif
diff --git a/saveload.h b/saveload.h
index ebe3ecc25..d6282b65f 100644
--- a/saveload.h
+++ b/saveload.h
@@ -13,7 +13,6 @@ typedef struct SaveLoadGlobVarList {
byte to_version;
} SaveLoadGlobVarList;
-
typedef struct {
uint32 id;
ChunkSaveLoadProc *save_proc;
@@ -28,62 +27,63 @@ typedef struct {
typedef void WriterProc(uint len);
typedef uint ReaderProc(void);
-typedef uint ReferenceToIntProc(void *v, uint t);
-typedef void *IntToReferenceProc(uint r, uint t);
+typedef enum SLRefType {
+ REF_ORDER = 0,
+ REF_VEHICLE = 1,
+ REF_STATION = 2,
+ REF_TOWN = 3,
+ REF_VEHICLE_OLD = 4,
+ REF_ROADSTOPS = 5
+} SLRefType;
+
+typedef uint ReferenceToIntProc(const void *obj, SLRefType rt);
+typedef void *IntToReferenceProc(uint index, SLRefType rt);
-typedef struct {
- bool save;
- byte need_length;
- byte block_mode;
- bool error;
- byte version;
- uint16 full_version;
+typedef struct SaveLoad SaveLoad;
- int obj_len;
- int array_index, last_array_index;
+/** The saveload struct, containing reader-writer functions, bufffer, version, etc. */
+typedef 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
+ byte version; /// the major savegame version identifier
+ uint16 full_version; /// the full version of the savegame
- uint32 offs_base;
+ int 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
- WriterProc *write_bytes;
- ReaderProc *read_bytes;
+ uint32 offs_base; /// the offset in number of bytes since we started writing data (eg uncompressed savegame size)
- ReferenceToIntProc *ref_to_int_proc;
- IntToReferenceProc *int_to_ref_proc;
+ WriterProc *write_bytes; /// savegame writer function
+ ReaderProc *read_bytes; /// savegame loader function
- const ChunkHandler * const * chs;
- const byte * const *includes;
+ ReferenceToIntProc *ref_to_int_proc; /// function to convert pointers to numbers when saving a game
+ IntToReferenceProc *int_to_ref_proc; /// function to convert numbers to pointers when loading a game
- byte *bufp, *bufe;
+ const ChunkHandler* const *chs; /// the chunk of data that is being processed atm (vehicles, signs, etc.)
+ const SaveLoad* const *includes; /// the internal layouf of the given chunk
- int tmp;
+ /** 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 and size to read/write, initialized by init
- uint bufsize;
- FILE *fh;
+ byte *buf; /// pointer to temporary memory to read/write, initialized by SaveLoadFormat->initread/write
+ uint bufsize; /// the size of the temporary memory *buf
+ FILE *fh; /// the file from which is read or written to
- void (*excpt_uninit)(void);
- const char *excpt_msg;
- jmp_buf excpt; // used to jump to "exception handler"
+ void (*excpt_uninit)(void); /// the function to execute on any encountered error
+ const char *excpt_msg; /// the error message
+ jmp_buf excpt; /// @todo used to jump to "exception handler"; really ugly
} SaverLoader;
extern SaverLoader _sl;
enum {
- REF_ORDER = 0,
- REF_VEHICLE = 1,
- REF_STATION = 2,
- REF_TOWN = 3,
- REF_VEHICLE_OLD = 4,
- REF_ROADSTOPS = 5
-};
-
-
-enum {
INC_VEHICLE_COMMON = 0,
};
-
enum {
CH_RIFF = 0,
CH_ARRAY = 1,
@@ -99,9 +99,9 @@ enum {
CH_NUM_PRI_LEVELS = 4,
};
-enum {
- SLE_FILE_I8 = 0,
- SLE_FILE_U8 = 1,
+typedef enum VarTypes {
+ SLE_FILE_I8 = 0,
+ SLE_FILE_U8 = 1,
SLE_FILE_I16 = 2,
SLE_FILE_U16 = 3,
SLE_FILE_I32 = 4,
@@ -113,63 +113,88 @@ enum {
// SLE_FILE_IVAR = 8,
// SLE_FILE_UVAR = 9,
- SLE_VAR_I8 = 0 << 4,
- SLE_VAR_U8 = 1 << 4,
- SLE_VAR_I16 = 2 << 4,
- SLE_VAR_U16 = 3 << 4,
- SLE_VAR_I32 = 4 << 4,
- SLE_VAR_U32 = 5 << 4,
- SLE_VAR_I64 = 6 << 4,
- SLE_VAR_U64 = 7 << 4,
+ SLE_VAR_I8 = 0 << 4,
+ SLE_VAR_U8 = 1 << 4,
+ SLE_VAR_I16 = 2 << 4,
+ SLE_VAR_U16 = 3 << 4,
+ SLE_VAR_I32 = 4 << 4,
+ SLE_VAR_U32 = 5 << 4,
+ SLE_VAR_I64 = 6 << 4,
+ SLE_VAR_U64 = 7 << 4,
SLE_VAR_NULL = 8 << 4, // useful to write zeros in savegame.
- SLE_VAR_INT = SLE_VAR_I32,
+ SLE_VAR_INT = SLE_VAR_I32,
SLE_VAR_UINT = SLE_VAR_U32,
- 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_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_INT32 = SLE_FILE_I32 | SLE_VAR_I32,
SLE_UINT32 = SLE_FILE_U32 | SLE_VAR_U32,
- SLE_INT64 = SLE_FILE_I64 | SLE_VAR_I64,
+ SLE_INT64 = SLE_FILE_I64 | SLE_VAR_I64,
SLE_UINT64 = SLE_FILE_U64 | SLE_VAR_U64,
SLE_STRINGID = SLE_FILE_STRINGID | SLE_VAR_U16,
+} VarType;
+
+enum SaveLoadTypes {
+ SL_VAR = 0,
+ SL_REF = 1,
+ SL_ARR = 2,
+ SL_CONDVAR = 0 | (1 << 2), // 4
+ SL_CONDREF = 1 | (1 << 2), // 5
+ SL_CONDARR = 2 | (1 << 2), // 6
+ // non-normal save-load types
+ SL_WRITEBYTE = 8,
+ SL_INCLUDE = 9,
+ SL_END = 15
};
-#define SLE_VAR(t,i,c) 0 | (offsetof(t,i) & 0xF), offsetof(t,i) >> 4, c
-#define SLE_REF(t,i,c) 0x10 | (offsetof(t,i) & 0xF), offsetof(t,i) >> 4, c
-#define SLE_ARR(t,i,c,l) 0x20 | (offsetof(t,i) & 0xF), offsetof(t,i) >> 4, c, l
-#define SLE_CONDVAR(t,i,c,from,to) 0x40 | (offsetof(t,i) & 0xF), offsetof(t,i) >> 4, c, from, to
-#define SLE_CONDREF(t,i,c,from,to) 0x50 | (offsetof(t,i) & 0xF), offsetof(t,i) >> 4, c, from, to
-#define SLE_CONDARR(t,i,c,l,from,to) 0x60 | (offsetof(t,i) & 0xF), offsetof(t,i) >> 4, c, from,to, l
-#define SLE_WRITEBYTE(t,i,b,c) 0x80 | (offsetof(t,i) & 0xF), offsetof(t,i) >> 4, b, c
-#define SLE_INCLUDE(t,i,c) 0x90 | (offsetof(t,i) & 0xF), offsetof(t,i) >> 4, c
-
-
-#define SLE_VARX(t,c) 0x00 | ((t) & 0xF), (t) >> 4, c
-#define SLE_REFX(t,c) 0x10 | ((t) & 0xF), (t) >> 4, c
-#define SLE_CONDVARX(t,c,from,to) 0x40 | ((t) & 0xF), (t) >> 4, c, from, to
-#define SLE_CONDREFX(t,c,from,to) 0x50 | ((t) & 0xF), (t) >> 4, c, from, to
-#define SLE_WRITEBYTEX(t,b) 0x80 | ((t) & 0xF), (t) >> 4, b
-#define SLE_INCLUDEX(t,c) 0x90 | ((t) & 0xF), (t) >> 4, c
-
-#define SLE_END() 0xF0
+/** SaveLoad type struct. Do NOT use this directly but use the SLE_ macros defined just below! */
+struct SaveLoad {
+ byte cmd; /// the action to take with the saved/loaded type, All types need different action
+ VarType type; /// type of the variable to be saved, int
+ uint16 offset; /// offset of this variable in the struct (max offset is 65536)
+ 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
+};
+/* Simple variables, references (pointers) and arrays */
+#define SLE_VAR(base, variable, type) {SL_VAR, type, offsetof(base, variable), 0, 0, 0}
+#define SLE_REF(base, variable, type) {SL_REF, type, offsetof(base, variable), 0, 0, 0}
+#define SLE_ARR(base, variable, type, length) {SL_ARR, type, offsetof(base, variable), length, 0, 0}
+/* Conditional variables, references (pointers) and arrays that are only valid for certain savegame versions */
+#define SLE_CONDVAR(base, variable, type, from, to) {SL_CONDVAR, type, offsetof(base, variable), 0, from, to}
+#define SLE_CONDREF(base, variable, type, from, to) {SL_CONDREF, type, offsetof(base, variable), 0, from, to}
+#define SLE_CONDARR(base, variable, type, length, from, to) {SL_CONDARR, type, offsetof(base, variable), length, from, to}
+/* Translate values ingame to different values in the savegame and vv */
+#define SLE_WRITEBYTE(base, variable, game_value, file_value) {SL_WRITEBYTE, 0, offsetof(base, variable), 0, game_value, file_value}
+/* Load common code and put it into each struct (currently only for vehicles */
+#define SLE_INCLUDE(base, variable, include_index) {SL_INCLUDE, 0, offsetof(base, variable), 0, include_index, 0}
+
+/* The same as the ones at the top, only the offset is given directly; used for unions */
+#define SLE_VARX(offset, type) {SL_VAR, type, offset, 0, 0, 0}
+#define SLE_REFX(offset, type) {SL_REF, type, offset, 0, 0, 0}
+#define SLE_CONDVARX(offset, type, from, to) {SL_CONDVAR, type, offset, 0, from, to}
+#define SLE_CONDREFX(offset, type, from, to) {SL_CONDREF, type, offset, 0, from, to}
+#define SLE_WRITEBYTEX(offset, something) {SL_WRITEBYTE, 0, offset, 0, something, 0}
+#define SLE_INCLUDEX(offset, type) {SL_INCLUDE, type, offset, 0, 0, 0}
+
+/* End marker */
+#define SLE_END() {SL_END, 0, 0, 0, 0, 0}
void SlSetArrayIndex(uint index);
int SlIterateArray(void);
void SlArray(void *array, uint length, uint conv);
-void SlObject(void *object, const void *desc);
+void SlObject(void *object, const SaveLoad *desc);
void SlAutolength(AutolengthProc *proc, void *arg);
uint SlGetFieldLength(void);
int SlReadByte(void);
-void SlSetLength(uint length);
-void SlWriteByte(byte v);
+void SlSetLength(size_t length);
+void SlWriteByte(byte b);
void SlGlobList(const SaveLoadGlobVarList *desc);
-//int GetSavegameType(char *file);
-
#endif /* SAVELOAD_H */
diff --git a/sdl.c b/sdl.c
index 48ff2d391..bb73f4482 100644
--- a/sdl.c
+++ b/sdl.c
@@ -439,7 +439,7 @@ static uint32 ConvertSdlKeyIntoMy(SDL_keysym *sym)
return (key << 16) + sym->unicode;
}
-void DoExitSave(void);
+extern void DoExitSave(void);
static int PollEvent(void)
{
diff --git a/signs.c b/signs.c
index 9974dd91c..d39382ec2 100644
--- a/signs.c
+++ b/signs.c
@@ -216,7 +216,7 @@ void InitializeSigns(void)
AddBlockToPool(&_sign_pool);
}
-static const byte _sign_desc[] = {
+static const SaveLoad _sign_desc[] = {
SLE_VAR(SignStruct,str, SLE_UINT16),
SLE_CONDVAR(SignStruct,x, SLE_FILE_I16 | SLE_VAR_I32, 0, 4),
SLE_CONDVAR(SignStruct,y, SLE_FILE_I16 | SLE_VAR_I32, 0, 4),
diff --git a/station_cmd.c b/station_cmd.c
index 2d914fbf3..bd276ed94 100644
--- a/station_cmd.c
+++ b/station_cmd.c
@@ -2966,7 +2966,7 @@ const TileTypeProcs _tile_type_station_procs = {
GetSlopeTileh_Station, /* get_slope_tileh_proc */
};
-static const byte _roadstop_desc[] = {
+static const SaveLoad _roadstop_desc[] = {
SLE_VAR(RoadStop,xy, SLE_UINT32),
SLE_VAR(RoadStop,used, SLE_UINT8),
SLE_VAR(RoadStop,status, SLE_UINT8),
@@ -2983,7 +2983,7 @@ static const byte _roadstop_desc[] = {
SLE_END()
};
-static const byte _station_desc[] = {
+static const SaveLoad _station_desc[] = {
SLE_CONDVAR(Station, xy, SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
SLE_CONDVAR(Station, xy, SLE_UINT32, 6, 255),
SLE_CONDVAR(Station, bus_tile_obsolete, SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
@@ -3036,7 +3036,7 @@ static const byte _station_desc[] = {
SLE_END()
};
-static const byte _goods_desc[] = {
+static const SaveLoad _goods_desc[] = {
SLE_VAR(GoodsEntry,waiting_acceptance,SLE_UINT16),
SLE_VAR(GoodsEntry,days_since_pickup, SLE_UINT8),
SLE_VAR(GoodsEntry,rating, SLE_UINT8),
diff --git a/stdafx.h b/stdafx.h
index 3d35aed91..c7c1aa83d 100644
--- a/stdafx.h
+++ b/stdafx.h
@@ -72,7 +72,7 @@
//#include <alloca.h>
//#include <malloc.h>
# define __int64 long long
-# define NOT_REACHED()
+# define NOT_REACHED() assert(0)
# define GCC_PACK __attribute__((packed))
# if (__GNUC__ == 2)
@@ -85,7 +85,7 @@
# define NORETURN
# define FORCEINLINE inline
# define CDECL
-# define NOT_REACHED()
+# define NOT_REACHED() assert(0)
# define GCC_PACK
# undef TTD_ALIGNMENT_4
# undef TTD_ALIGNMENT_2
@@ -104,7 +104,11 @@
# define FORCEINLINE __forceinline
# define inline _inline
# define CDECL _cdecl
-# define NOT_REACHED() _assume(0)
+# if defined(_DEBUG)
+# define NOT_REACHED() assert(0)
+# else
+# define NOT_REACHED() _assume(0)
+# endif
int CDECL snprintf(char *str, size_t size, const char *format, ...);
int CDECL vsnprintf(char *str, size_t size, const char *format, va_list ap);
# undef TTD_ALIGNMENT_4
diff --git a/texteff.c b/texteff.c
index fdf6c5640..b418b55de 100644
--- a/texteff.c
+++ b/texteff.c
@@ -368,10 +368,9 @@ void InitializeAnimatedTiles(void)
static void SaveLoad_ANIT(void)
{
- if (_sl.version < 6)
- SlArray(_animated_tile_list, lengthof(_animated_tile_list),
- SLE_FILE_U16 | SLE_VAR_U32);
- else
+ if (_sl.version < 6) {
+ SlArray(_animated_tile_list, lengthof(_animated_tile_list), SLE_FILE_U16 | SLE_VAR_U32);
+ } else
SlArray(_animated_tile_list, lengthof(_animated_tile_list), SLE_UINT32);
}
diff --git a/town_cmd.c b/town_cmd.c
index 498c2154b..b9d960926 100644
--- a/town_cmd.c
+++ b/town_cmd.c
@@ -1973,7 +1973,7 @@ const TileTypeProcs _tile_type_town_procs = {
// Save and load of towns.
-static const byte _town_desc[] = {
+static const SaveLoad _town_desc[] = {
SLE_CONDVAR(Town, xy, SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
SLE_CONDVAR(Town, xy, SLE_UINT32, 6, 255),
diff --git a/ttd.c b/ttd.c
index 57792e8c5..cddd6746b 100644
--- a/ttd.c
+++ b/ttd.c
@@ -27,7 +27,6 @@
#include "fileio.h"
#include "hal.h"
#include "airport.h"
-#include "saveload.h"
#include "ai.h"
#include "console.h"
#include "screenshot.h"
diff --git a/vehicle.c b/vehicle.c
index c0649f069..fba10a87b 100644
--- a/vehicle.c
+++ b/vehicle.c
@@ -1815,7 +1815,7 @@ restart:
// Save and load of vehicles
-const byte _common_veh_desc[] = {
+const SaveLoad _common_veh_desc[] = {
SLE_VAR(Vehicle,subtype, SLE_UINT8),
SLE_REF(Vehicle,next, REF_VEHICLE_OLD),
@@ -1912,7 +1912,7 @@ const byte _common_veh_desc[] = {
};
-static const byte _train_desc[] = {
+static const SaveLoad _train_desc[] = {
SLE_WRITEBYTE(Vehicle,type,VEH_Train, 0), // Train type. VEH_Train in mem, 0 in file.
SLE_INCLUDEX(0, INC_VEHICLE_COMMON),
SLE_VARX(offsetof(Vehicle,u)+offsetof(VehicleRail,crash_anim_pos), SLE_UINT16),
@@ -1929,7 +1929,7 @@ static const byte _train_desc[] = {
SLE_END()
};
-static const byte _roadveh_desc[] = {
+static const SaveLoad _roadveh_desc[] = {
SLE_WRITEBYTE(Vehicle,type,VEH_Road, 1), // Road type. VEH_Road in mem, 1 in file.
SLE_INCLUDEX(0, INC_VEHICLE_COMMON),
SLE_VARX(offsetof(Vehicle,u)+offsetof(VehicleRoad,state), SLE_UINT8),
@@ -1949,7 +1949,7 @@ static const byte _roadveh_desc[] = {
SLE_END()
};
-static const byte _ship_desc[] = {
+static const SaveLoad _ship_desc[] = {
SLE_WRITEBYTE(Vehicle,type,VEH_Ship, 2), // Ship type. VEH_Ship in mem, 2 in file.
SLE_INCLUDEX(0, INC_VEHICLE_COMMON),
SLE_VARX(offsetof(Vehicle,u)+offsetof(VehicleShip,state), SLE_UINT8),
@@ -1960,7 +1960,7 @@ static const byte _ship_desc[] = {
SLE_END()
};
-static const byte _aircraft_desc[] = {
+static const SaveLoad _aircraft_desc[] = {
SLE_WRITEBYTE(Vehicle,type,VEH_Aircraft, 3), // Aircraft type. VEH_Aircraft in mem, 3 in file.
SLE_INCLUDEX(0, INC_VEHICLE_COMMON),
SLE_VARX(offsetof(Vehicle,u)+offsetof(VehicleAir,crashed_counter), SLE_UINT16),
@@ -1979,7 +1979,7 @@ static const byte _aircraft_desc[] = {
SLE_END()
};
-static const byte _special_desc[] = {
+static const SaveLoad _special_desc[] = {
SLE_WRITEBYTE(Vehicle,type,VEH_Special, 4),
SLE_VAR(Vehicle,subtype, SLE_UINT8),
@@ -2011,7 +2011,7 @@ static const byte _special_desc[] = {
SLE_END()
};
-static const byte _disaster_desc[] = {
+static const SaveLoad _disaster_desc[] = {
SLE_WRITEBYTE(Vehicle,type,VEH_Disaster, 5),
SLE_REF(Vehicle,next, REF_VEHICLE_OLD),
diff --git a/waypoint.c b/waypoint.c
index f2c502b32..93e56b935 100644
--- a/waypoint.c
+++ b/waypoint.c
@@ -429,7 +429,7 @@ void InitializeWaypoints(void)
AddBlockToPool(&_waypoint_pool);
}
-static const byte _waypoint_desc[] = {
+static const SaveLoad _waypoint_desc[] = {
SLE_CONDVAR(Waypoint, xy, SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
SLE_CONDVAR(Waypoint, xy, SLE_UINT32, 6, 255),
SLE_CONDVAR(Waypoint, town_index, SLE_UINT16, 12, 255),
diff --git a/win32.c b/win32.c
index 6f2a746b3..a2ef16322 100644
--- a/win32.c
+++ b/win32.c
@@ -181,7 +181,7 @@ static void ClientSizeChanged(int w, int h)
}
}
-void DoExitSave(void);
+extern void DoExitSave(void);
static LRESULT CALLBACK WndProcGdi(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
@@ -1194,8 +1194,11 @@ static const char _save_succeeded[] =
"Be aware that critical parts of the internal game state may have become "
"corrupted. The saved game is not guaranteed to work.";
-bool EmergencySave();
-
+static bool EmergencySave(void)
+{
+ SaveOrLoad("crash.sav", SL_SAVE);
+ return true;
+}
typedef struct {
HINTERNET (WINAPI *InternetOpenA)(LPCSTR,DWORD, LPCSTR, LPCSTR, DWORD);