saveload.c
changeset 0 29654efe3188
child 121 c2f18f4d8be1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/saveload.c	Mon Aug 09 17:04:08 2004 +0000
@@ -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;
+}*/