changeset 16123:a73c48c80b5d draft

(svn r20819) -Fix [FS#3714]: be a bit more "lenient" w.r.t. invalid savegames; don't crash on saveload related NOT_REACHEDs, just show the user an error that the savegame is corrupted
author rubidium <rubidium@openttd.org>
date Thu, 16 Sep 2010 22:06:36 +0000
parents 9994b0118198
children 1a876551f6d7
files src/saveload/afterload.cpp src/saveload/ai_sl.cpp src/saveload/oldloader_sl.cpp src/saveload/saveload.cpp src/saveload/saveload.h src/saveload/vehicle_sl.cpp
diffstat 6 files changed, 46 insertions(+), 32 deletions(-) [+]
line wrap: on
line diff
--- a/src/saveload/afterload.cpp
+++ b/src/saveload/afterload.cpp
@@ -79,7 +79,7 @@
 			SetWaterClass(t, WATER_CLASS_INVALID);
 			return;
 		} else {
-			NOT_REACHED();
+			SlErrorCorrupt("Invalid water class for dry tile");
 		}
 	}
 
@@ -108,7 +108,7 @@
 						case WATER_CLASS_SEA:   has_water = true; break;
 						case WATER_CLASS_CANAL: has_canal = true; break;
 						case WATER_CLASS_RIVER: has_river = true; break;
-						default: NOT_REACHED();
+						default: SlErrorCorrupt("Invalid water class for tile");
 					}
 				}
 				break;
@@ -871,7 +871,7 @@
 				case MP_ROAD:
 					SB(_m[t].m5, 6, 2, GB(_m[t].m5, 4, 2));
 					switch (GetRoadTileType(t)) {
-						default: NOT_REACHED();
+						default: SlErrorCorrupt("Invalid road tile type");
 						case ROAD_TILE_NORMAL:
 							SB(_m[t].m4, 0, 4, GB(_m[t].m5, 0, 4));
 							SB(_m[t].m4, 4, 4, 0);
@@ -912,7 +912,7 @@
 					if (fix_roadtypes) SetRoadTypes(t, (RoadTypes)GB(_me[t].m7, 5, 3));
 					SB(_me[t].m7, 5, 1, GB(_m[t].m3, 7, 1)); // snow/desert
 					switch (GetRoadTileType(t)) {
-						default: NOT_REACHED();
+						default: SlErrorCorrupt("Invalid road tile type");
 						case ROAD_TILE_NORMAL:
 							SB(_me[t].m7, 0, 4, GB(_m[t].m3, 0, 4)); // road works
 							SB(_m[t].m6, 3, 3, GB(_m[t].m3, 4, 3));  // ground
@@ -1031,7 +1031,7 @@
 
 				if (dir != DirToDiagDir(v->direction)) continue;
 				switch (dir) {
-					default: NOT_REACHED();
+					default: SlErrorCorrupt("Invalid vehicle direction");
 					case DIAGDIR_NE: if ((v->x_pos & 0xF) !=  0)            continue; break;
 					case DIAGDIR_SE: if ((v->y_pos & 0xF) != TILE_SIZE - 1) continue; break;
 					case DIAGDIR_SW: if ((v->x_pos & 0xF) != TILE_SIZE - 1) continue; break;
--- a/src/saveload/ai_sl.cpp
+++ b/src/saveload/ai_sl.cpp
@@ -178,7 +178,7 @@
 		case SQSL_ARRAY_TABLE_END:
 			return false;
 
-		default: NOT_REACHED();
+		default: SlErrorCorrupt("Invalid AI data type");
 	}
 }
 
--- a/src/saveload/oldloader_sl.cpp
+++ b/src/saveload/oldloader_sl.cpp
@@ -1095,7 +1095,7 @@
 		res = LoadChunk(ls, NULL, vehicle_empty_chunk);
 	} else {
 		switch (v->type) {
-			default: NOT_REACHED();
+			default: SlErrorCorrupt("Invalid vehicle type");
 			case VEH_TRAIN   : res = LoadChunk(ls, v, vehicle_train_chunk);    break;
 			case VEH_ROAD    : res = LoadChunk(ls, v, vehicle_road_chunk);     break;
 			case VEH_SHIP    : res = LoadChunk(ls, v, vehicle_ship_chunk);     break;
@@ -1295,7 +1295,7 @@
 		} else {
 			/* Read the vehicle type and allocate the right vehicle */
 			switch (ReadByte(ls)) {
-				default: NOT_REACHED();
+				default: SlErrorCorrupt("Invalid vehicle type");
 				case 0x00 /* VEH_INVALID */: v = NULL;                                        break;
 				case 0x10 /* VEH_TRAIN   */: v = new (_current_vehicle_id) Train();           break;
 				case 0x11 /* VEH_ROAD    */: v = new (_current_vehicle_id) RoadVehicle();     break;
--- a/src/saveload/saveload.cpp
+++ b/src/saveload/saveload.cpp
@@ -387,6 +387,19 @@
 	throw std::exception();
 }
 
+/**
+ * Error handler for corrupt savegames. Sets everything up to show the
+ * error message and to clean up the mess of a partial savegame load.
+ * @param msg Location the corruption has been spotted.
+ * @note This function does never return as it throws an exception to
+ *       break out of all the saveload code.
+ */
+void NORETURN SlErrorCorrupt(const char *msg)
+{
+	SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, msg);
+}
+
+
 typedef void (*AsyncSaveFinishProc)();                ///< Callback for when the savegame loading is finished.
 static AsyncSaveFinishProc _async_save_finish = NULL; ///< Callback to call when the savegame loading is finished.
 static ThreadObject *_save_thread;                    ///< The thread we're using to compress and write a savegame
@@ -427,7 +440,7 @@
 static void SlReadFill()
 {
 	size_t len = _sl.read_bytes();
-	if (len == 0) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Unexpected end of chunk");
+	if (len == 0) SlErrorCorrupt("Unexpected end of chunk");
 
 	_sl.bufp = _sl.buf;
 	_sl.bufe = _sl.buf + len;
@@ -553,7 +566,7 @@
 			if (HasBit(i, 5)) {
 				i &= ~0x20;
 				if (HasBit(i, 4)) {
-					SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Unsupported gamma");
+					SlErrorCorrupt("Unsupported gamma");
 				}
 				i = (i << 8) | SlReadByte();
 			}
@@ -688,7 +701,7 @@
 
 	/* After reading in the whole array inside the loop
 	 * we must have read in all the data, so we must be at end of current block. */
-	if (_next_offs != 0 && SlGetOffs() != _next_offs) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Invalid chunk size");
+	if (_next_offs != 0 && SlGetOffs() != _next_offs) SlErrorCorrupt("Invalid chunk size");
 
 	while (true) {
 		uint length = SlReadArrayLength();
@@ -1136,38 +1149,38 @@
 	switch (rt) {
 		case REF_ORDERLIST:
 			if (OrderList::IsValidID(index)) return OrderList::Get(index);
-			SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Referencing invalid OrderList");
+			SlErrorCorrupt("Referencing invalid OrderList");
 
 		case REF_ORDER:
 			if (Order::IsValidID(index)) return Order::Get(index);
 			/* in old versions, invalid order was used to mark end of order list */
 			if (CheckSavegameVersionOldStyle(5, 2)) return NULL;
-			SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Referencing invalid Order");
+			SlErrorCorrupt("Referencing invalid Order");
 
 		case REF_VEHICLE_OLD:
 		case REF_VEHICLE:
 			if (Vehicle::IsValidID(index)) return Vehicle::Get(index);
-			SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Referencing invalid Vehicle");
+			SlErrorCorrupt("Referencing invalid Vehicle");
 
 		case REF_STATION:
 			if (Station::IsValidID(index)) return Station::Get(index);
-			SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Referencing invalid Station");
+			SlErrorCorrupt("Referencing invalid Station");
 
 		case REF_TOWN:
 			if (Town::IsValidID(index)) return Town::Get(index);
-			SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Referencing invalid Town");
+			SlErrorCorrupt("Referencing invalid Town");
 
 		case REF_ROADSTOPS:
 			if (RoadStop::IsValidID(index)) return RoadStop::Get(index);
-			SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Referencing invalid RoadStop");
+			SlErrorCorrupt("Referencing invalid RoadStop");
 
 		case REF_ENGINE_RENEWS:
 			if (EngineRenew::IsValidID(index)) return EngineRenew::Get(index);
-			SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Referencing invalid EngineRenew");
+			SlErrorCorrupt("Referencing invalid EngineRenew");
 
 		case REF_CARGO_PACKET:
 			if (CargoPacket::IsValidID(index)) return CargoPacket::Get(index);
-			SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Referencing invalid CargoPacket");
+			SlErrorCorrupt("Referencing invalid CargoPacket");
 
 		default: NOT_REACHED();
 	}
@@ -1441,7 +1454,7 @@
 	/* And write the stuff */
 	proc(arg);
 
-	if (offs != SlGetOffs()) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Invalid chunk size");
+	if (offs != SlGetOffs()) SlErrorCorrupt("Invalid chunk size");
 }
 
 /**
@@ -1473,9 +1486,9 @@
 				_sl.obj_len = len;
 				endoffs = SlGetOffs() + len;
 				ch->load_proc();
-				if (SlGetOffs() != endoffs) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Invalid chunk size");
+				if (SlGetOffs() != endoffs) SlErrorCorrupt("Invalid chunk size");
 			} else {
-				SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Invalid chunk type");
+				SlErrorCorrupt("Invalid chunk type");
 			}
 			break;
 	}
@@ -1523,9 +1536,9 @@
 				} else {
 					SlSkipBytes(len);
 				}
-				if (SlGetOffs() != endoffs) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Invalid chunk size");
+				if (SlGetOffs() != endoffs) SlErrorCorrupt("Invalid chunk size");
 			} else {
-				SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Invalid chunk type");
+				SlErrorCorrupt("Invalid chunk type");
 			}
 			break;
 	}
@@ -1632,7 +1645,7 @@
 		DEBUG(sl, 2, "Loading chunk %c%c%c%c", id >> 24, id >> 16, id >> 8, id);
 
 		ch = SlFindChunkHandler(id);
-		if (ch == NULL) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Unknown chunk type");
+		if (ch == NULL) SlErrorCorrupt("Unknown chunk type");
 		SlLoadChunk(ch);
 	}
 }
@@ -1647,7 +1660,7 @@
 		DEBUG(sl, 2, "Loading chunk %c%c%c%c", id >> 24, id >> 16, id >> 8, id);
 
 		ch = SlFindChunkHandler(id);
-		if (ch == NULL) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Unknown chunk type");
+		if (ch == NULL) SlErrorCorrupt("Unknown chunk type");
 		SlLoadCheckChunk(ch);
 	}
 }
@@ -1700,13 +1713,13 @@
 		size = TO_BE32(size);
 	}
 
-	if (size >= sizeof(out)) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Inconsistent size");
+	if (size >= sizeof(out)) SlErrorCorrupt("Inconsistent size");
 
 	/* Read block */
 	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(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Bad checksum");
+	if (tmp[0] != lzo_adler32(0, out, size + sizeof(uint32))) SlErrorCorrupt("Bad checksum");
 
 	/* Decompress */
 	lzo1x_decompress(out + sizeof(uint32) * 1, size, _sl.buf, &len, NULL);
@@ -2099,7 +2112,7 @@
 		uint i = 0;
 		size_t t = _ts.count;
 
-		if (_ts.count != _sl.offs_base) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Unexpected size of chunk");
+		if (_ts.count != _sl.offs_base) SlErrorCorrupt("Unexpected size of chunk");
 		while (t >= MEMORY_CHUNK_SIZE) {
 			_sl.buf = _memory_savegame[i++];
 			fmt->writer(MEMORY_CHUNK_SIZE);
@@ -2116,7 +2129,7 @@
 		}
 
 		fmt->uninit_write();
-		if (_ts.count != _sl.offs_base) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Unexpected size of chunk");
+		if (_ts.count != _sl.offs_base) SlErrorCorrupt("Unexpected size of chunk");
 		UnInitMem();
 		fclose(_sl.fh);
 
--- a/src/saveload/saveload.h
+++ b/src/saveload/saveload.h
@@ -346,6 +346,7 @@
 void SlArray(void *array, size_t length, VarType conv);
 void SlObject(void *object, const SaveLoad *sld);
 bool SlObjectMember(void *object, const SaveLoad *sld);
+void NORETURN SlErrorCorrupt(const char *msg);
 
 bool SaveloadCrashWithMissingNewGRFs();
 
--- a/src/saveload/vehicle_sl.cpp
+++ b/src/saveload/vehicle_sl.cpp
@@ -150,7 +150,7 @@
 						u->SetWagon();
 						u->SetFreeWagon();
 						break;
-					default: NOT_REACHED();
+					default: SlErrorCorrupt("Invalid train subtype");
 				}
 			}
 		}
@@ -714,7 +714,7 @@
 			case VEH_EFFECT:   v = new (index) EffectVehicle();   break;
 			case VEH_DISASTER: v = new (index) DisasterVehicle(); break;
 			case VEH_INVALID: // Savegame shouldn't contain invalid vehicles
-			default: NOT_REACHED();
+			default: SlErrorCorrupt("Invalid vehicle type");
 		}
 
 		SlObject(v, GetVehicleDescription(vtype));