src/saveload.cpp
branchnoai
changeset 9629 66dde6412125
parent 9628 b5c2449616b5
child 9631 8a2d1c2ceb88
--- a/src/saveload.cpp	Sun Jun 17 21:31:00 2007 +0000
+++ b/src/saveload.cpp	Tue Jun 26 23:40:58 2007 +0000
@@ -26,10 +26,12 @@
 #include "saveload.h"
 #include "network/network.h"
 #include "variables.h"
+#include "table/strings.h"
+#include "strings.h"
 #include <setjmp.h>
 #include <list>
 
-extern const uint16 SAVEGAME_VERSION = 64;
+extern const uint16 SAVEGAME_VERSION = 69;
 uint16 _sl_version;       ///< the major savegame version identifier
 byte   _sl_minor_version; ///< the minor savegame version, DO NOT USE!
 
@@ -65,7 +67,8 @@
 	FILE *fh;                            ///< the file from which is read or written to
 
 	void (*excpt_uninit)();              ///< the function to execute on any encountered error
-	const char *excpt_msg;               ///< the error message
+	StringID error_str;                  ///< the translateable error message to show
+	char *extra_msg;                     ///< the error message
 	jmp_buf excpt;                       ///< @todo used to jump to "exception handler";  really ugly
 } _sl;
 
@@ -112,7 +115,7 @@
 }
 
 /** Return the size in bytes of a reference (pointer) */
-static inline size_t SlCalcRefLen() {return 2;}
+static inline size_t SlCalcRefLen() {return CheckSavegameVersion(69) ? 2 : 4;}
 
 /** Flush the output buffer by writing to disk with the given reader.
  * If the buffer pointer has not yet been set up, set it up now. Usually
@@ -136,9 +139,11 @@
 /** Error handler, calls longjmp to simulate an exception.
  * @todo this was used to have a central place to handle errors, but it is
  * pretty ugly, and seriously interferes with any multithreaded approaches */
-static void NORETURN SlError(const char *msg)
+static void NORETURN SlError(StringID string, const char *extra_msg = NULL)
 {
-	_sl.excpt_msg = msg;
+	_sl.error_str = string;
+	free(_sl.extra_msg);
+	_sl.extra_msg = (extra_msg == NULL) ? NULL : strdup(extra_msg);
 	longjmp(_sl.excpt, 0);
 }
 
@@ -224,7 +229,7 @@
 			if (HASBIT(i, 5)) {
 				i &= ~0x20;
 				if (HASBIT(i, 4))
-					SlError("Unsupported gamma");
+					SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Unsupported gamma");
 				i = (i << 8) | SlReadByte();
 			}
 			i = (i << 8) | SlReadByte();
@@ -641,9 +646,10 @@
 {
 	std::list<void *> *l = (std::list<void *> *) list;
 
-	/* Each entry is saved as 2 bytes, plus 2 bytes are used for the length
+	int type_size = CheckSavegameVersion(69) ? 2 : 4;
+	/* Each entry is saved as type_size bytes, plus type_size bytes are used for the length
 	 * of the list */
-	return l->size() * 2 + 2;
+	return l->size() * type_size + type_size;
 }
 
 
@@ -664,19 +670,19 @@
 	std::list<void *> *l = (std::list<void *> *) list;
 
 	if (_sl.save) {
-		SlWriteUint16(l->size());
+		SlWriteUint32(l->size());
 
 		std::list<void *>::iterator iter;
 		for (iter = l->begin(); iter != l->end(); ++iter) {
 			void *ptr = *iter;
-			SlWriteUint16(ReferenceToInt(ptr, conv));
+			SlWriteUint32(ReferenceToInt(ptr, conv));
 		}
 	} else {
-		uint length = SlReadUint16();
+		uint length = CheckSavegameVersion(69) ? SlReadUint16() : SlReadUint32();
 
 		/* Load each reference and push to the end of the list */
 		for (uint i = 0; i < length; i++) {
-			void *ptr = IntToReference(SlReadUint16(), conv);
+			void *ptr = IntToReference(CheckSavegameVersion(69) ? SlReadUint16() : SlReadUint32(), conv);
 			l->push_back(ptr);
 		}
 	}
@@ -768,11 +774,10 @@
 		switch (sld->cmd) {
 		case SL_VAR: SlSaveLoadConv(ptr, conv); break;
 		case SL_REF: // Reference variable, translate
-			/* @todo XXX - another artificial limitof 65K elements of pointers? */
-			if (_sl.save) { // XXX - read/write pointer as uint16? What is with higher indeces?
-				SlWriteUint16(ReferenceToInt(*(void**)ptr, (SLRefType)conv));
+			if (_sl.save) {
+				SlWriteUint32(ReferenceToInt(*(void**)ptr, (SLRefType)conv));
 			} else {
-				*(void**)ptr = IntToReference(SlReadUint16(), (SLRefType)conv);
+				*(void**)ptr = IntToReference(CheckSavegameVersion(69) ? SlReadUint16() : SlReadUint32(), (SLRefType)conv);
 			}
 			break;
 		case SL_ARR: SlArray(ptr, sld->length, conv); break;
@@ -820,7 +825,7 @@
 	}
 
 	for (; sld->cmd != SL_END; sld++) {
-		void *ptr = GetVariableAddress(object, sld);
+		void *ptr = sld->global ? sld->address : GetVariableAddress(object, sld);
 		SlObjectMember(ptr, sld);
 	}
 }
@@ -831,14 +836,7 @@
  */
 void SlGlobList(const SaveLoadGlobVarList *sldg)
 {
-	if (_sl.need_length != NL_NONE) {
-		SlSetLength(SlCalcObjLength(NULL, (const SaveLoad*)sldg));
-		if (_sl.need_length == NL_CALCLENGTH) return;
-	}
-
-	for (; sldg->cmd != SL_END; sldg++) {
-		SlObjectMember(sldg->address, (const SaveLoad*)sldg);
-	}
+	SlObject(NULL, (const SaveLoad*)sldg);
 }
 
 /**
@@ -866,7 +864,7 @@
 	/* And write the stuff */
 	proc(arg);
 
-	assert(offs == SlGetOffs());
+	if (offs != SlGetOffs()) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Invalid chunk size");
 }
 
 /**
@@ -898,9 +896,9 @@
 			_sl.obj_len = len;
 			endoffs = SlGetOffs() + len;
 			ch->load_proc();
-			assert(SlGetOffs() == endoffs);
+			if (SlGetOffs() != endoffs) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Invalid chunk size");
 		} else {
-			SlError("Invalid chunk type");
+			SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Invalid chunk type");
 		}
 		break;
 	}
@@ -1001,7 +999,7 @@
 		DEBUG(sl, 2, "Loading chunk %c%c%c%c", id >> 24, id >> 16, id >> 8, id);
 
 		ch = SlFindChunkHandler(id);
-		if (ch == NULL) SlError("found unknown tag in savegame (sync error)");
+		if (ch == NULL) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Unknown chunk type");
 		SlLoadChunk(ch);
 	}
 }
@@ -1021,7 +1019,7 @@
 	uint len;
 
 	/* Read header*/
-	if (fread(tmp, sizeof(tmp), 1, _sl.fh) != 1) SlError("file read failed");
+	if (fread(tmp, sizeof(tmp), 1, _sl.fh) != 1) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE, "File read failed");
 
 	/* Check if size is bad */
 	((uint32*)out)[0] = size = tmp[1];
@@ -1031,13 +1029,13 @@
 		size = TO_BE32(size);
 	}
 
-	if (size >= sizeof(out)) SlError("inconsistent size");
+	if (size >= sizeof(out)) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Inconsistent size");
 
 	/* Read block */
-	if (fread(out + sizeof(uint32), size, 1, _sl.fh) != 1) SlError("file read failed");
+	if (fread(out + sizeof(uint32), size, 1, _sl.fh) != 1) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
 
 	/* Verify checksum */
-	if (tmp[0] != lzo_adler32(0, out, size + sizeof(uint32))) SlError("bad checksum");
+	if (tmp[0] != lzo_adler32(0, out, size + sizeof(uint32))) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Bad checksum");
 
 	/* Decompress */
 	lzo1x_decompress(out + sizeof(uint32)*1, size, _sl.buf, &len, NULL);
@@ -1055,7 +1053,7 @@
 	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");
+	if (fwrite(out, outlen + sizeof(uint32)*2, 1, _sl.fh) != 1) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE);
 }
 
 static bool InitLZO()
@@ -1099,7 +1097,6 @@
  ********** START OF MEMORY CODE (in ram)****
  ********************************************/
 
-#include "table/strings.h"
 #include "table/sprites.h"
 #include "gfx.h"
 #include "gui.h"
@@ -1178,8 +1175,7 @@
 		if (r == Z_STREAM_END)
 			break;
 
-		if (r != Z_OK)
-			SlError("inflate() failed");
+		if (r != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "inflate() failed");
 	} while (_z.avail_out);
 
 	return 4096 - _z.avail_out;
@@ -1214,11 +1210,11 @@
 		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 (fwrite(buf, n, 1, _sl.fh) != 1) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE);
 		}
 		if (r == Z_STREAM_END)
 			break;
-		if (r != Z_OK) SlError("zlib returned error code");
+		if (r != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "zlib returned error code");
 	} while (z->avail_in || !z->avail_out);
 }
 
@@ -1258,6 +1254,7 @@
 extern const ChunkHandler _animated_tile_chunk_handlers[];
 extern const ChunkHandler _newgrf_chunk_handlers[];
 extern const ChunkHandler _group_chunk_handlers[];
+extern const ChunkHandler _cargopacket_chunk_handlers[];
 
 static const ChunkHandler * const _chunk_handlers[] = {
 	_misc_chunk_handlers,
@@ -1276,6 +1273,7 @@
 	_animated_tile_chunk_handlers,
 	_newgrf_chunk_handlers,
 	_group_chunk_handlers,
+	_cargopacket_chunk_handlers,
 	NULL,
 };
 
@@ -1307,6 +1305,7 @@
 		case REF_ORDER:     return ((const    Order*)obj)->index + 1;
 		case REF_ROADSTOPS: return ((const RoadStop*)obj)->index + 1;
 		case REF_ENGINE_RENEWS: return ((const EngineRenew*)obj)->index + 1;
+		case REF_CARGO_PACKET:  return ((const CargoPacket*)obj)->index + 1;
 		default: NOT_REACHED();
 	}
 
@@ -1367,6 +1366,11 @@
 				error("EngineRenews: failed loading savegame: too many EngineRenews");
 			return GetEngineRenew(index);
 		}
+		case REF_CARGO_PACKET: {
+			if (!AddBlockIfNeeded(&_CargoPacket_pool, index))
+				error("CargoPackets: failed loading savegame: too many Cargo packets");
+			return GetCargoPacket(index);
+		}
 
 		case REF_VEHICLE_OLD: {
 			/* Old vehicles were saved differently:
@@ -1475,10 +1479,28 @@
 	_ts.saveinprogress = false;
 }
 
+/** Set the error message from outside of the actual loading/saving of the game (AfterLoadGame and friends) */
+void SetSaveLoadError(StringID str)
+{
+	_sl.error_str = str;
+}
+
+/** Get the string representation of the error message */
+const char *GetSaveLoadErrorString()
+{
+	SetDParam(0, _sl.error_str);
+	SetDParamStr(1, _sl.extra_msg);
+
+	static char err_str[512];
+	GetString(err_str, _sl.save ? STR_4007_GAME_SAVE_FAILED : STR_4009_GAME_LOAD_FAILED, lastof(err_str));
+	return err_str;
+}
+
 /** Show a gui message when saving has failed */
 void SaveFileError()
 {
-	ShowErrorMessage(STR_4007_GAME_SAVE_FAILED, STR_NULL, 0, 0);
+	SetDParamStr(0, GetSaveLoadErrorString());
+	ShowErrorMessage(STR_012D, STR_NULL, 0, 0);
 	SaveFileDone();
 }
 
@@ -1492,13 +1514,16 @@
 	const SaveLoadFormat *fmt;
 	uint32 hdr[2];
 
+	_sl.excpt_uninit = NULL;
 	/* XXX - Setup setjmp error handler if an error occurs anywhere deep during
 	 * loading/saving execute a longjmp() and continue execution here */
 	if (setjmp(_sl.excpt)) {
 		AbortSaveLoad();
-		_sl.excpt_uninit();
+		if (_sl.excpt_uninit != NULL) _sl.excpt_uninit();
 
-		fprintf(stderr, "Save game failed: %s.", _sl.excpt_msg);
+		ShowInfo(GetSaveLoadErrorString());
+		fprintf(stderr, GetSaveLoadErrorString());
+
 		if (threaded) {
 			OTTD_SendThreadMessage(MSG_OTTD_SAVETHREAD_ERROR);
 		} else {
@@ -1512,9 +1537,9 @@
 	/* We have written our stuff to memory, now write it to file! */
 	hdr[0] = fmt->tag;
 	hdr[1] = TO_BE32(SAVEGAME_VERSION << 16);
-	if (fwrite(hdr, sizeof(hdr), 1, _sl.fh) != 1) SlError("file write failed");
+	if (fwrite(hdr, sizeof(hdr), 1, _sl.fh) != 1) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE);
 
-	if (!fmt->init_write()) SlError("cannot initialize compressor");
+	if (!fmt->init_write()) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor");
 
 	{
 		uint i;
@@ -1583,6 +1608,22 @@
 		return SL_OK;
 	}
 
+	/* XXX - Setup setjmp error handler if an error occurs anywhere deep during
+	 * loading/saving execute a longjmp() and continue execution here */
+	_sl.excpt_uninit = NULL;
+	if (setjmp(_sl.excpt)) {
+		AbortSaveLoad();
+
+		/* deinitialize compressor. */
+		if (_sl.excpt_uninit != NULL) _sl.excpt_uninit();
+
+		/* Skip the "color" character */
+		ShowInfoF(GetSaveLoadErrorString() + 3);
+
+		/* A saver/loader exception!! reinitialize all variables to prevent crash! */
+		return (mode == SL_LOAD) ? SL_REINIT : SL_ERROR;
+	}
+
 	_sl.fh = (mode == SL_SAVE) ? FioFOpenFile(filename, "wb", sb) : FioFOpenFile(filename, "rb", sb);
 
 	/* Make it a little easier to load savegames from the console */
@@ -1590,8 +1631,7 @@
 	if (_sl.fh == NULL && mode == SL_LOAD) _sl.fh = FioFOpenFile(filename, "rb", BASE_DIR);
 
 	if (_sl.fh == NULL) {
-		DEBUG(sl, 0, "Cannot open savegame '%s' for saving/loading.", filename);
-		return SL_ERROR;
+		SlError(mode == SL_SAVE ? STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE : STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
 	}
 
 	_sl.bufe = _sl.bufp = NULL;
@@ -1600,24 +1640,6 @@
 	_sl.includes = _desc_includes;
 	_sl.chs = _chunk_handlers;
 
-	/* XXX - Setup setjmp error handler if an error occurs anywhere deep during
-	 * loading/saving execute a longjmp() and continue execution here */
-	if (setjmp(_sl.excpt)) {
-		AbortSaveLoad();
-
-		/* 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;
-		}
-
-		ShowInfoF("Save game failed: %s.", _sl.excpt_msg);
-		return SL_ERROR;
-	}
-
 	/* General tactic is to first save the game to memory, then use an available writer
 	 * to write it to file, either in threaded mode if possible, or single-threaded */
 	if (mode == SL_SAVE) { /* SAVE game */
@@ -1649,10 +1671,7 @@
 	} else { /* LOAD game */
 		assert(mode == SL_LOAD);
 
-		if (fread(hdr, sizeof(hdr), 1, _sl.fh) != 1) {
-			DEBUG(sl, 0, "Cannot read savegame header, aborting");
-			return AbortSaveLoad();
-		}
+		if (fread(hdr, sizeof(hdr), 1, _sl.fh) != 1) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
 
 		/* see if we have any loader for this type. */
 		for (fmt = _saveload_formats; ; fmt++) {
@@ -1684,10 +1703,7 @@
 				DEBUG(sl, 1, "Loading savegame version %d", _sl_version);
 
 				/* Is the version higher than the current? */
-				if (_sl_version > SAVEGAME_VERSION) {
-					DEBUG(sl, 0, "Savegame version invalid");
-					return AbortSaveLoad();
-				}
+				if (_sl_version > SAVEGAME_VERSION) SlError(STR_GAME_SAVELOAD_ERROR_TOO_NEW_SAVEGAME);
 				break;
 			}
 		}
@@ -1697,13 +1713,15 @@
 
 		/* loader for this savegame type is not implemented? */
 		if (fmt->init_read == NULL) {
-			ShowInfoF("Loader for '%s' is not available.", fmt->name);
-			return AbortSaveLoad();
+			char err_str[64];
+			snprintf(err_str, lengthof(err_str), "Loader for '%s' is not available.", fmt->name);
+			SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, err_str);
 		}
 
 		if (!fmt->init_read()) {
-			DEBUG(sl, 0, "Initializing loader '%s' failed", fmt->name);
-			return AbortSaveLoad();
+			char err_str[64];
+			snprintf(err_str, lengthof(err_str), "Initializing loader '%s' failed", fmt->name);
+			SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, err_str);
 		}
 
 		/* Old maps were hardcoded to 256x256 and thus did not contain