truelight@0: #include "stdafx.h" truelight@0: #include "ttd.h" truelight@0: #include "vehicle.h" truelight@0: #include "station.h" truelight@0: #include "town.h" truelight@0: #include "player.h" truelight@0: #include "saveload.h" truelight@0: #include truelight@0: truelight@0: enum { truelight@0: SAVEGAME_MAJOR_VERSION = 4, dominik@121: SAVEGAME_MINOR_VERSION = 1, truelight@0: truelight@0: SAVEGAME_LOADABLE_VERSION = (SAVEGAME_MAJOR_VERSION << 8) + SAVEGAME_MINOR_VERSION truelight@0: }; truelight@0: truelight@0: /******************************************************/ truelight@0: /******************************************************/ truelight@0: /******************************************************/ truelight@0: truelight@0: typedef void WriterProc(uint len); truelight@0: typedef uint ReaderProc(); truelight@0: truelight@0: typedef uint ReferenceToIntProc(void *v, uint t); truelight@0: typedef void *IntToReferenceProc(uint r, uint t); truelight@0: truelight@0: typedef struct { truelight@0: bool save; truelight@0: byte need_length; truelight@0: byte block_mode; truelight@0: bool error; truelight@0: byte version; truelight@0: truelight@0: int obj_len; truelight@0: int array_index, last_array_index; truelight@0: truelight@0: uint32 offs_base; truelight@0: truelight@0: WriterProc *write_bytes; truelight@0: ReaderProc *read_bytes; truelight@0: truelight@0: ReferenceToIntProc *ref_to_int_proc; truelight@0: IntToReferenceProc *int_to_ref_proc; truelight@0: truelight@0: const ChunkHandler * const * chs; truelight@0: const byte * const *includes; truelight@0: truelight@0: byte *bufp, *bufe; truelight@0: truelight@0: int tmp; truelight@0: truelight@0: // these 3 may be used by compressor/decompressors. truelight@0: byte *buf; // pointer and size to read/write, initialized by init truelight@0: uint bufsize; truelight@0: FILE *fh; truelight@0: truelight@0: void (*excpt_uninit)(); truelight@0: const char *excpt_msg; truelight@0: jmp_buf excpt; // used to jump to "exception handler" truelight@0: } SaverLoader; truelight@0: truelight@0: enum NeedLengthValues { NL_NONE = 0,NL_WANTLENGTH = 1,NL_CALCLENGTH = 2}; truelight@0: truelight@0: SaverLoader _sl; truelight@0: truelight@0: // fill the input buffer truelight@0: static void SlReadFill() truelight@0: { truelight@0: uint len = _sl.read_bytes(); truelight@0: assert(len != 0); truelight@0: truelight@0: _sl.bufp = _sl.buf; truelight@0: _sl.bufe = _sl.buf + len; truelight@0: _sl.offs_base += len; truelight@0: } truelight@0: truelight@0: static uint32 SlGetOffs() truelight@0: { truelight@0: return _sl.offs_base - (_sl.bufe - _sl.bufp); truelight@0: } truelight@0: truelight@0: // flush the output buffer truelight@0: static void SlWriteFill() truelight@0: { truelight@0: // flush current buffer? truelight@0: if (_sl.bufp != NULL) { truelight@0: uint len = _sl.bufp - _sl.buf; truelight@0: _sl.offs_base += len; truelight@0: if (len) _sl.write_bytes(len); truelight@0: } truelight@193: truelight@0: // setup next buffer truelight@0: _sl.bufp = _sl.buf; truelight@0: _sl.bufe = _sl.buf + _sl.bufsize; truelight@0: } truelight@0: truelight@0: // error handler, calls longjmp to simulate an exception. truelight@0: static void NORETURN SlError(const char *msg) truelight@0: { truelight@0: _sl.excpt_msg = msg; truelight@0: longjmp(_sl.excpt, 0); truelight@0: } truelight@0: truelight@0: int SlReadByte() truelight@0: { truelight@0: if (_sl.bufp == _sl.bufe) SlReadFill(); truelight@0: return *_sl.bufp++; truelight@0: } truelight@0: truelight@0: void SlWriteByte(byte v) truelight@0: { truelight@0: if (_sl.bufp == _sl.bufe) SlWriteFill(); truelight@0: *_sl.bufp++ = v; truelight@0: } truelight@0: tron@410: static int SlReadUint16() truelight@0: { truelight@0: int x = SlReadByte() << 8; truelight@0: return x | SlReadByte(); truelight@0: } truelight@0: tron@410: static uint32 SlReadUint32() truelight@0: { truelight@0: uint32 x = SlReadUint16() << 16; truelight@0: return x | SlReadUint16(); truelight@0: } truelight@0: tron@410: static uint64 SlReadUint64() truelight@0: { truelight@0: uint32 x = SlReadUint32(); truelight@0: uint32 y = SlReadUint32(); truelight@0: return (uint64)x << 32 | y; truelight@0: } truelight@0: tron@410: static void SlWriteUint16(uint16 v) truelight@0: { truelight@0: SlWriteByte((byte)(v >> 8)); truelight@0: SlWriteByte((byte)v); truelight@0: } truelight@0: tron@410: static void SlWriteUint32(uint32 v) truelight@0: { truelight@0: SlWriteUint16((uint16)(v >> 16)); truelight@0: SlWriteUint16((uint16)v); truelight@0: } truelight@0: tron@410: static void SlWriteUint64(uint64 x) truelight@0: { truelight@0: SlWriteUint32((uint32)(x >> 32)); truelight@0: SlWriteUint32((uint32)x); truelight@0: } truelight@0: truelight@0: static int SlReadSimpleGamma() truelight@0: { truelight@0: int x = SlReadByte(); truelight@0: if (x & 0x80) truelight@0: x = ((x&0x7F) << 8) + SlReadByte(); truelight@0: return x; truelight@0: } truelight@0: truelight@0: static void SlWriteSimpleGamma(uint i) truelight@0: { truelight@0: assert(i < (1 << 14)); truelight@0: if (i >= 0x80) { truelight@0: SlWriteByte((byte)(0x80|(i >> 8))); truelight@0: SlWriteByte((byte)i); truelight@0: } else { truelight@0: SlWriteByte(i); truelight@0: } truelight@0: } truelight@0: truelight@0: static uint SlGetGammaLength(uint i) { truelight@0: return (i>=0x80) ? 2 : 1; truelight@0: } truelight@0: tron@536: inline int SlReadSparseIndex() truelight@0: { truelight@0: return SlReadSimpleGamma(); truelight@0: } truelight@0: tron@536: inline void SlWriteSparseIndex(uint index) truelight@0: { truelight@0: SlWriteSimpleGamma(index); truelight@0: } truelight@0: tron@536: inline int SlReadArrayLength() truelight@0: { truelight@0: return SlReadSimpleGamma(); truelight@0: } truelight@0: tron@536: inline void SlWriteArrayLength(uint length) truelight@0: { truelight@0: SlWriteSimpleGamma(length); truelight@0: } truelight@0: truelight@0: void SlSetArrayIndex(uint index) truelight@0: { truelight@0: _sl.need_length = NL_WANTLENGTH; truelight@0: _sl.array_index = index; truelight@0: } truelight@0: truelight@0: int SlIterateArray() truelight@0: { truelight@0: int ind; truelight@0: static uint32 next_offs; truelight@193: truelight@0: // Must be at end of current block. truelight@0: assert(next_offs == 0 || SlGetOffs() == next_offs); truelight@0: truelight@0: while(true) { truelight@0: uint len = SlReadArrayLength(); truelight@0: if (len == 0) { truelight@0: next_offs = 0; truelight@0: return -1; truelight@0: } truelight@0: truelight@0: _sl.obj_len = --len; truelight@0: next_offs = SlGetOffs() + len; truelight@0: truelight@0: switch(_sl.block_mode) { truelight@0: case CH_SPARSE_ARRAY: ind = SlReadSparseIndex(); break; truelight@0: case CH_ARRAY: ind = _sl.array_index++; break; truelight@0: default: truelight@0: DEBUG(misc, 0) ("SlIterateArray: error\n"); truelight@0: return -1; // error truelight@0: } truelight@193: truelight@0: if (len != 0) truelight@0: return ind; truelight@0: } truelight@0: } truelight@0: truelight@0: // Sets the length of either a RIFF object or the number of items in an array. truelight@0: void SlSetLength(uint length) truelight@0: { truelight@0: switch(_sl.need_length) { truelight@0: case NL_WANTLENGTH: truelight@0: _sl.need_length = NL_NONE; truelight@0: switch(_sl.block_mode) { truelight@0: case CH_RIFF: truelight@0: // Really simple to write a RIFF length :) truelight@0: SlWriteUint32(length); truelight@0: break; truelight@0: case CH_ARRAY: truelight@0: assert(_sl.array_index >= _sl.last_array_index); truelight@0: while (++_sl.last_array_index <= _sl.array_index) truelight@0: SlWriteArrayLength(1); truelight@0: SlWriteArrayLength(length + 1); truelight@0: break; truelight@0: case CH_SPARSE_ARRAY: truelight@0: SlWriteArrayLength(length + 1 + SlGetGammaLength(_sl.array_index)); // Also include length of sparse index. truelight@0: SlWriteSparseIndex(_sl.array_index); truelight@0: break; truelight@0: default: NOT_REACHED(); truelight@0: } truelight@0: break; truelight@0: case NL_CALCLENGTH: truelight@0: _sl.obj_len += length; truelight@0: break; truelight@0: } truelight@0: } truelight@0: tron@410: static void SlCopyBytes(void *ptr, size_t length) truelight@0: { truelight@0: byte *p = (byte*)ptr; truelight@0: truelight@0: if (_sl.save) { truelight@0: while(length) { truelight@0: SlWriteByte(*p++); truelight@0: length--; truelight@0: } truelight@0: } else { truelight@0: while(length) { truelight@0: // INLINED SlReadByte truelight@0: #if !defined(_DEBUG) truelight@0: if (_sl.bufp == _sl.bufe) SlReadFill(); truelight@0: *p++ = *_sl.bufp++; truelight@0: #else truelight@0: *p++ = SlReadByte(); truelight@0: #endif truelight@0: length--; truelight@0: } truelight@0: } truelight@0: } truelight@0: truelight@0: void SlSkipBytes(size_t length) truelight@0: { truelight@0: while (length) { truelight@0: SlReadByte(); truelight@0: length--; truelight@0: } truelight@0: } truelight@0: truelight@0: uint SlGetFieldLength() truelight@0: { truelight@0: return _sl.obj_len; truelight@0: } truelight@0: truelight@0: truelight@0: static void SlSaveLoadConv(void *ptr, uint conv) truelight@0: { truelight@0: int64 x = 0; truelight@193: truelight@0: if (_sl.save) { truelight@0: // Read a value from the struct. These ARE endian safe. truelight@0: switch((conv >> 4)&0xf) { truelight@0: case SLE_VAR_I8>>4: x = *(int8*)ptr; break; truelight@0: case SLE_VAR_U8>>4: x = *(byte*)ptr; break; truelight@0: case SLE_VAR_I16>>4: x = *(int16*)ptr; break; truelight@0: case SLE_VAR_U16>>4: x = *(uint16*)ptr; break; truelight@0: case SLE_VAR_I32>>4: x = *(int32*)ptr; break; truelight@0: case SLE_VAR_U32>>4: x = *(uint32*)ptr; break; truelight@193: case SLE_VAR_I64>>4: x = *(int64*)ptr; break; truelight@0: case SLE_VAR_U64>>4: x = *(uint64*)ptr; break; truelight@0: case SLE_VAR_NULL>>4: x = 0; break; truelight@0: default: truelight@0: NOT_REACHED(); truelight@0: } truelight@0: truelight@0: // Write it to the file truelight@0: switch(conv & 0xF) { truelight@0: case SLE_FILE_I8: assert(x >= -128 && x <= 127); SlWriteByte(x);break; truelight@0: case SLE_FILE_U8: assert(x >= 0 && x <= 255); SlWriteByte(x);break; truelight@0: case SLE_FILE_I16:assert(x >= -32768 && x <= 32767); SlWriteUint16(x);break; truelight@0: case SLE_FILE_STRINGID: truelight@0: case SLE_FILE_U16:assert(x >= 0 && x <= 65535); SlWriteUint16(x);break; truelight@0: case SLE_FILE_I32: truelight@0: case SLE_FILE_U32:SlWriteUint32((uint32)x);break; truelight@0: case SLE_FILE_I64: truelight@0: case SLE_FILE_U64:SlWriteUint64(x);break; truelight@0: default: truelight@0: assert(0); truelight@0: NOT_REACHED(); truelight@0: } truelight@0: } else { truelight@193: truelight@0: // Read a value from the file truelight@0: switch(conv & 0xF) { truelight@0: case SLE_FILE_I8: x = (int8)SlReadByte(); break; truelight@0: case SLE_FILE_U8: x = (byte)SlReadByte(); break; truelight@0: case SLE_FILE_I16: x = (int16)SlReadUint16(); break; truelight@0: case SLE_FILE_U16: x = (uint16)SlReadUint16(); break; truelight@0: case SLE_FILE_I32: x = (int32)SlReadUint32(); break; truelight@0: case SLE_FILE_U32: x = (uint32)SlReadUint32(); break; truelight@0: case SLE_FILE_I64: x = (int64)SlReadUint64(); break; truelight@0: case SLE_FILE_U64: x = (uint64)SlReadUint64(); break; truelight@0: case SLE_FILE_STRINGID: x = RemapOldStringID((uint16)SlReadUint16()); break; truelight@0: default: truelight@0: assert(0); truelight@0: NOT_REACHED(); truelight@0: } truelight@0: truelight@0: // Write it to the struct, these ARE endian safe. truelight@0: switch((conv >> 4)&0xf) { truelight@0: case SLE_VAR_I8>>4: *(int8*)ptr = x; break; truelight@0: case SLE_VAR_U8>>4: *(byte*)ptr = x; break; truelight@0: case SLE_VAR_I16>>4: *(int16*)ptr = x; break; truelight@0: case SLE_VAR_U16>>4: *(uint16*)ptr = x; break; truelight@0: case SLE_VAR_I32>>4: *(int32*)ptr = x; break; truelight@0: case SLE_VAR_U32>>4: *(uint32*)ptr = x; break; truelight@0: case SLE_VAR_I64>>4: *(int64*)ptr = x; break; truelight@0: case SLE_VAR_U64>>4: *(uint64*)ptr = x; break; truelight@0: case SLE_VAR_NULL: break; truelight@0: default: truelight@0: NOT_REACHED(); truelight@0: } truelight@0: } truelight@0: } truelight@0: truelight@0: static const byte _conv_lengths[] = {1,1,2,2,4,4,8,8,2}; truelight@0: truelight@0: static uint SlCalcConvLen(uint conv, void *p) truelight@0: { truelight@0: return _conv_lengths[conv & 0xF]; truelight@0: } truelight@0: truelight@0: static uint SlCalcArrayLen(void *array, uint length, uint conv) truelight@0: { truelight@0: return _conv_lengths[conv & 0xF] * length; truelight@0: } truelight@0: truelight@0: static const byte _conv_mem_size[9] = {1,1,2,2,4,4,8,8,0}; truelight@0: void SlArray(void *array, uint length, uint conv) truelight@0: { truelight@0: // Automatically calculate the length? truelight@0: if (_sl.need_length != NL_NONE) { truelight@0: SlSetLength(SlCalcArrayLen(array, length, conv)); truelight@0: // Determine length only? truelight@0: if (_sl.need_length == NL_CALCLENGTH) truelight@0: return; truelight@0: } truelight@0: truelight@0: // handle buggy stuff truelight@0: if (!_sl.save && _sl.version == 0) { truelight@0: if (conv == SLE_INT16 || conv == SLE_UINT16 || conv == SLE_STRINGID) { truelight@0: length *= 2; truelight@0: conv = SLE_INT8; truelight@0: } else if (conv == SLE_INT32 || conv == SLE_UINT32) { truelight@0: length *= 4; truelight@0: conv = SLE_INT8; truelight@0: } truelight@0: } truelight@0: truelight@0: // Optimized cases when input equals output. truelight@0: switch(conv) { truelight@0: case SLE_INT8: truelight@0: case SLE_UINT8:SlCopyBytes(array, length);break; truelight@0: default: { truelight@0: // Default "slow" case. truelight@0: byte *a = (byte*)array; truelight@0: while (length) { truelight@0: SlSaveLoadConv(a, conv); truelight@0: a += _conv_mem_size[(conv >> 4)&0xf]; truelight@0: length--; truelight@0: } truelight@0: } truelight@0: } truelight@0: } truelight@0: truelight@0: // Calculate the size of an object. truelight@0: static size_t SlCalcObjLength(void *object, const void *desc) truelight@0: { truelight@0: size_t length = 0; truelight@0: uint cmd,conv; darkvater@222: const byte *d = (const byte*)desc; truelight@0: truelight@0: // Need to determine the length and write a length tag. truelight@0: while (true) { truelight@0: cmd = (d[0] >> 4); truelight@0: if (cmd < 8) { truelight@0: conv = d[2]; truelight@0: d += 3; truelight@0: if (cmd&4) { truelight@0: d += 2; truelight@0: // check if the field is of the right version truelight@0: if (_sl.version < d[-2] || _sl.version > d[-1]) { truelight@0: if ((cmd & 3) == 2) d++; truelight@0: continue; truelight@0: } truelight@0: } truelight@0: truelight@0: switch(cmd&3) { truelight@0: // Normal variable truelight@193: case 0: length += SlCalcConvLen(conv, NULL);break; truelight@0: // Reference truelight@0: case 1: length += 2; break; truelight@0: // Array truelight@0: case 2: length += SlCalcArrayLen(NULL, *d++, conv); break; truelight@0: default:NOT_REACHED(); truelight@0: } truelight@0: } else if (cmd == 8) { truelight@0: length++; truelight@0: d += 4; truelight@0: } else if (cmd == 9) { truelight@0: length += SlCalcObjLength(NULL, _sl.includes[d[2]]); truelight@0: d += 3; truelight@0: } else if (cmd == 15) truelight@0: break; truelight@0: else truelight@0: assert(0); truelight@0: } truelight@0: return length; truelight@0: } truelight@0: truelight@0: void SlObject(void *object, const void *desc) truelight@0: { darkvater@222: const byte *d = (const byte*)desc; truelight@0: void *ptr; truelight@0: uint cmd,conv; truelight@0: truelight@0: // Automatically calculate the length? truelight@0: if (_sl.need_length != NL_NONE) { truelight@0: SlSetLength(SlCalcObjLength(object, d)); truelight@0: if (_sl.need_length == NL_CALCLENGTH) truelight@0: return; truelight@0: } truelight@0: truelight@0: while (true) { truelight@0: // Currently it only supports up to 4096 byte big objects truelight@0: ptr = (byte*)object + (d[0] & 0xF) + (d[1] << 4); truelight@0: truelight@0: cmd = d[0] >> 4; truelight@0: truelight@0: if (cmd < 8) { truelight@0: conv = d[2]; truelight@0: d += 3; truelight@0: truelight@0: if (cmd&4) { truelight@0: d += 2; truelight@0: // check if the field is of the right version truelight@0: if (_sl.version < d[-2] || _sl.version > d[-1]) { truelight@0: if ((cmd & 3) == 2) d++; truelight@0: continue; truelight@0: } truelight@0: } truelight@0: truelight@0: switch(cmd&3) { truelight@0: // Normal variable truelight@0: case 0: SlSaveLoadConv(ptr, conv); break; truelight@0: // Reference truelight@0: case 1: truelight@0: if (_sl.save) { truelight@0: SlWriteUint16(_sl.ref_to_int_proc(*(void**)ptr, conv)); truelight@0: } else { truelight@0: *(void**)ptr = _sl.int_to_ref_proc(SlReadUint16(), conv); truelight@0: } truelight@0: break; truelight@0: // Array truelight@0: case 2: SlArray(ptr, *d++, conv); break; truelight@0: default:NOT_REACHED(); truelight@0: } truelight@193: truelight@0: // Write byte. truelight@0: } else if (cmd == 8) { truelight@0: if (_sl.save) { truelight@0: SlWriteByte(d[3]); truelight@0: } else { truelight@0: *(byte*)ptr = d[2]; truelight@0: } truelight@0: d += 4; truelight@0: truelight@0: // Include truelight@0: } else if (cmd == 9) { truelight@0: SlObject(ptr, _sl.includes[d[2]]); truelight@0: d += 3; truelight@0: } else if (cmd == 15) truelight@0: break; truelight@0: else truelight@0: assert(0); truelight@0: } truelight@0: } truelight@0: truelight@0: truelight@0: static size_t SlCalcGlobListLength(const SaveLoadGlobVarList *desc) truelight@0: { truelight@0: size_t length = 0; truelight@0: truelight@0: while (desc->address) { tron@617: if(_sl.version >= desc->from_version && _sl.version <= desc->to_version) tron@617: length += SlCalcConvLen(desc->conv, NULL); truelight@0: desc++; truelight@0: } truelight@0: return length; truelight@0: } truelight@0: truelight@0: // Save/Load a list of global variables truelight@0: void SlGlobList(const SaveLoadGlobVarList *desc) truelight@0: { truelight@0: if (_sl.need_length != NL_NONE) { truelight@0: SlSetLength(SlCalcGlobListLength(desc)); truelight@0: if (_sl.need_length == NL_CALCLENGTH) truelight@0: return; truelight@0: } truelight@0: while (true) { truelight@0: void *ptr = desc->address; truelight@0: if (ptr == NULL) truelight@0: break; truelight@0: if(_sl.version >= desc->from_version && _sl.version <= desc->to_version) truelight@0: SlSaveLoadConv(ptr, desc->conv); truelight@0: desc++; truelight@0: } truelight@0: } truelight@0: truelight@0: truelight@0: void SlAutolength(AutolengthProc *proc, void *arg) truelight@0: { truelight@0: uint32 offs; truelight@0: truelight@0: assert(_sl.save); truelight@0: truelight@0: // Tell it to calculate the length truelight@0: _sl.need_length = NL_CALCLENGTH; truelight@0: _sl.obj_len = 0; truelight@0: proc(arg); truelight@0: truelight@0: // Setup length truelight@0: _sl.need_length = NL_WANTLENGTH; truelight@0: SlSetLength(_sl.obj_len); truelight@0: truelight@0: offs = SlGetOffs() + _sl.obj_len; truelight@0: truelight@0: // And write the stuff truelight@0: proc(arg); truelight@0: truelight@0: assert(offs == SlGetOffs()); truelight@0: } truelight@0: truelight@0: static void SlLoadChunk(const ChunkHandler *ch) truelight@0: { truelight@0: byte m = SlReadByte(); truelight@0: size_t len; truelight@0: uint32 endoffs; truelight@0: truelight@0: _sl.block_mode = m; truelight@0: _sl.obj_len = 0; truelight@193: truelight@0: switch(m) { truelight@0: case CH_ARRAY: truelight@0: _sl.array_index = 0; truelight@0: ch->load_proc(); truelight@0: break; truelight@0: truelight@0: case CH_SPARSE_ARRAY: truelight@0: ch->load_proc(); truelight@0: break; truelight@0: truelight@0: case CH_RIFF: truelight@0: // Read length truelight@0: len = SlReadByte() << 16; truelight@0: len += SlReadUint16(); truelight@0: _sl.obj_len = len; truelight@0: endoffs = SlGetOffs() + len; truelight@0: ch->load_proc(); truelight@0: assert(SlGetOffs() == endoffs); truelight@0: break; truelight@0: default: truelight@0: assert(0); truelight@0: } truelight@0: } truelight@0: truelight@0: static ChunkSaveLoadProc *_tmp_proc_1; truelight@0: truelight@0: static void SlStubSaveProc2(void *arg) truelight@0: { truelight@0: _tmp_proc_1(); truelight@0: } truelight@0: truelight@0: static void SlStubSaveProc() truelight@0: { truelight@0: SlAutolength(SlStubSaveProc2, NULL); truelight@0: } truelight@0: truelight@0: static void SlSaveChunk(const ChunkHandler *ch) truelight@0: { truelight@0: ChunkSaveLoadProc *proc; truelight@0: truelight@0: SlWriteUint32(ch->id); truelight@0: truelight@0: proc = ch->save_proc; truelight@0: if (ch->flags & CH_AUTO_LENGTH) { truelight@0: // Need to calculate the length. Solve that by calling SlAutoLength in the save_proc. truelight@0: _tmp_proc_1 = proc; truelight@0: proc = SlStubSaveProc; truelight@0: } truelight@193: truelight@0: _sl.block_mode = ch->flags & CH_TYPE_MASK; truelight@0: switch(ch->flags & CH_TYPE_MASK) { truelight@0: case CH_RIFF: truelight@0: _sl.need_length = NL_WANTLENGTH; truelight@0: proc(); truelight@0: break; truelight@0: case CH_ARRAY: truelight@0: _sl.last_array_index = 0; truelight@0: SlWriteByte(CH_ARRAY); truelight@0: proc(); truelight@0: SlWriteArrayLength(0); // Terminate arrays truelight@0: break; truelight@0: case CH_SPARSE_ARRAY: truelight@0: SlWriteByte(CH_SPARSE_ARRAY); truelight@0: proc(); truelight@0: SlWriteArrayLength(0); // Terminate arrays truelight@0: break; truelight@0: default: truelight@0: NOT_REACHED(); truelight@0: } truelight@0: } truelight@0: tron@410: static void SlSaveChunks() truelight@0: { truelight@0: const ChunkHandler *ch; truelight@0: const ChunkHandler * const * chsc; truelight@0: uint p; truelight@0: truelight@0: for(p=0; p!=CH_NUM_PRI_LEVELS; p++) { truelight@0: for(chsc=_sl.chs;(ch=*chsc++) != NULL;) { truelight@0: while(true) { truelight@0: if (((ch->flags >> CH_PRI_SHL) & (CH_NUM_PRI_LEVELS - 1)) == p) truelight@193: SlSaveChunk(ch); truelight@0: if (ch->flags & CH_LAST) truelight@0: break; truelight@0: ch++; truelight@0: } truelight@0: } truelight@0: } truelight@0: truelight@0: // Terminator truelight@0: SlWriteUint32(0); truelight@0: } truelight@0: truelight@0: static const ChunkHandler *SlFindChunkHandler(uint32 id) truelight@0: { truelight@0: const ChunkHandler *ch; truelight@0: const ChunkHandler * const * chsc; truelight@0: for(chsc=_sl.chs;(ch=*chsc++) != NULL;) { truelight@0: while(true) { truelight@0: if (ch->id == id) truelight@0: return ch; truelight@0: if (ch->flags & CH_LAST) truelight@0: break; truelight@0: ch++; truelight@0: } truelight@0: } truelight@0: return NULL; truelight@0: } truelight@0: tron@410: static void SlLoadChunks() truelight@0: { truelight@0: uint32 id; truelight@0: const ChunkHandler *ch; truelight@0: truelight@0: while(true) { truelight@0: id = SlReadUint32(); truelight@0: if (id == 0) truelight@0: return; truelight@0: #if 0 truelight@0: printf("Loading chunk %c%c%c%c\n", id >> 24, id>>16, id>>8,id); truelight@0: #endif truelight@0: ch = SlFindChunkHandler(id); truelight@0: if (ch == NULL) SlError("found unknown tag in savegame (sync error)"); truelight@0: SlLoadChunk(ch); truelight@0: } truelight@0: } truelight@0: truelight@0: //******************************************* truelight@0: //********** START OF LZO CODE ************** truelight@0: //******************************************* truelight@0: #define LZO_SIZE 8192 truelight@0: truelight@0: int CDECL lzo1x_1_compress( const byte *src, uint src_len,byte *dst, uint *dst_len,void *wrkmem ); truelight@0: uint32 CDECL lzo_adler32(uint32 adler, const byte *buf, uint len); truelight@0: int CDECL lzo1x_decompress( const byte *src, uint src_len,byte *dst, uint *dst_len,void *wrkmem /* NOT USED */ ); truelight@0: truelight@0: static uint ReadLZO() truelight@0: { truelight@0: byte out[LZO_SIZE + LZO_SIZE / 64 + 16 + 3 + 8]; truelight@0: uint32 tmp[2]; truelight@0: uint32 size; truelight@0: uint len; truelight@0: truelight@0: // Read header truelight@0: if (fread(tmp, sizeof(tmp), 1, _sl.fh) != 1) SlError("file read failed"); truelight@0: truelight@0: // Check if size is bad truelight@0: ((uint32*)out)[0] = size = tmp[1]; truelight@193: truelight@0: if (_sl.version != 0) { truelight@0: tmp[0] = TO_BE32(tmp[0]); truelight@0: size = TO_BE32(size); truelight@0: } truelight@0: truelight@0: if (size >= sizeof(out)) SlError("inconsistent size"); truelight@0: truelight@0: // Read block truelight@0: if (fread(out + sizeof(uint32), size, 1, _sl.fh) != 1) SlError("file read failed"); truelight@0: truelight@0: // Verify checksum truelight@0: if (tmp[0] != lzo_adler32(0, out, size + sizeof(uint32))) SlError("bad checksum"); truelight@193: truelight@0: // Decompress truelight@0: lzo1x_decompress(out + sizeof(uint32)*1, size, _sl.buf, &len, NULL); truelight@0: return len; truelight@0: } truelight@0: truelight@0: // p contains the pointer to the buffer, len contains the pointer to the length. truelight@0: // len bytes will be written, p and l will be updated to reflect the next buffer. truelight@0: static void WriteLZO(uint size) truelight@0: { truelight@0: byte out[LZO_SIZE + LZO_SIZE / 64 + 16 + 3 + 8]; truelight@0: byte wrkmem[sizeof(byte*)*4096]; truelight@0: uint outlen; truelight@193: truelight@0: lzo1x_1_compress(_sl.buf, size, out + sizeof(uint32)*2, &outlen, wrkmem); truelight@0: ((uint32*)out)[1] = TO_BE32(outlen); truelight@0: ((uint32*)out)[0] = TO_BE32(lzo_adler32(0, out + sizeof(uint32), outlen + sizeof(uint32))); truelight@0: if (fwrite(out, outlen + sizeof(uint32)*2, 1, _sl.fh) != 1) SlError("file write failed"); truelight@0: } truelight@0: truelight@0: static bool InitLZO() { truelight@0: _sl.bufsize = LZO_SIZE; truelight@0: _sl.buf = (byte*)malloc(LZO_SIZE); truelight@0: return true; truelight@0: } truelight@0: truelight@0: static void UninitLZO() { truelight@0: free(_sl.buf); truelight@0: } truelight@0: truelight@0: //******************************************* truelight@0: //******** START OF NOCOMP CODE ************* truelight@0: //******************************************* truelight@0: static uint ReadNoComp() truelight@0: { truelight@0: return fread(_sl.buf, 1, LZO_SIZE, _sl.fh); truelight@0: } truelight@0: truelight@0: static void WriteNoComp(uint size) truelight@0: { truelight@0: fwrite(_sl.buf, 1, size, _sl.fh); truelight@0: } truelight@0: truelight@0: static bool InitNoComp() truelight@0: { truelight@0: _sl.bufsize = LZO_SIZE; truelight@0: _sl.buf = (byte*)malloc(LZO_SIZE); truelight@0: return true; truelight@0: } truelight@0: truelight@0: static void UninitNoComp() truelight@0: { truelight@0: free(_sl.buf); truelight@0: } truelight@0: truelight@0: //******************************************** truelight@0: //********** START OF ZLIB CODE ************** truelight@0: //******************************************** truelight@0: truelight@0: #if defined(WITH_ZLIB) truelight@0: #include truelight@0: static z_stream _z; truelight@0: truelight@0: static bool InitReadZlib() truelight@0: { truelight@0: memset(&_z, 0, sizeof(_z)); truelight@0: if (inflateInit(&_z) != Z_OK) return false; truelight@193: truelight@0: _sl.bufsize = 4096; truelight@0: _sl.buf = (byte*)malloc(4096 + 4096); // also contains fread buffer truelight@0: return true; truelight@0: } truelight@0: truelight@0: static uint ReadZlib() truelight@0: { truelight@0: int r; truelight@0: truelight@0: _z.next_out = _sl.buf; truelight@0: _z.avail_out = 4096; truelight@0: truelight@0: do { truelight@0: // read more bytes from the file? truelight@0: if (_z.avail_in == 0) { truelight@0: _z.avail_in = fread(_z.next_in = _sl.buf + 4096, 1, 4096, _sl.fh); truelight@0: } truelight@0: truelight@0: // inflate the data truelight@0: r = inflate(&_z, 0); truelight@0: if (r == Z_STREAM_END) truelight@0: break; truelight@0: truelight@193: if (r != Z_OK) truelight@0: SlError("inflate() failed"); truelight@0: } while (_z.avail_out); truelight@0: truelight@0: return 4096 - _z.avail_out; truelight@0: } truelight@0: truelight@0: static void UninitReadZlib() truelight@0: { truelight@0: inflateEnd(&_z); truelight@0: free(_sl.buf); truelight@0: } truelight@0: truelight@0: static bool InitWriteZlib() truelight@0: { truelight@0: memset(&_z, 0, sizeof(_z)); truelight@0: if (deflateInit(&_z, 6) != Z_OK) return false; truelight@0: truelight@0: _sl.bufsize = 4096; truelight@0: _sl.buf = (byte*)malloc(4096); // also contains fread buffer truelight@0: return true; truelight@0: } truelight@0: truelight@0: static void WriteZlibLoop(z_streamp z, byte *p, uint len, int mode) truelight@0: { truelight@0: char buf[1024]; // output buffer truelight@0: int r; truelight@0: uint n; truelight@0: z->next_in = p; truelight@0: z->avail_in = len; truelight@0: do { truelight@0: z->next_out = buf; truelight@0: z->avail_out = sizeof(buf); truelight@0: r = deflate(z, mode); truelight@0: // bytes were emitted? truelight@0: if ((n=sizeof(buf) - z->avail_out) != 0) { truelight@0: if (fwrite(buf, n, 1, _sl.fh) != 1) SlError("file write error"); truelight@0: } truelight@0: if (r == Z_STREAM_END) truelight@0: break; truelight@0: if (r != Z_OK) SlError("zlib returned error code"); truelight@0: } while (z->avail_in || !z->avail_out); truelight@0: } truelight@0: truelight@0: static void WriteZlib(uint len) truelight@0: { truelight@0: WriteZlibLoop(&_z, _sl.buf, len, 0); truelight@0: } truelight@0: truelight@0: static void UninitWriteZlib() truelight@0: { truelight@0: // flush any pending output. truelight@0: if (_sl.fh) WriteZlibLoop(&_z, NULL, 0, Z_FINISH); truelight@0: deflateEnd(&_z); truelight@0: free(_sl.buf); truelight@0: } truelight@0: truelight@0: #endif //WITH_ZLIB truelight@0: truelight@0: //******************************************* truelight@0: //************* END OF CODE ***************** truelight@0: //******************************************* truelight@0: truelight@0: // these define the chunks truelight@0: extern const ChunkHandler _misc_chunk_handlers[]; truelight@0: extern const ChunkHandler _player_chunk_handlers[]; truelight@0: extern const ChunkHandler _veh_chunk_handlers[]; truelight@0: extern const ChunkHandler _town_chunk_handlers[]; truelight@0: extern const ChunkHandler _sign_chunk_handlers[]; truelight@0: extern const ChunkHandler _station_chunk_handlers[]; truelight@0: extern const ChunkHandler _industry_chunk_handlers[]; truelight@0: extern const ChunkHandler _engine_chunk_handlers[]; truelight@0: extern const ChunkHandler _economy_chunk_handlers[]; truelight@0: extern const ChunkHandler _animated_tile_chunk_handlers[]; truelight@0: truelight@0: static const ChunkHandler * const _chunk_handlers[] = { truelight@0: _misc_chunk_handlers, truelight@0: _veh_chunk_handlers, truelight@0: _industry_chunk_handlers, truelight@0: _economy_chunk_handlers, truelight@0: _engine_chunk_handlers, truelight@0: _town_chunk_handlers, truelight@0: _sign_chunk_handlers, truelight@0: _station_chunk_handlers, truelight@0: _player_chunk_handlers, truelight@0: _animated_tile_chunk_handlers, truelight@0: NULL, truelight@0: }; truelight@0: truelight@0: // used to include a vehicle desc in another desc. truelight@0: extern const byte _common_veh_desc[]; truelight@0: static const byte * const _desc_includes[] = { truelight@0: _common_veh_desc truelight@0: }; truelight@0: truelight@0: typedef struct { truelight@0: void *base; truelight@0: size_t size; truelight@0: } ReferenceSetup; truelight@0: truelight@0: // used to translate "pointers" truelight@0: static const ReferenceSetup _ref_setup[] = { truelight@0: {_order_array,sizeof(_order_array[0])}, truelight@0: {_vehicles,sizeof(_vehicles[0])}, truelight@0: {_stations,sizeof(_stations[0])}, truelight@0: {_towns,sizeof(_towns[0])}, truelight@0: }; truelight@0: tron@410: static uint ReferenceToInt(void *v, uint t) truelight@0: { truelight@0: if (v == NULL) return 0; truelight@0: return ((byte*)v - (byte*)_ref_setup[t].base) / _ref_setup[t].size + 1; truelight@0: } truelight@0: truelight@0: void *IntToReference(uint r, uint t) truelight@0: { truelight@0: if (r == 0) return NULL; truelight@0: return (byte*)_ref_setup[t].base + (r-1) * _ref_setup[t].size; truelight@0: } truelight@0: truelight@0: typedef struct { truelight@0: const char *name; truelight@0: uint32 tag; truelight@0: truelight@0: bool (*init_read)(); truelight@0: ReaderProc *reader; truelight@0: void (*uninit_read)(); truelight@0: truelight@0: bool (*init_write)(); truelight@0: WriterProc *writer; truelight@0: void (*uninit_write)(); truelight@0: truelight@0: } SaveLoadFormat; truelight@0: truelight@0: static const SaveLoadFormat _saveload_formats[] = { truelight@0: {"lzo", TO_BE32X('OTTD'), InitLZO,ReadLZO, UninitLZO, InitLZO, WriteLZO, UninitLZO}, truelight@0: {"none", TO_BE32X('OTTN'), InitNoComp,ReadNoComp, UninitNoComp, InitNoComp, WriteNoComp, UninitNoComp}, truelight@0: #if defined(WITH_ZLIB) truelight@0: {"zlib", TO_BE32X('OTTZ'), InitReadZlib,ReadZlib, UninitReadZlib, InitWriteZlib, WriteZlib, UninitWriteZlib}, truelight@0: #else truelight@0: {"zlib", TO_BE32X('OTTZ'), NULL,NULL,NULL,NULL,NULL,NULL} truelight@0: #endif truelight@0: }; truelight@0: truelight@0: static const SaveLoadFormat *GetSavegameFormat(const char *s) truelight@0: { truelight@0: const SaveLoadFormat *def; truelight@0: int i; truelight@0: truelight@0: // find default savegame format truelight@0: def = endof(_saveload_formats) - 1; truelight@0: while (!def->init_write) def--; truelight@0: truelight@0: if (_savegame_format[0]) { truelight@0: for(i = 0; i!=lengthof(_saveload_formats); i++) truelight@0: if (_saveload_formats[i].init_write && !strcmp(s, _saveload_formats[i].name)) truelight@0: return _saveload_formats + i; truelight@0: truelight@0: ShowInfoF("Savegame format '%s' is not available. Reverting to '%s'.", s, def->name); truelight@0: } truelight@0: return def; truelight@0: } truelight@0: truelight@0: // actual loader/saver function truelight@0: extern void InitializeGame(); truelight@0: extern bool AfterLoadGame(uint version); truelight@0: extern void BeforeSaveGame(); truelight@0: extern bool LoadOldSaveGame(const char *file); truelight@0: truelight@0: // Save or Load files SL_LOAD, SL_SAVE, SL_OLD_LOAD truelight@0: int SaveOrLoad(const char *filename, int mode) truelight@0: { truelight@0: uint32 hdr[2]; truelight@0: const SaveLoadFormat *fmt; truelight@0: uint version; truelight@193: truelight@0: // old style load truelight@0: if (mode == SL_OLD_LOAD) { truelight@0: InitializeGame(); truelight@0: if (!LoadOldSaveGame(filename)) return SL_REINIT; truelight@0: AfterLoadGame(0); truelight@0: return SL_OK; truelight@0: } truelight@193: truelight@0: _sl.fh = fopen(filename, mode?"wb":"rb"); truelight@0: if (_sl.fh == NULL) truelight@0: return SL_ERROR; truelight@193: truelight@0: _sl.bufe = _sl.bufp = NULL; truelight@0: _sl.offs_base = 0; truelight@0: _sl.int_to_ref_proc = IntToReference; truelight@0: _sl.ref_to_int_proc = ReferenceToInt; truelight@0: _sl.save = mode; truelight@0: _sl.includes = _desc_includes; truelight@0: _sl.chs = _chunk_handlers; truelight@0: truelight@0: // setup setjmp error handler truelight@0: if (setjmp(_sl.excpt)) { truelight@0: // close file handle. truelight@0: fclose(_sl.fh); _sl.fh = NULL; truelight@0: truelight@0: // deinitialize compressor. truelight@0: _sl.excpt_uninit(); truelight@0: truelight@0: // a saver/loader exception!! truelight@0: // reinitialize all variables to prevent crash! truelight@0: if (mode == SL_LOAD) { truelight@0: ShowInfoF("Load game failed: %s.", _sl.excpt_msg); truelight@0: return SL_REINIT; truelight@0: } else { truelight@0: ShowInfoF("Save game failed: %s.", _sl.excpt_msg); truelight@0: return SL_ERROR; truelight@0: } truelight@0: } truelight@0: truelight@193: // we first initialize here to avoid: "warning: variable `version' might truelight@0: // be clobbered by `longjmp' or `vfork'" truelight@0: version = 0; truelight@0: truelight@0: if (mode != SL_LOAD) { truelight@0: fmt = GetSavegameFormat(_savegame_format); truelight@193: truelight@0: _sl.write_bytes = fmt->writer; truelight@0: _sl.excpt_uninit = fmt->uninit_write; truelight@0: if (!fmt->init_write()) goto init_err; truelight@193: truelight@0: hdr[0] = fmt->tag; truelight@0: hdr[1] = TO_BE32((SAVEGAME_MAJOR_VERSION<<16) + (SAVEGAME_MINOR_VERSION << 8)); truelight@0: if (fwrite(hdr, sizeof(hdr), 1, _sl.fh) != 1) SlError("file write failed"); truelight@193: truelight@0: _sl.version = SAVEGAME_MAJOR_VERSION; truelight@193: truelight@0: BeforeSaveGame(); truelight@0: SlSaveChunks(); truelight@0: SlWriteFill(); // flush the save buffer truelight@0: fmt->uninit_write(); truelight@0: truelight@0: } else { truelight@0: if (fread(hdr, sizeof(hdr), 1, _sl.fh) != 1) { truelight@0: read_err: truelight@0: printf("Savegame is obsolete or invalid format.\n"); truelight@0: init_err: truelight@0: fclose(_sl.fh); truelight@0: return SL_ERROR; truelight@0: } truelight@0: truelight@0: // see if we have any loader for this type. truelight@0: for(fmt = _saveload_formats; ; fmt++) { truelight@0: if (fmt == endof(_saveload_formats)) { truelight@0: printf("Unknown savegame type, trying to load it as the buggy format.\n"); truelight@0: rewind(_sl.fh); truelight@0: _sl.version = 0; truelight@0: version = 0; truelight@0: fmt = _saveload_formats + 0; // lzo truelight@0: break; truelight@0: } truelight@0: if (fmt->tag == hdr[0]) { truelight@0: // check version number truelight@0: version = TO_BE32(hdr[1]) >> 8; truelight@193: truelight@0: // incompatible version? truelight@0: if (version > SAVEGAME_LOADABLE_VERSION) goto read_err; truelight@0: _sl.version = (version>>8); truelight@0: break; truelight@0: } truelight@0: } truelight@0: truelight@0: _sl.read_bytes = fmt->reader; truelight@0: _sl.excpt_uninit = fmt->uninit_read; truelight@0: truelight@0: // loader for this savegame type is not implemented? truelight@0: if (fmt->init_read == NULL) { truelight@0: ShowInfoF("Loader for '%s' is not available.", fmt->name); truelight@0: fclose(_sl.fh); truelight@0: return SL_ERROR; truelight@0: } truelight@0: truelight@0: if (!fmt->init_read()) goto init_err; truelight@0: // Clear everything truelight@0: InitializeGame(); truelight@0: SlLoadChunks(); truelight@0: fmt->uninit_read(); truelight@0: } truelight@0: truelight@0: fclose(_sl.fh); truelight@0: truelight@0: if (mode == SL_LOAD) { truelight@0: if (!AfterLoadGame(version)) truelight@0: return SL_REINIT; truelight@0: } truelight@0: truelight@0: return SL_OK; truelight@0: } truelight@0: truelight@0: bool EmergencySave() truelight@0: { truelight@0: SaveOrLoad("crash.sav", SL_SAVE); truelight@0: return true; truelight@0: } truelight@0: dominik@643: void DoExitSave() dominik@643: { dominik@643: char buf[200]; dominik@643: sprintf(buf, "%s%sexit.sav", _path.autosave_dir, PATHSEP); dominik@643: debug(buf); dominik@643: SaveOrLoad(buf, SL_SAVE); dominik@643: } dominik@643: truelight@0: // not used right now, but could be used if extensions of savegames are garbled truelight@0: /*int GetSavegameType(char *file) truelight@0: { truelight@0: const SaveLoadFormat *fmt; truelight@0: uint32 hdr; truelight@0: FILE *f; truelight@0: int mode = SL_OLD_LOAD; truelight@0: truelight@0: f = fopen(file, "rb"); truelight@0: if (fread(&hdr, sizeof(hdr), 1, f) != 1) { truelight@0: printf("Savegame is obsolete or invalid format.\n"); truelight@0: mode = SL_LOAD; // don't try to get filename, just show name as it is written truelight@0: } truelight@0: else { truelight@0: // see if we have any loader for this type. truelight@0: for (fmt = _saveload_formats; fmt != endof(_saveload_formats); fmt++) { truelight@0: if (fmt->tag == hdr) { truelight@0: mode = SL_LOAD; // new type of savegame truelight@0: break; truelight@0: } truelight@0: } truelight@0: } truelight@0: truelight@0: fclose(f); truelight@0: return mode; truelight@0: }*/