diff options
author | truelight <truelight@openttd.org> | 2004-08-09 17:04:08 +0000 |
---|---|---|
committer | truelight <truelight@openttd.org> | 2004-08-09 17:04:08 +0000 |
commit | efaeb275f78e18d594d9ee8ff04eccd2dc59512c (patch) | |
tree | bc8e1f56d77706d14d048cb2d99e53291930b520 /saveload.c | |
download | openttd-efaeb275f78e18d594d9ee8ff04eccd2dc59512c.tar.xz |
(svn r1) Import of revision 975 of old (crashed) SVN
Diffstat (limited to 'saveload.c')
-rw-r--r-- | saveload.c | 1173 |
1 files changed, 1173 insertions, 0 deletions
diff --git a/saveload.c b/saveload.c new file mode 100644 index 000000000..4c9693dba --- /dev/null +++ b/saveload.c @@ -0,0 +1,1173 @@ +#include "stdafx.h" +#include "ttd.h" +#include "vehicle.h" +#include "station.h" +#include "town.h" +#include "player.h" +#include "saveload.h" +#include <setjmp.h> + +enum { + SAVEGAME_MAJOR_VERSION = 4, + SAVEGAME_MINOR_VERSION = 0, + + SAVEGAME_LOADABLE_VERSION = (SAVEGAME_MAJOR_VERSION << 8) + SAVEGAME_MINOR_VERSION +}; + +/******************************************************/ +/******************************************************/ +/******************************************************/ + +typedef void WriterProc(uint len); +typedef uint ReaderProc(); + +typedef uint ReferenceToIntProc(void *v, uint t); +typedef void *IntToReferenceProc(uint r, uint t); + +typedef struct { + bool save; + byte need_length; + byte block_mode; + bool error; + byte version; + + int obj_len; + int array_index, last_array_index; + + uint32 offs_base; + + WriterProc *write_bytes; + ReaderProc *read_bytes; + + ReferenceToIntProc *ref_to_int_proc; + IntToReferenceProc *int_to_ref_proc; + + const ChunkHandler * const * chs; + const byte * const *includes; + + byte *bufp, *bufe; + + int tmp; + + // these 3 may be used by compressor/decompressors. + byte *buf; // pointer and size to read/write, initialized by init + uint bufsize; + FILE *fh; + + void (*excpt_uninit)(); + const char *excpt_msg; + jmp_buf excpt; // used to jump to "exception handler" +} SaverLoader; + +enum NeedLengthValues { NL_NONE = 0,NL_WANTLENGTH = 1,NL_CALCLENGTH = 2}; + +SaverLoader _sl; + +// fill the input buffer +static void SlReadFill() +{ + uint len = _sl.read_bytes(); + assert(len != 0); + + _sl.bufp = _sl.buf; + _sl.bufe = _sl.buf + len; + _sl.offs_base += len; +} + +static uint32 SlGetOffs() +{ + return _sl.offs_base - (_sl.bufe - _sl.bufp); +} + +// flush the output buffer +static void SlWriteFill() +{ + // 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() +{ + if (_sl.bufp == _sl.bufe) SlReadFill(); + return *_sl.bufp++; +} + +void SlWriteByte(byte v) +{ + if (_sl.bufp == _sl.bufe) SlWriteFill(); + *_sl.bufp++ = v; +} + +int SlReadUint16() +{ + int x = SlReadByte() << 8; + return x | SlReadByte(); +} + +uint32 SlReadUint32() +{ + uint32 x = SlReadUint16() << 16; + return x | SlReadUint16(); +} + +uint64 SlReadUint64() +{ + uint32 x = SlReadUint32(); + uint32 y = SlReadUint32(); + return (uint64)x << 32 | y; +} + +void SlWriteUint16(uint16 v) +{ + SlWriteByte((byte)(v >> 8)); + SlWriteByte((byte)v); +} + +void SlWriteUint32(uint32 v) +{ + SlWriteUint16((uint16)(v >> 16)); + SlWriteUint16((uint16)v); +} + +void SlWriteUint64(uint64 x) +{ + SlWriteUint32((uint32)(x >> 32)); + SlWriteUint32((uint32)x); +} + +static int SlReadSimpleGamma() +{ + 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; +} + +int INLINE SlReadSparseIndex() +{ + return SlReadSimpleGamma(); +} + +void INLINE SlWriteSparseIndex(uint index) +{ + SlWriteSimpleGamma(index); +} + +int INLINE SlReadArrayLength() +{ + return SlReadSimpleGamma(); +} + +void INLINE SlWriteArrayLength(uint length) +{ + SlWriteSimpleGamma(length); +} + +void SlSetArrayIndex(uint index) +{ + _sl.need_length = NL_WANTLENGTH; + _sl.array_index = index; +} + +int SlIterateArray() +{ + 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; + } +} + +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--; + } + } +} + +void SlSkipBytes(size_t length) +{ + while (length) { + SlReadByte(); + length--; + } +} + +uint SlGetFieldLength() +{ + 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; + byte *d = (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) +{ + byte *d = (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) { + 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() +{ + 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(); + } +} + +void SlSaveChunks() +{ + const ChunkHandler *ch; + const ChunkHandler * const * chsc; + uint p; + + for(p=0; p!=CH_NUM_PRI_LEVELS; p++) { + for(chsc=_sl.chs;(ch=*chsc++) != NULL;) { + while(true) { + if (((ch->flags >> CH_PRI_SHL) & (CH_NUM_PRI_LEVELS - 1)) == p) + SlSaveChunk(ch); + if (ch->flags & CH_LAST) + break; + ch++; + } + } + } + + // Terminator + SlWriteUint32(0); +} + +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; +} + +void SlLoadChunks() +{ + 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 + +int CDECL lzo1x_1_compress( const byte *src, uint src_len,byte *dst, uint *dst_len,void *wrkmem ); +uint32 CDECL lzo_adler32(uint32 adler, const byte *buf, uint len); +int CDECL lzo1x_decompress( const byte *src, uint src_len,byte *dst, uint *dst_len,void *wrkmem /* NOT USED */ ); + +static uint ReadLZO() +{ + byte out[LZO_SIZE + LZO_SIZE / 64 + 16 + 3 + 8]; + uint32 tmp[2]; + uint32 size; + uint len; + + // Read header + if (fread(tmp, sizeof(tmp), 1, _sl.fh) != 1) SlError("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() { + _sl.bufsize = LZO_SIZE; + _sl.buf = (byte*)malloc(LZO_SIZE); + return true; +} + +static void UninitLZO() { + free(_sl.buf); +} + +//******************************************* +//******** START OF NOCOMP CODE ************* +//******************************************* +static uint ReadNoComp() +{ + return fread(_sl.buf, 1, LZO_SIZE, _sl.fh); +} + +static void WriteNoComp(uint size) +{ + fwrite(_sl.buf, 1, size, _sl.fh); +} + +static bool InitNoComp() +{ + _sl.bufsize = LZO_SIZE; + _sl.buf = (byte*)malloc(LZO_SIZE); + return true; +} + +static void UninitNoComp() +{ + free(_sl.buf); +} + +//******************************************** +//********** START OF ZLIB CODE ************** +//******************************************** + +#if defined(WITH_ZLIB) +#include <zlib.h> +static z_stream _z; + +static bool InitReadZlib() +{ + memset(&_z, 0, sizeof(_z)); + if (inflateInit(&_z) != Z_OK) return false; + + _sl.bufsize = 4096; + _sl.buf = (byte*)malloc(4096 + 4096); // also contains fread buffer + return true; +} + +static uint ReadZlib() +{ + int r; + + _z.next_out = _sl.buf; + _z.avail_out = 4096; + + do { + // read more bytes from the file? + if (_z.avail_in == 0) { + _z.avail_in = 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() +{ + inflateEnd(&_z); + free(_sl.buf); +} + +static bool InitWriteZlib() +{ + 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() +{ + // 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 _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, + _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 +}; + +typedef struct { + void *base; + size_t size; +} ReferenceSetup; + +// used to translate "pointers" +static const ReferenceSetup _ref_setup[] = { + {_order_array,sizeof(_order_array[0])}, + {_vehicles,sizeof(_vehicles[0])}, + {_stations,sizeof(_stations[0])}, + {_towns,sizeof(_towns[0])}, +}; + +uint ReferenceToInt(void *v, uint t) +{ + if (v == NULL) return 0; + return ((byte*)v - (byte*)_ref_setup[t].base) / _ref_setup[t].size + 1; +} + +void *IntToReference(uint r, uint t) +{ + if (r == 0) return NULL; + return (byte*)_ref_setup[t].base + (r-1) * _ref_setup[t].size; +} + +typedef struct { + const char *name; + uint32 tag; + + bool (*init_read)(); + ReaderProc *reader; + void (*uninit_read)(); + + bool (*init_write)(); + WriterProc *writer; + void (*uninit_write)(); + +} 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 +extern void InitializeGame(); +extern bool AfterLoadGame(uint version); +extern void BeforeSaveGame(); +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(); + 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; + version = 0; + fmt = _saveload_formats + 0; // lzo + break; + } + if (fmt->tag == hdr[0]) { + // check version number + version = TO_BE32(hdr[1]) >> 8; + + // incompatible version? + if (version > SAVEGAME_LOADABLE_VERSION) goto read_err; + _sl.version = (version>>8); + 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 + InitializeGame(); + SlLoadChunks(); + fmt->uninit_read(); + } + + fclose(_sl.fh); + + if (mode == SL_LOAD) { + if (!AfterLoadGame(version)) + return SL_REINIT; + } + + return SL_OK; +} + +bool EmergencySave() +{ + SaveOrLoad("crash.sav", SL_SAVE); + return true; +} + +// 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; +}*/ |