OpenTTD-patches/saveload.c
truelight 97728357e4 (svn r1786) -Fix: unitnumber is increased to 16bit, so now you can have up to 5000
trains in one game (instead of the 240 which was the current value). 
Default max allowed vehicles per type is changed:
  Trains:  500 (old 80)
  Road:    500 (old 80)
  Ships:   200 (old 40)
  Aicraft: 300 (old 50)
(Tnx to Celestar and Darkvater for checking the patch)
2005-02-04 14:24:23 +00:00

1203 lines
26 KiB
C

#include "stdafx.h"
#include "ttd.h"
#include "vehicle.h"
#include "station.h"
#include "town.h"
#include "player.h"
#include "saveload.h"
enum {
SAVEGAME_MAJOR_VERSION = 8,
SAVEGAME_MINOR_VERSION = 0,
SAVEGAME_LOADABLE_VERSION = (SAVEGAME_MAJOR_VERSION << 8) + SAVEGAME_MINOR_VERSION
};
/******************************************************/
/******************************************************/
/******************************************************/
enum NeedLengthValues { NL_NONE = 0,NL_WANTLENGTH = 1,NL_CALCLENGTH = 2};
SaverLoader _sl;
// fill the input buffer
static void SlReadFill(void)
{
uint len = _sl.read_bytes();
assert(len != 0);
_sl.bufp = _sl.buf;
_sl.bufe = _sl.buf + len;
_sl.offs_base += len;
}
static uint32 SlGetOffs(void)
{
return _sl.offs_base - (_sl.bufe - _sl.bufp);
}
// flush the output buffer
static void SlWriteFill(void)
{
// flush current buffer?
if (_sl.bufp != NULL) {
uint len = _sl.bufp - _sl.buf;
_sl.offs_base += len;
if (len) _sl.write_bytes(len);
}
// setup next buffer
_sl.bufp = _sl.buf;
_sl.bufe = _sl.buf + _sl.bufsize;
}
// error handler, calls longjmp to simulate an exception.
static void NORETURN SlError(const char *msg)
{
_sl.excpt_msg = msg;
longjmp(_sl.excpt, 0);
}
int SlReadByte(void)
{
if (_sl.bufp == _sl.bufe) SlReadFill();
return *_sl.bufp++;
}
void SlWriteByte(byte v)
{
if (_sl.bufp == _sl.bufe) SlWriteFill();
*_sl.bufp++ = v;
}
static int SlReadUint16(void)
{
int x = SlReadByte() << 8;
return x | SlReadByte();
}
static uint32 SlReadUint32(void)
{
uint32 x = SlReadUint16() << 16;
return x | SlReadUint16();
}
static uint64 SlReadUint64(void)
{
uint32 x = SlReadUint32();
uint32 y = SlReadUint32();
return (uint64)x << 32 | y;
}
static void SlWriteUint16(uint16 v)
{
SlWriteByte((byte)(v >> 8));
SlWriteByte((byte)v);
}
static void SlWriteUint32(uint32 v)
{
SlWriteUint16((uint16)(v >> 16));
SlWriteUint16((uint16)v);
}
static void SlWriteUint64(uint64 x)
{
SlWriteUint32((uint32)(x >> 32));
SlWriteUint32((uint32)x);
}
static int SlReadSimpleGamma(void)
{
int x = SlReadByte();
if (x & 0x80)
x = ((x&0x7F) << 8) + SlReadByte();
return x;
}
static void SlWriteSimpleGamma(uint i)
{
assert(i < (1 << 14));
if (i >= 0x80) {
SlWriteByte((byte)(0x80|(i >> 8)));
SlWriteByte((byte)i);
} 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);
}
static inline int SlReadArrayLength(void)
{
return SlReadSimpleGamma();
}
static inline void SlWriteArrayLength(uint length)
{
SlWriteSimpleGamma(length);
}
void SlSetArrayIndex(uint index)
{
_sl.need_length = NL_WANTLENGTH;
_sl.array_index = index;
}
int SlIterateArray(void)
{
int ind;
static uint32 next_offs;
// Must be at end of current block.
assert(next_offs == 0 || SlGetOffs() == next_offs);
while(true) {
uint len = SlReadArrayLength();
if (len == 0) {
next_offs = 0;
return -1;
}
_sl.obj_len = --len;
next_offs = SlGetOffs() + len;
switch(_sl.block_mode) {
case CH_SPARSE_ARRAY: ind = SlReadSparseIndex(); break;
case CH_ARRAY: ind = _sl.array_index++; break;
default:
DEBUG(misc, 0) ("SlIterateArray: error\n");
return -1; // error
}
if (len != 0)
return ind;
}
}
// Sets the length of either a RIFF object or the number of items in an array.
void SlSetLength(uint length)
{
switch(_sl.need_length) {
case NL_WANTLENGTH:
_sl.need_length = NL_NONE;
switch(_sl.block_mode) {
case CH_RIFF:
// Really simple to write a RIFF length :)
SlWriteUint32(length);
break;
case CH_ARRAY:
assert(_sl.array_index >= _sl.last_array_index);
while (++_sl.last_array_index <= _sl.array_index)
SlWriteArrayLength(1);
SlWriteArrayLength(length + 1);
break;
case CH_SPARSE_ARRAY:
SlWriteArrayLength(length + 1 + SlGetGammaLength(_sl.array_index)); // Also include length of sparse index.
SlWriteSparseIndex(_sl.array_index);
break;
default: NOT_REACHED();
}
break;
case NL_CALCLENGTH:
_sl.obj_len += length;
break;
}
}
static void SlCopyBytes(void *ptr, size_t length)
{
byte *p = (byte*)ptr;
if (_sl.save) {
while(length) {
SlWriteByte(*p++);
length--;
}
} else {
while(length) {
// INLINED SlReadByte
#if !defined(_DEBUG)
if (_sl.bufp == _sl.bufe) SlReadFill();
*p++ = *_sl.bufp++;
#else
*p++ = SlReadByte();
#endif
length--;
}
}
}
static void SlSkipBytes(size_t length)
{
while (length) {
SlReadByte();
length--;
}
}
uint SlGetFieldLength(void)
{
return _sl.obj_len;
}
static void SlSaveLoadConv(void *ptr, uint 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();
}
// 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;
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();
}
} else {
// 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;
case SLE_FILE_U16: x = (uint16)SlReadUint16(); break;
case SLE_FILE_I32: x = (int32)SlReadUint32(); break;
case SLE_FILE_U32: x = (uint32)SlReadUint32(); break;
case SLE_FILE_I64: x = (int64)SlReadUint64(); break;
case SLE_FILE_U64: x = (uint64)SlReadUint64(); break;
case SLE_FILE_STRINGID: x = RemapOldStringID((uint16)SlReadUint16()); break;
default:
assert(0);
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();
}
}
}
static const byte _conv_lengths[] = {1,1,2,2,4,4,8,8,2};
static uint SlCalcConvLen(uint conv, void *p)
{
return _conv_lengths[conv & 0xF];
}
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));
// Determine length only?
if (_sl.need_length == NL_CALCLENGTH)
return;
}
// handle buggy stuff
if (!_sl.save && _sl.version == 0) {
if (conv == SLE_INT16 || conv == SLE_UINT16 || conv == SLE_STRINGID) {
length *= 2;
conv = SLE_INT8;
} else if (conv == SLE_INT32 || conv == SLE_UINT32) {
length *= 4;
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.
byte *a = (byte*)array;
while (length) {
SlSaveLoadConv(a, conv);
a += _conv_mem_size[(conv >> 4)&0xf];
length--;
}
}
}
}
// Calculate the size of an object.
static size_t SlCalcObjLength(void *object, const void *desc)
{
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++;
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();
}
} 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);
}
return length;
}
void SlObject(void *object, const void *desc)
{
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));
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;
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++;
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);
}
break;
// Array
case 2: SlArray(ptr, *d++, conv); break;
default:NOT_REACHED();
}
// Write byte.
} else if (cmd == 8) {
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);
}
}
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++;
}
return length;
}
// Save/Load a list of global variables
void SlGlobList(const SaveLoadGlobVarList *desc)
{
if (_sl.need_length != NL_NONE) {
SlSetLength(SlCalcGlobListLength(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++;
}
}
void SlAutolength(AutolengthProc *proc, void *arg)
{
uint32 offs;
assert(_sl.save);
// Tell it to calculate the length
_sl.need_length = NL_CALCLENGTH;
_sl.obj_len = 0;
proc(arg);
// Setup length
_sl.need_length = NL_WANTLENGTH;
SlSetLength(_sl.obj_len);
offs = SlGetOffs() + _sl.obj_len;
// And write the stuff
proc(arg);
assert(offs == SlGetOffs());
}
static void SlLoadChunk(const ChunkHandler *ch)
{
byte m = SlReadByte();
size_t len;
uint32 endoffs;
_sl.block_mode = m;
_sl.obj_len = 0;
switch(m) {
case CH_ARRAY:
_sl.array_index = 0;
ch->load_proc();
break;
case CH_SPARSE_ARRAY:
ch->load_proc();
break;
case CH_RIFF:
// Read length
len = SlReadByte() << 16;
len += SlReadUint16();
_sl.obj_len = len;
endoffs = SlGetOffs() + len;
ch->load_proc();
assert(SlGetOffs() == endoffs);
break;
default:
assert(0);
}
}
static ChunkSaveLoadProc *_tmp_proc_1;
static void SlStubSaveProc2(void *arg)
{
_tmp_proc_1();
}
static void SlStubSaveProc(void)
{
SlAutolength(SlStubSaveProc2, NULL);
}
static void SlSaveChunk(const ChunkHandler *ch)
{
ChunkSaveLoadProc *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;
proc = SlStubSaveProc;
}
_sl.block_mode = ch->flags & CH_TYPE_MASK;
switch(ch->flags & CH_TYPE_MASK) {
case CH_RIFF:
_sl.need_length = NL_WANTLENGTH;
proc();
break;
case CH_ARRAY:
_sl.last_array_index = 0;
SlWriteByte(CH_ARRAY);
proc();
SlWriteArrayLength(0); // Terminate arrays
break;
case CH_SPARSE_ARRAY:
SlWriteByte(CH_SPARSE_ARRAY);
proc();
SlWriteArrayLength(0); // Terminate arrays
break;
default:
NOT_REACHED();
}
}
static void SlSaveChunks(void)
{
const ChunkHandler *ch;
const ChunkHandler * const * chsc;
uint p;
for(p=0; p!=CH_NUM_PRI_LEVELS; p++) {
for(chsc=_sl.chs;(ch=*chsc++) != NULL;) {
while(true) {
if (((ch->flags >> CH_PRI_SHL) & (CH_NUM_PRI_LEVELS - 1)) == p)
SlSaveChunk(ch);
if (ch->flags & CH_LAST)
break;
ch++;
}
}
}
// Terminator
SlWriteUint32(0);
}
static const ChunkHandler *SlFindChunkHandler(uint32 id)
{
const ChunkHandler *ch;
const ChunkHandler * const * chsc;
for(chsc=_sl.chs;(ch=*chsc++) != NULL;) {
while(true) {
if (ch->id == id)
return ch;
if (ch->flags & CH_LAST)
break;
ch++;
}
}
return NULL;
}
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
ch = SlFindChunkHandler(id);
if (ch == NULL) SlError("found unknown tag in savegame (sync error)");
SlLoadChunk(ch);
}
}
//*******************************************
//********** START OF LZO CODE **************
//*******************************************
#define LZO_SIZE 8192
#include "minilzo.h"
static uint ReadLZO(void)
{
byte out[LZO_SIZE + LZO_SIZE / 64 + 16 + 3 + 8];
uint32 tmp[2];
uint32 size;
uint len;
// Read header
if (fread(tmp, sizeof(tmp), 1, _sl.fh) != 1) SlError("file read failed");
// Check if size is bad
((uint32*)out)[0] = size = tmp[1];
if (_sl.version != 0) {
tmp[0] = TO_BE32(tmp[0]);
size = TO_BE32(size);
}
if (size >= sizeof(out)) SlError("inconsistent size");
// Read block
if (fread(out + sizeof(uint32), size, 1, _sl.fh) != 1) SlError("file read failed");
// Verify checksum
if (tmp[0] != lzo_adler32(0, out, size + sizeof(uint32))) SlError("bad checksum");
// Decompress
lzo1x_decompress(out + sizeof(uint32)*1, size, _sl.buf, &len, NULL);
return len;
}
// p contains the pointer to the buffer, len contains the pointer to the length.
// len bytes will be written, p and l will be updated to reflect the next buffer.
static void WriteLZO(uint size)
{
byte out[LZO_SIZE + LZO_SIZE / 64 + 16 + 3 + 8];
byte wrkmem[sizeof(byte*)*4096];
uint outlen;
lzo1x_1_compress(_sl.buf, size, out + sizeof(uint32)*2, &outlen, wrkmem);
((uint32*)out)[1] = TO_BE32(outlen);
((uint32*)out)[0] = TO_BE32(lzo_adler32(0, out + sizeof(uint32), outlen + sizeof(uint32)));
if (fwrite(out, outlen + sizeof(uint32)*2, 1, _sl.fh) != 1) SlError("file write failed");
}
static bool InitLZO(void)
{
_sl.bufsize = LZO_SIZE;
_sl.buf = (byte*)malloc(LZO_SIZE);
return true;
}
static void UninitLZO(void)
{
free(_sl.buf);
}
//*******************************************
//******** START OF NOCOMP CODE *************
//*******************************************
static uint ReadNoComp(void)
{
return fread(_sl.buf, 1, LZO_SIZE, _sl.fh);
}
static void WriteNoComp(uint size)
{
fwrite(_sl.buf, 1, size, _sl.fh);
}
static bool InitNoComp(void)
{
_sl.bufsize = LZO_SIZE;
_sl.buf = (byte*)malloc(LZO_SIZE);
return true;
}
static void UninitNoComp(void)
{
free(_sl.buf);
}
//********************************************
//********** START OF ZLIB CODE **************
//********************************************
#if defined(WITH_ZLIB)
#include <zlib.h>
static z_stream _z;
static bool InitReadZlib(void)
{
memset(&_z, 0, sizeof(_z));
if (inflateInit(&_z) != Z_OK) return false;
_sl.bufsize = 4096;
_sl.buf = (byte*)malloc(4096 + 4096); // also contains fread buffer
return true;
}
static uint ReadZlib(void)
{
int r;
_z.next_out = _sl.buf;
_z.avail_out = 4096;
do {
// read more bytes from the file?
if (_z.avail_in == 0) {
_z.avail_in = fread(_z.next_in = _sl.buf + 4096, 1, 4096, _sl.fh);
}
// inflate the data
r = inflate(&_z, 0);
if (r == Z_STREAM_END)
break;
if (r != Z_OK)
SlError("inflate() failed");
} while (_z.avail_out);
return 4096 - _z.avail_out;
}
static void UninitReadZlib(void)
{
inflateEnd(&_z);
free(_sl.buf);
}
static bool InitWriteZlib(void)
{
memset(&_z, 0, sizeof(_z));
if (deflateInit(&_z, 6) != Z_OK) return false;
_sl.bufsize = 4096;
_sl.buf = (byte*)malloc(4096); // also contains fread buffer
return true;
}
static void WriteZlibLoop(z_streamp z, byte *p, uint len, int mode)
{
char buf[1024]; // output buffer
int r;
uint n;
z->next_in = p;
z->avail_in = len;
do {
z->next_out = buf;
z->avail_out = sizeof(buf);
r = deflate(z, mode);
// bytes were emitted?
if ((n=sizeof(buf) - z->avail_out) != 0) {
if (fwrite(buf, n, 1, _sl.fh) != 1) SlError("file write error");
}
if (r == Z_STREAM_END)
break;
if (r != Z_OK) SlError("zlib returned error code");
} while (z->avail_in || !z->avail_out);
}
static void WriteZlib(uint len)
{
WriteZlibLoop(&_z, _sl.buf, len, 0);
}
static void UninitWriteZlib(void)
{
// flush any pending output.
if (_sl.fh) WriteZlibLoop(&_z, NULL, 0, Z_FINISH);
deflateEnd(&_z);
free(_sl.buf);
}
#endif //WITH_ZLIB
//*******************************************
//************* END OF CODE *****************
//*******************************************
// these define the chunks
extern const ChunkHandler _misc_chunk_handlers[];
extern const ChunkHandler _player_chunk_handlers[];
extern const ChunkHandler _veh_chunk_handlers[];
extern const ChunkHandler _order_chunk_handlers[];
extern const ChunkHandler _town_chunk_handlers[];
extern const ChunkHandler _sign_chunk_handlers[];
extern const ChunkHandler _station_chunk_handlers[];
extern const ChunkHandler _industry_chunk_handlers[];
extern const ChunkHandler _engine_chunk_handlers[];
extern const ChunkHandler _economy_chunk_handlers[];
extern const ChunkHandler _animated_tile_chunk_handlers[];
static const ChunkHandler * const _chunk_handlers[] = {
_misc_chunk_handlers,
_veh_chunk_handlers,
_order_chunk_handlers,
_industry_chunk_handlers,
_economy_chunk_handlers,
_engine_chunk_handlers,
_town_chunk_handlers,
_sign_chunk_handlers,
_station_chunk_handlers,
_player_chunk_handlers,
_animated_tile_chunk_handlers,
NULL,
};
// used to include a vehicle desc in another desc.
extern const byte _common_veh_desc[];
static const byte * 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)
{
if (v == NULL)
return 0;
switch (t) {
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 ((byte*)v - (byte*)_roadstops) / sizeof(_roadstops[0]) + 1;
return (RoadStop *)v - _roadstops + 1;
default:
NOT_REACHED();
}
return 0;
}
static void *IntToReference(uint r, uint t)
{
/* 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;
if (t != REF_VEHICLE_OLD && r == 0)
return NULL;
switch (t) {
case REF_ORDER: return GetOrder(r - 1);
case REF_VEHICLE: {
if (!AddBlockIfNeeded(&_vehicle_pool, r - 1))
error("Vehicles: failed loading savegame: too many vehicles");
return GetVehicle(r - 1);
}
case REF_STATION: {
if (!AddBlockIfNeeded(&_station_pool, r - 1))
error("Stations: failed loading savegame: too many stations");
return GetStation(r - 1);
}
case REF_TOWN: {
if (!AddBlockIfNeeded(&_town_pool, r - 1))
error("Towns: failed loading savegame: too many towns");
return GetTown(r - 1);
}
case REF_ROADSTOPS:
//return (byte*)_roadstops + (r - 1) * sizeof(_roadstops[0]);
return &_roadstops[r - 1];
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)
return NULL;
if (!AddBlockIfNeeded(&_vehicle_pool, r))
error("Vehicles: failed loading savegame: too many vehicles");
return GetVehicle(r);
}
default:
NOT_REACHED();
}
return NULL;
}
typedef struct {
const char *name;
uint32 tag;
bool (*init_read)(void);
ReaderProc *reader;
void (*uninit_read)(void);
bool (*init_write)(void);
WriterProc *writer;
void (*uninit_write)(void);
} 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},
#if defined(WITH_ZLIB)
{"zlib", TO_BE32X('OTTZ'), InitReadZlib,ReadZlib, UninitReadZlib, InitWriteZlib, WriteZlib, UninitWriteZlib},
#else
{"zlib", TO_BE32X('OTTZ'), NULL,NULL,NULL,NULL,NULL,NULL}
#endif
};
static const SaveLoadFormat *GetSavegameFormat(const char *s)
{
const SaveLoadFormat *def;
int i;
// find default savegame format
def = endof(_saveload_formats) - 1;
while (!def->init_write) def--;
if (_savegame_format[0]) {
for(i = 0; i!=lengthof(_saveload_formats); i++)
if (_saveload_formats[i].init_write && !strcmp(s, _saveload_formats[i].name))
return _saveload_formats + i;
ShowInfoF("Savegame format '%s' is not available. Reverting to '%s'.", s, def->name);
}
return def;
}
// actual loader/saver function
void InitializeGame(uint log_x, uint log_y);
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
int SaveOrLoad(const char *filename, int mode)
{
uint32 hdr[2];
const SaveLoadFormat *fmt;
uint version;
// old style load
if (mode == SL_OLD_LOAD) {
InitializeGame(8, 8);
if (!LoadOldSaveGame(filename)) return SL_REINIT;
AfterLoadGame(0);
return SL_OK;
}
_sl.fh = fopen(filename, mode?"wb":"rb");
if (_sl.fh == NULL)
return SL_ERROR;
_sl.bufe = _sl.bufp = NULL;
_sl.offs_base = 0;
_sl.int_to_ref_proc = IntToReference;
_sl.ref_to_int_proc = ReferenceToInt;
_sl.save = mode;
_sl.includes = _desc_includes;
_sl.chs = _chunk_handlers;
// setup setjmp error handler
if (setjmp(_sl.excpt)) {
// close file handle.
fclose(_sl.fh); _sl.fh = NULL;
// deinitialize compressor.
_sl.excpt_uninit();
// 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;
} else {
ShowInfoF("Save game failed: %s.", _sl.excpt_msg);
return SL_ERROR;
}
}
// we first initialize here to avoid: "warning: variable `version' might
// be clobbered by `longjmp' or `vfork'"
version = 0;
if (mode != SL_LOAD) {
fmt = GetSavegameFormat(_savegame_format);
_sl.write_bytes = fmt->writer;
_sl.excpt_uninit = fmt->uninit_write;
if (!fmt->init_write()) goto init_err;
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");
_sl.version = SAVEGAME_MAJOR_VERSION;
BeforeSaveGame();
SlSaveChunks();
SlWriteFill(); // flush the save buffer
fmt->uninit_write();
} else {
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;
}
// see if we have any loader for this type.
for(fmt = _saveload_formats; ; fmt++) {
if (fmt == endof(_saveload_formats)) {
printf("Unknown savegame type, trying to load it as the buggy format.\n");
rewind(_sl.fh);
_sl.version = 0;
_sl.full_version = 0;
version = 0;
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;
_sl.version = (version >> 8);
_sl.full_version = version;
break;
}
}
_sl.read_bytes = fmt->reader;
_sl.excpt_uninit = fmt->uninit_read;
// loader for this savegame type is not implemented?
if (fmt->init_read == NULL) {
ShowInfoF("Loader for '%s' is not available.", fmt->name);
fclose(_sl.fh);
return SL_ERROR;
}
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 */
InitializeGame(8, 8);
SlLoadChunks();
fmt->uninit_read();
}
fclose(_sl.fh);
if (mode == SL_LOAD) {
if (!AfterLoadGame(version))
return SL_REINIT;
}
return SL_OK;
}
#ifdef WIN32
bool EmergencySave(void)
{
SaveOrLoad("crash.sav", SL_SAVE);
return true;
}
#endif
void DoExitSave(void)
{
char buf[200];
sprintf(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)
{
const SaveLoadFormat *fmt;
uint32 hdr;
FILE *f;
int mode = SL_OLD_LOAD;
f = fopen(file, "rb");
if (fread(&hdr, sizeof(hdr), 1, f) != 1) {
printf("Savegame is obsolete or invalid format.\n");
mode = SL_LOAD; // don't try to get filename, just show name as it is written
}
else {
// see if we have any loader for this type.
for (fmt = _saveload_formats; fmt != endof(_saveload_formats); fmt++) {
if (fmt->tag == hdr) {
mode = SL_LOAD; // new type of savegame
break;
}
}
}
fclose(f);
return mode;
}*/