← Back to team overview

widelands-dev team mailing list archive

[Merge] lp:~widelands-dev/widelands/map_compatibility into lp:widelands

 

GunChleoc has proposed merging lp:~widelands-dev/widelands/map_compatibility into lp:widelands.

Requested reviews:
  Widelands Developers (widelands-dev)

For more details, see:
https://code.launchpad.net/~widelands-dev/widelands/map_compatibility/+merge/276088

There is a problem with loading older maps, so I put the remaining compatibility code for maps back in.

It was removed in r7558.
-- 
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/map_compatibility into lp:widelands.
=== modified file 'src/logic/expedition_bootstrap.cc'
--- src/logic/expedition_bootstrap.cc	2015-04-15 12:59:16 +0000
+++ src/logic/expedition_bootstrap.cc	2015-10-29 08:43:55 +0000
@@ -260,22 +260,34 @@
 	}
 }
 
-void ExpeditionBootstrap::load(Warehouse& warehouse, FileRead& fr,
-										 Game& game, MapObjectLoader& mol)
+constexpr uint16_t kCurrentPacketVersionWarehouse = 6;
+
+void ExpeditionBootstrap::load
+	(uint32_t warehouse_packet_version, Warehouse& warehouse, FileRead& fr,
+	 Game& game, MapObjectLoader& mol)
 {
-	// Expedition workers
-	const uint8_t num_workers = fr.unsigned_8();
-	for (uint8_t i = 0; i < num_workers; ++i) {
-		workers_.emplace_back(new ExpeditionWorker(nullptr));
-		if (fr.unsigned_8() == 1) {
-			Request* worker_request = new Request
-			  (warehouse, 0, ExpeditionBootstrap::worker_callback, wwWORKER);
-			workers_.back()->request.reset(worker_request);
-			worker_request->read(fr, game, mol);
-			workers_.back()->worker = nullptr;
+	try {
+		if (warehouse_packet_version >= kCurrentPacketVersionWarehouse) {
+			// Expedition workers
+			const uint8_t num_workers = fr.unsigned_8();
+			for (uint8_t i = 0; i < num_workers; ++i) {
+				workers_.emplace_back(new ExpeditionWorker(nullptr));
+				if (fr.unsigned_8() == 1) {
+					Request* worker_request = new Request
+					  (warehouse, 0, ExpeditionBootstrap::worker_callback, wwWORKER);
+					workers_.back()->request.reset(worker_request);
+					worker_request->read(fr, game, mol);
+					workers_.back()->worker = nullptr;
+				} else {
+					workers_.back()->worker = &mol.get<Worker>(fr.unsigned_32());
+				}
+			}
 		} else {
-			workers_.back()->worker = &mol.get<Worker>(fr.unsigned_32());
+			throw UnhandledVersionError("ExpeditionBootstrap",
+												 warehouse_packet_version, kCurrentPacketVersionWarehouse);
 		}
+	} catch (const WException & e) {
+		throw wexception("expedition bootstrap: %s", e.what());
 	}
 
 	// Expedition WaresQueues

=== modified file 'src/logic/expedition_bootstrap.h'
--- src/logic/expedition_bootstrap.h	2015-04-15 12:59:16 +0000
+++ src/logic/expedition_bootstrap.h	2015-10-29 08:43:55 +0000
@@ -75,8 +75,9 @@
 
 	// Save/Load this into a file. The actual data is stored in the buildingdata
 	// packet, and there in the warehouse data packet.
-	void load(Warehouse& warehouse, FileRead& fr,
-				 Game& game, MapObjectLoader& mol);
+	void load
+		(uint32_t warehouse_packet_version, Warehouse& warehouse,
+		 FileRead& fr, Game& game, MapObjectLoader& mol);
 	void save(FileWrite& fw, Game& game, MapObjectSaver& mos);
 
 private:

=== modified file 'src/map_io/map_buildingdata_packet.cc'
--- src/map_io/map_buildingdata_packet.cc	2015-10-24 15:42:37 +0000
+++ src/map_io/map_buildingdata_packet.cc	2015-10-29 08:43:55 +0000
@@ -52,10 +52,12 @@
 namespace Widelands {
 
 // Overall package version
+// Since V3: m_old_buildings vector
 constexpr uint16_t kCurrentPacketVersion = 4;
 
 // Building type package versions
 constexpr uint16_t kCurrentPacketVersionDismantlesite = 1;
+// Since V3: m_prev_building not written
 constexpr uint16_t kCurrentPacketVersionConstructionsite = 3;
 constexpr uint16_t kCurrentPacketPFBuilding = 1;
 constexpr uint16_t kCurrentPacketVersionWarehouse = 6;
@@ -77,9 +79,17 @@
 
 	try {
 		uint16_t const packet_version = fr.unsigned_16();
-		if (packet_version == kCurrentPacketVersion) {
-			while (! fr.end_of_file()) {
+		if (1 <= packet_version && packet_version <= kCurrentPacketVersion) {
+			for (;;) {
+				if (2 <= packet_version && fr.end_of_file())
+					break;
 				Serial const serial = fr.unsigned_32();
+				if (packet_version < 2 && serial == 0xffffffff) {
+					if (!fr.end_of_file())
+						throw GameDataError
+							("expected end of file after serial 0xffffffff");
+					break;
+				}
 				try {
 					Building & building = mol.get<Building>(serial);
 
@@ -143,19 +153,21 @@
 					else {
 						building.m_leave_allow = nullptr;
 					}
-
-					while (fr.unsigned_8()) {
-						BuildingIndex oldidx = building.descr().tribe().safe_building_index(fr.c_string());
-						building.m_old_buildings.push_back(oldidx);
-					}
-					// Only construction sites may have an empty list
-					if (building.m_old_buildings.empty() &&
-						 building.descr().type() != MapObjectType::CONSTRUCTIONSITE) {
-						throw GameDataError
-							("Failed to read %s %u: No former buildings information.\n"
-							"Your savegame is corrupted", building.descr().descname().c_str(), building.serial());
-					}
-
+					if (packet_version >= 3) {
+						// For former versions, the former buildings vector
+						// will be built after other data are loaded, see below.
+						// read_formerbuildings_v2()
+						while (fr.unsigned_8()) {
+							BuildingIndex oldidx = building.descr().tribe().safe_building_index(fr.c_string());
+							building.m_old_buildings.push_back(oldidx);
+						}
+						// Only construction sites may have an empty list
+						if (building.m_old_buildings.empty() && !is_a(ConstructionSite, &building)) {
+							throw GameDataError
+								("Failed to read %s %u: No former buildings informations.\n"
+								"Your savegame is corrupted", building.descr().descname().c_str(), building.serial());
+						}
+					}
 					if (fr.unsigned_8()) {
 						if (upcast(ProductionSite, productionsite, &building)) {
 							if (building.descr().type() == MapObjectType::MILITARYSITE) {
@@ -203,6 +215,10 @@
 						//  {ConstructionSite, Warehouse, ProductionSite}
 						assert(false);
 					}
+					if (packet_version < 3) {
+						read_formerbuildings_v2(building, fr, game, mol);
+					}
+
 					mol.mark_object_as_loaded(building);
 				} catch (const WException & e) {
 					throw GameDataError("building %u: %s", serial, e.what());
@@ -216,6 +232,46 @@
 	}
 }
 
+void MapBuildingdataPacket::read_formerbuildings_v2
+	(Building& b, FileRead&, Game&, MapObjectLoader&)
+{
+	const TribeDescr & t = b.descr().tribe();
+	BuildingIndex b_idx = t.building_index(b.descr().name());
+	if (is_a(ProductionSite, &b)) {
+		assert(b.m_old_buildings.empty());
+		b.m_old_buildings.push_back(b_idx);
+	} else if (is_a(Warehouse, &b)) {
+		assert(b.m_old_buildings.empty());
+		b.m_old_buildings.push_back(b_idx);
+	} else if (is_a(DismantleSite, &b)) {
+		// Former buildings filled with the current one
+		// upon building init.
+		assert(!b.m_old_buildings.empty());
+	} else if (is_a(ConstructionSite, &b)) {
+		// Not needed for csite.
+		return;
+	} else {
+		assert(false);
+	}
+
+	// iterate through all buildings to find first predecessor
+	for (;;) {
+		BuildingIndex former_idx = b.m_old_buildings.front();
+		const BuildingDescr * oldest = t.get_building_descr(former_idx);
+		if (!oldest->is_enhanced()) {
+			break;
+		}
+		for (BuildingIndex i = 0; i < t.get_nrbuildings(); ++i) {
+			BuildingDescr const * ob = t.get_building_descr(i);
+			if (ob->enhancement() == former_idx) {
+				b.m_old_buildings.insert(b.m_old_buildings.begin(), i);
+				break;
+			}
+		}
+	}
+}
+
+
 void MapBuildingdataPacket::read_partially_finished_building
 	(PartiallyFinishedBuilding  & pfb,
 	 FileRead              & fr,
@@ -286,9 +342,14 @@
 {
 	try {
 		uint16_t const packet_version = fr.unsigned_16();
-		if (packet_version >= kCurrentPacketVersionConstructionsite) {
+		if (packet_version == 1)
+			return read_constructionsite_v1(constructionsite, fr, game, mol);
+
+		if (packet_version >= 2) {
 			read_partially_finished_building(constructionsite, fr, game, mol);
 
+			const TribeDescr & tribe = constructionsite.descr().tribe();
+
 			for (ConstructionSite::Wares::iterator wares_iter = constructionsite.m_wares.begin();
 				  wares_iter != constructionsite.m_wares.end();
 				  ++wares_iter) {
@@ -297,6 +358,13 @@
 						(ConstructionSite::wares_queue_callback, &constructionsite);
 			}
 
+			if (packet_version <= 2) {
+				if (fr.unsigned_8()) {
+					BuildingIndex idx = tribe.safe_building_index(fr.c_string());
+					constructionsite.m_old_buildings.push_back(idx);
+				}
+			}
+
 			constructionsite.m_fetchfromflag  = fr.  signed_32();
 		} else {
 			throw UnhandledVersionError("MapBuildingdataPacket - Constructionsite",
@@ -307,6 +375,66 @@
 	}
 }
 
+void MapBuildingdataPacket::read_constructionsite_v1
+	(ConstructionSite      & constructionsite,
+	 FileRead              & fr,
+	 Game                  & game,
+	 MapObjectLoader & mol)
+{
+	const TribeDescr & tribe = constructionsite.descr().tribe();
+	constructionsite.m_building =
+		tribe.get_building_descr(tribe.safe_building_index(fr.c_string()));
+	if (fr.unsigned_8()) {
+		BuildingIndex bidx = tribe.safe_building_index(fr.c_string());
+		constructionsite.m_old_buildings.push_back(bidx);
+	}
+
+	delete constructionsite.m_builder_request;
+	if (fr.unsigned_8()) {
+		constructionsite.m_builder_request =
+			new Request
+			(constructionsite,
+			 0,
+			 ConstructionSite::request_builder_callback,
+			 wwWORKER);
+		constructionsite.m_builder_request->read(fr, game, mol);
+	} else
+		constructionsite.m_builder_request = nullptr;
+
+	if (uint32_t const builder_serial = fr.unsigned_32()) {
+		try {
+			constructionsite.m_builder = &mol.get<Worker>(builder_serial);
+		} catch (const WException & e) {
+			throw GameDataError
+				("builder (%u): %s", builder_serial, e.what());
+		}
+	} else
+		constructionsite.m_builder = nullptr;
+
+	try {
+		uint16_t const size = fr.unsigned_16();
+		constructionsite.m_wares.resize(size);
+		for (uint16_t i = 0; i < constructionsite.m_wares.size(); ++i)
+		{
+			constructionsite.m_wares[i] =
+				new WaresQueue
+				(constructionsite, INVALID_INDEX, 0);
+			constructionsite.m_wares[i]->set_callback
+				(ConstructionSite::wares_queue_callback, &constructionsite);
+			constructionsite.m_wares[i]->read(fr, game, mol);
+		}
+	} catch (const WException & e) {
+		throw GameDataError("wares: %s", e.what());
+	}
+
+	constructionsite.m_fetchfromflag  = fr.  signed_32();
+
+	constructionsite.m_working        = fr.unsigned_8 ();
+	constructionsite.m_work_steptime  = fr.unsigned_32();
+	constructionsite.m_work_completed = fr.unsigned_32();
+	constructionsite.m_work_steps     = fr.unsigned_32();
+}
+
 void MapBuildingdataPacket::read_dismantlesite
 	(DismantleSite         & dms,
 	 FileRead              & fr,
@@ -337,7 +465,7 @@
 {
 	try {
 		uint16_t const packet_version = fr.unsigned_16();
-		if (packet_version == kCurrentPacketVersionWarehouse) {
+		if (1 <= packet_version && packet_version <= kCurrentPacketVersionWarehouse) {
 			WareIndex const nr_wares   = warehouse.descr().tribe().get_nrwares();
 			WareIndex const nr_tribe_workers = warehouse.descr().tribe().get_nrworkers();
 			warehouse.m_supply->set_nrwares  (nr_wares);
@@ -350,24 +478,52 @@
 			const TribeDescr & tribe = warehouse.descr().tribe();
 			while (fr.unsigned_8()) {
 				WareIndex const id = tribe.ware_index(fr.c_string());
-				uint32_t amount = fr.unsigned_32();
-				Warehouse::StockPolicy policy =
-					static_cast<Warehouse::StockPolicy>(fr.unsigned_8());
-
-				if (id != INVALID_INDEX) {
-					warehouse.insert_wares(id, amount);
-					warehouse.set_ware_policy(id, policy);
+				if (packet_version >= 5) {
+					uint32_t amount = fr.unsigned_32();
+					Warehouse::StockPolicy policy =
+						static_cast<Warehouse::StockPolicy>(fr.unsigned_8());
+
+					if (id != INVALID_INDEX) {
+						warehouse.insert_wares(id, amount);
+						warehouse.set_ware_policy(id, policy);
+					}
+				} else {
+					uint16_t amount = fr.unsigned_16();
+
+					if (id != INVALID_INDEX)
+						warehouse.insert_wares(id, amount);
 				}
 			}
 			while (fr.unsigned_8()) {
 				WareIndex const id = tribe.worker_index(fr.c_string());
-				uint32_t amount = fr.unsigned_32();
-				Warehouse::StockPolicy policy =
-					static_cast<Warehouse::StockPolicy>(fr.unsigned_8());
-
-				if (id != INVALID_INDEX) {
-					warehouse.insert_workers(id, amount);
-					warehouse.set_worker_policy(id, policy);
+				if (packet_version >= 5) {
+					uint32_t amount = fr.unsigned_32();
+					Warehouse::StockPolicy policy =
+						static_cast<Warehouse::StockPolicy>(fr.unsigned_8());
+
+					if (id != INVALID_INDEX) {
+						warehouse.insert_workers(id, amount);
+						warehouse.set_worker_policy(id, policy);
+					}
+				} else {
+					uint16_t amount = fr.unsigned_16();
+
+					if (id != INVALID_INDEX)
+						warehouse.insert_workers(id, amount);
+				}
+			}
+
+			if (packet_version <= 3) {
+				// eat the obsolete idle request structures
+				uint32_t nrrequests = fr.unsigned_16();
+				while (nrrequests--) {
+					std::unique_ptr<Request> req
+						(new Request
+						 	(warehouse,
+						 	 0,
+						 	 &Warehouse::request_cb,
+						 	 wwWORKER));
+					req->read(fr, game, mol);
 				}
 			}
 
@@ -379,6 +535,13 @@
 
 					try {
 						Worker & worker = mol.get<Worker>(worker_serial);
+						if (1 == packet_version) {
+							char const * const name = fr.c_string();
+							if (name != worker.descr().name())
+								throw GameDataError
+									("expected %s but found \"%s\"",
+									 worker.descr().name().c_str(), name);
+						}
 						WareIndex worker_index = tribe.worker_index(worker.descr().name().c_str());
 						if (!warehouse.m_incorporated_workers.count(worker_index))
 							warehouse.m_incorporated_workers[worker_index] = std::vector<Worker *>();
@@ -394,89 +557,138 @@
 			const std::vector<WareIndex> & worker_types_without_cost =
 				tribe.worker_types_without_cost();
 
-			for (;;) {
-				char const * const worker_typename = fr.c_string   ();
-				if (!*worker_typename) //  encountered the terminator ("")
-					break;
-				uint32_t     const next_spawn      = fr.unsigned_32();
-				WareIndex   const worker_index    =
-					tribe.safe_worker_index(worker_typename);
+			if (1 == packet_version) { //  a single next_spawn time for "carrier"
+				uint32_t const next_spawn = fr.unsigned_32();
+				WareIndex const worker_index =
+					tribe.safe_worker_index("carrier");
 				if (worker_index == INVALID_INDEX) {
 					log
 						("WARNING: %s %u has a next_spawn time for nonexistent "
 						 "worker type \"%s\" set to %u, ignoring\n",
 						 warehouse.descr().descname().c_str(), warehouse.serial(),
-						 worker_typename, next_spawn);
-					continue;
-				}
-				if (tribe.get_worker_descr(worker_index)->buildcost().size()) {
+						 "carrier", next_spawn);
+				} else if
+					(tribe.get_worker_descr(worker_index)->buildcost().size())
+				{
 					log
 						("WARNING: %s %u has a next_spawn time for worker type "
 						 "\"%s\", that costs something to build, set to %u, "
 						 "ignoring\n",
 						 warehouse.descr().descname().c_str(), warehouse.serial(),
-						 worker_typename, next_spawn);
-					continue;
-				}
-				for (uint8_t i = 0;; ++i) {
-					assert(i < worker_types_without_cost.size());
-					if (worker_types_without_cost.at(i) == worker_index) {
-						if
-							(warehouse.m_next_worker_without_cost_spawn[i]
-							 !=
-							 static_cast<uint32_t>(never()))
-							throw GameDataError
-								(
-								 "%s %u has a next_spawn time for worker type "
-								 "\"%s\" set to %u, but it was previously set "
-								 "to %u\n",
-								 warehouse.descr().descname().c_str(), warehouse.serial(),
-								 worker_typename, next_spawn,
-								 warehouse.m_next_worker_without_cost_spawn[i]);
-						warehouse.m_next_worker_without_cost_spawn[i] =
-							next_spawn;
+						 "carrier", next_spawn);
+				} else
+					for (uint8_t i = 0;; ++i) {
+						assert(i < worker_types_without_cost.size());
+						if (worker_types_without_cost.at(i) == worker_index) {
+							if
+								(warehouse.m_next_worker_without_cost_spawn[i]
+								 !=
+								 static_cast<uint32_t>(never()))
+							{
+								warehouse.molog
+									("read_warehouse: "
+									 "m_next_worker_without_cost_spawn[%u] = %u\n",
+									 i, warehouse.m_next_worker_without_cost_spawn[i]);
+							}
+							assert
+								(warehouse.m_next_worker_without_cost_spawn[i]
+								 ==
+								 static_cast<uint32_t>(never()));
+							warehouse.m_next_worker_without_cost_spawn[i] =
+								next_spawn;
+							break;
+						}
+					}
+			} else
+				for (;;) {
+					char const * const worker_typename = fr.c_string   ();
+					if (!*worker_typename) //  encountered the terminator ("")
 						break;
-					}
-				}
-			}
-			//  The checks that the warehouse has a next_spawn time for each
-			//  worker type that the player is allowed to spawn, is in
-			//  Warehouse::load_finish.
-
-			// Read planned worker data
-			// Consistency checks are in Warehouse::load_finish
-			uint32_t nr_planned_workers = fr.unsigned_32();
-			while (nr_planned_workers--) {
-				warehouse.m_planned_workers.push_back
-					(Warehouse::PlannedWorkers());
-				Warehouse::PlannedWorkers & pw =
-					warehouse.m_planned_workers.back();
-				pw.index = tribe.worker_index(fr.c_string());
-				pw.amount = fr.unsigned_32();
-
-				uint32_t nr_requests = fr.unsigned_32();
-				while (nr_requests--) {
-					pw.requests.push_back
-						(new Request
-							(warehouse,
-							 0,
-							 &Warehouse::request_cb,
-							 wwWORKER));
-					pw.requests.back()->read(fr, game, mol);
-				}
-			}
-
-			warehouse.m_next_stock_remove_act = fr.unsigned_32();
-
-			if (warehouse.descr().get_isport()) {
-				if (Serial portdock = fr.unsigned_32()) {
-					warehouse.m_portdock = &mol.get<PortDock>(portdock);
-					warehouse.m_portdock->set_economy(warehouse.get_economy());
-					// Expedition specific stuff. This is done in this packet
-					// because the "new style" loader is not supported and
-					// doesn't lend itself to request and other stuff.
-					if (warehouse.m_portdock->expedition_started()) {
-						warehouse.m_portdock->expedition_bootstrap()->load(warehouse, fr, game, mol);
+					uint32_t     const next_spawn      = fr.unsigned_32();
+					WareIndex   const worker_index    =
+						tribe.safe_worker_index(worker_typename);
+					if (worker_index == INVALID_INDEX) {
+						log
+							("WARNING: %s %u has a next_spawn time for nonexistent "
+							 "worker type \"%s\" set to %u, ignoring\n",
+							 warehouse.descr().descname().c_str(), warehouse.serial(),
+							 worker_typename, next_spawn);
+						continue;
+					}
+					if (tribe.get_worker_descr(worker_index)->buildcost().size()) {
+						log
+							("WARNING: %s %u has a next_spawn time for worker type "
+							 "\"%s\", that costs something to build, set to %u, "
+							 "ignoring\n",
+							 warehouse.descr().descname().c_str(), warehouse.serial(),
+							 worker_typename, next_spawn);
+						continue;
+					}
+					for (uint8_t i = 0;; ++i) {
+						assert(i < worker_types_without_cost.size());
+						if (worker_types_without_cost.at(i) == worker_index) {
+							if
+								(warehouse.m_next_worker_without_cost_spawn[i]
+								 !=
+								 static_cast<uint32_t>(never()))
+								throw GameDataError
+									(
+									 "%s %u has a next_spawn time for worker type "
+									 "\"%s\" set to %u, but it was previously set "
+									 "to %u\n",
+									 warehouse.descr().descname().c_str(), warehouse.serial(),
+									 worker_typename, next_spawn,
+									 warehouse.m_next_worker_without_cost_spawn[i]);
+							warehouse.m_next_worker_without_cost_spawn[i] =
+								next_spawn;
+							break;
+						}
+					}
+				}
+				//  The checks that the warehouse has a next_spawn time for each
+				//  worker type that the player is allowed to spawn, is in
+				//  Warehouse::load_finish.
+
+			if (packet_version >= 3) {
+				// Read planned worker data
+				// Consistency checks are in Warehouse::load_finish
+				uint32_t nr_planned_workers = fr.unsigned_32();
+				while (nr_planned_workers--) {
+					warehouse.m_planned_workers.push_back
+						(Warehouse::PlannedWorkers());
+					Warehouse::PlannedWorkers & pw =
+						warehouse.m_planned_workers.back();
+					pw.index = tribe.worker_index(fr.c_string());
+					pw.amount = fr.unsigned_32();
+
+					uint32_t nr_requests = fr.unsigned_32();
+					while (nr_requests--) {
+						pw.requests.push_back
+							(new Request
+							 	(warehouse,
+							 	 0,
+							 	 &Warehouse::request_cb,
+							 	 wwWORKER));
+						pw.requests.back()->read(fr, game, mol);
+					}
+				}
+			}
+
+			if (packet_version >= 5)
+				warehouse.m_next_stock_remove_act = fr.unsigned_32();
+
+			if (packet_version >= 6) {
+				if (warehouse.descr().get_isport()) {
+					if (Serial portdock = fr.unsigned_32()) {
+						warehouse.m_portdock = &mol.get<PortDock>(portdock);
+						warehouse.m_portdock->set_economy(warehouse.get_economy());
+						// Expedition specific stuff. This is done in this packet
+						// because the "new style" loader is not supported and
+						// doesn't lend itself to request and other stuff.
+						if (warehouse.m_portdock->expedition_started()) {
+							warehouse.m_portdock->expedition_bootstrap()->load
+								(packet_version, warehouse, fr, game, mol);
+						}
 					}
 				}
 			}
@@ -625,7 +837,7 @@
 {
 	try {
 		uint16_t const packet_version = fr.unsigned_16();
-		if (packet_version == kCurrentPacketVersionProductionsite) {
+		if (1 <= packet_version && packet_version <= kCurrentPacketVersionProductionsite) {
 			ProductionSite::WorkingPosition & wp_begin =
 				*productionsite.m_working_positions;
 			const ProductionSiteDescr & pr_descr = productionsite.descr();
@@ -719,7 +931,7 @@
 
 			//  skipped programs
 			uint32_t const gametime = game.get_gametime();
-			for (uint8_t i = fr.unsigned_8(); i; --i) {
+			for (uint8_t i = 3 <= packet_version ? fr.unsigned_8() : 0; i; --i) {
 				char const * const program_name = fr.c_string();
 				if (pr_descr.programs().count(program_name)) {
 					uint32_t const skip_time = fr.unsigned_32();
@@ -754,10 +966,12 @@
 				productionsite.m_stack[i].phase = fr.  signed_32();
 				productionsite.m_stack[i].flags = fr.unsigned_32();
 
-				uint32_t serial = fr.unsigned_32();
-				if (serial)
-					productionsite.m_stack[i].objvar = &mol.get<MapObject>(serial);
-				productionsite.m_stack[i].coord = read_coords_32_allow_null(&fr, game.map().extent());
+				if (packet_version >= 5) {
+					uint32_t serial = fr.unsigned_32();
+					if (serial)
+						productionsite.m_stack[i].objvar = &mol.get<MapObject>(serial);
+					productionsite.m_stack[i].coord = read_coords_32_allow_null(&fr, game.map().extent());
+				}
 			}
 			productionsite.m_program_timer = fr.unsigned_8();
 			productionsite.m_program_time = fr.signed_32();

=== modified file 'src/map_io/map_buildingdata_packet.h'
--- src/map_io/map_buildingdata_packet.h	2014-10-01 17:16:21 +0000
+++ src/map_io/map_buildingdata_packet.h	2015-10-29 08:43:55 +0000
@@ -52,6 +52,8 @@
 		(DismantleSite       &, FileRead  &, Game &, MapObjectLoader &);
 	void read_partially_finished_building
 		(PartiallyFinishedBuilding   &, FileRead  &, Game &, MapObjectLoader &);
+	void read_constructionsite_v1
+		(ConstructionSite       &, FileRead  &, Game &, MapObjectLoader &);
 	void read_warehouse
 		(Warehouse              &, FileRead  &, Game &, MapObjectLoader &);
 	void read_militarysite
@@ -60,6 +62,8 @@
 		(TrainingSite           &, FileRead  &, Game &, MapObjectLoader &);
 	void read_productionsite
 		(ProductionSite         &, FileRead  &, Game &, MapObjectLoader &);
+	void read_formerbuildings_v2
+		(Building               &, FileRead  &, Game &, MapObjectLoader &);
 
 	void write_constructionsite
 		(const ConstructionSite &, FileWrite &, Game &, MapObjectSaver  &);

=== modified file 'src/map_io/map_exploration_packet.cc'
--- src/map_io/map_exploration_packet.cc	2015-10-24 15:42:37 +0000
+++ src/map_io/map_exploration_packet.cc	2015-10-29 08:43:55 +0000
@@ -58,7 +58,21 @@
 	MapIndex const max_index = map.max_index();
 	try {
 		uint16_t const packet_version = fr.unsigned_16();
-		if (packet_version == kCurrentPacketVersion) {
+		if (packet_version == 1) {
+			for (MapIndex i = 0; i < max_index; ++i) {
+				uint32_t const data = fr.unsigned_16();
+				for (uint8_t j = 0; j < nr_players; ++j) {
+					bool const see = data & (1 << j);
+					if (Player * const player = egbase.get_player(j + 1))
+						player->m_fields[i].vision = see ? 1 : 0;
+					else if (see)
+						log
+							("MapExplorationPacket::read: WARNING: Player %u, "
+							 "which does not exist, sees field %u.\n",
+							 j + 1, i);
+				}
+			}
+		} else if (packet_version == kCurrentPacketVersion) {
 			for (MapIndex i = 0; i < max_index; ++i) {
 				uint32_t const data = fr.unsigned_32();
 				for (uint8_t j = 0; j < nr_players; ++j) {

=== modified file 'src/map_io/map_flagdata_packet.cc'
--- src/map_io/map_flagdata_packet.cc	2015-10-24 15:42:37 +0000
+++ src/map_io/map_flagdata_packet.cc	2015-10-29 08:43:55 +0000
@@ -54,15 +54,42 @@
 
 	try {
 		uint16_t const packet_version = fr.unsigned_16();
-		if (packet_version == kCurrentPacketVersion) {
+		if (1 <= packet_version && packet_version <= kCurrentPacketVersion) {
 			const Map  & map    = egbase.map();
-			while (! fr.end_of_file()) {
+			Extent const extent = map.extent();
+			for (;;) {
+				if (2 <= packet_version && fr.end_of_file())
+					break;
 				Serial const serial = fr.unsigned_32();
+				if (packet_version < 2 && serial == 0xffffffff) {
+					if (!fr.end_of_file())
+						throw GameDataError
+							("expected end of file after serial 0xffffffff");
+					break;
+				}
 				try {
 					Flag & flag = mol.get<Flag>(serial);
 
 					//  Owner is already set, nothing to do from PlayerImmovable.
 
+					if (packet_version < 3) {
+						if
+							(upcast
+							 	(Flag const,
+							 	 mf,
+							 	 map[flag.m_position = read_coords_32(&fr, extent)]
+							 	 .get_immovable()))
+						{
+							if (mf != &flag)
+								throw GameDataError
+									("wrong flag (%u) at given position (%i, %i)",
+									 mf->serial(),
+									 flag.m_position.x, flag.m_position.y);
+						} else
+							throw GameDataError
+								("no flag at given position (%i, %i)",
+								 flag.m_position.x, flag.m_position.y);
+					}
 					flag.m_animstart = fr.unsigned_16();
 
 					{
@@ -72,6 +99,26 @@
 							dynamic_cast<Building *>
 								(building_position.field->get_immovable());
 					}
+					if (packet_version < 3) {
+						if (uint32_t const building_serial = fr.unsigned_32())
+							try {
+								const Building & building =
+									mol.get<Building>(building_serial);
+								if (flag.m_building != &building)
+									throw GameDataError
+										(
+									 	 "has building %u at (%i, %i), which is not "
+									 	 "at the top left node",
+										 building_serial,
+										 building.get_position().x,
+										 building.get_position().y);
+							} catch (const WException & e) {
+								throw GameDataError
+									("building (%u): %s", building_serial, e.what());
+							}
+						else
+							flag.m_building = nullptr;
+					}
 
 					//  Roads are set somewhere else.
 
@@ -87,7 +134,10 @@
 						flag.m_ware_filled = wares_filled;
 						for (uint32_t i = 0; i < wares_filled; ++i) {
 							flag.m_wares[i].pending = fr.unsigned_8();
-							flag.m_wares[i].priority = fr.signed_32();
+							if (packet_version < 4)
+								flag.m_wares[i].priority = 0;
+							else
+								flag.m_wares[i].priority = fr.signed_32();
 							uint32_t const ware_serial = fr.unsigned_32();
 							try {
 								flag.m_wares[i].ware =

=== modified file 'src/map_io/map_player_names_and_tribes_packet.cc'
--- src/map_io/map_player_names_and_tribes_packet.cc	2015-10-24 15:42:37 +0000
+++ src/map_io/map_player_names_and_tribes_packet.cc	2015-10-29 08:43:55 +0000
@@ -63,8 +63,7 @@
 	try {
 		int32_t const packet_version =
 			prof.get_safe_section("global").get_int("packet_version");
-		// Supporting older versions for map loading
-		if (1 <= packet_version && packet_version <= kCurrentPacketVersion) {
+		if (packet_version <= kCurrentPacketVersion) {
 			PlayerNumber const nr_players = map->get_nrplayers();
 			iterate_player_numbers(p, nr_players) {
 				Section & s = prof.get_safe_section((boost::format("player_%u")

=== modified file 'src/map_io/map_players_view_packet.cc'
--- src/map_io/map_players_view_packet.cc	2015-10-24 15:42:37 +0000
+++ src/map_io/map_players_view_packet.cc	2015-10-29 08:43:55 +0000
@@ -139,7 +139,7 @@
 				throw GameDataError("MapPlayersViewPacket::read: player %u:Could not open "  \
 				                      "\"%s\" for reading. This file should exist when \"%s\" exists", \
 				                      plnum,                                                           \
-											 filename,                                                        \
+				                      filename,                                                        \
 				                      unseen_times_filename);                                          \
 		}                                                                                            \
 	}
@@ -167,6 +167,29 @@
 									 static_cast<long unsigned int>((file).get_size() - (file).get_pos()),    \
 		                      filename);
 
+// TODO(unknown): Legacy code deprecated since build18
+template <uint8_t const Size> struct BitInBuffer {
+	static_assert(Size == 1 || Size == 2 || Size == 4, "Unexpected Size.");
+	BitInBuffer(FileRead* fr) : buffer(0), mask(0x00) {
+		m_fr = fr;
+	}
+
+	uint8_t get() {
+		if (mask == 0x00) {
+			buffer = m_fr->unsigned_8();
+			mask = 0xff;
+		}
+		uint8_t const result = buffer >> (8 - Size);
+		buffer <<= Size;
+		mask <<= Size;
+		assert(result < (1 << Size));
+		return result;
+	}
+
+private:
+	FileRead* m_fr;
+	uint8_t buffer, mask;
+};
 
 // Errors for the Read* functions.
 struct TribeNonexistent : public FileRead::DataError {
@@ -579,6 +602,15 @@
 			BORDER_FILENAME_TEMPLATE,
 			kCurrentPacketVersionBorder);
 
+		// TODO(unknown): Legacy code deprecated since build18
+		BitInBuffer<2> legacy_node_immovable_kinds_bitbuffer(&node_immovable_kinds_file);
+		BitInBuffer<2> legacy_road_bitbuffer(&roads_file);
+		BitInBuffer<4> legacy_terrains_bitbuffer(&terrains_file);
+		BitInBuffer<2> legacy_triangle_immovable_kinds_bitbuffer(&triangle_immovable_kinds_file);
+		BitInBuffer<1> legacy_surveys_bitbuffer(&surveys_file);
+		BitInBuffer<4> legacy_surveys_amount_bitbuffer(&survey_amounts_file);
+		BitInBuffer<1> legacy_border_bitbuffer(&border_file);
+
 		for
 			(FCoords first_in_row(Coords(0, 0), &first_field);
 			 first_in_row.y < mapheight;
@@ -657,12 +689,10 @@
 							 f.x, f.y, owner, nr_players);
 					}
 					uint8_t imm_kind = 0;
-					if (node_immovable_kinds_file_version == kCurrentPacketVersionImmovableKinds) {
+					if (node_immovable_kinds_file_version < kCurrentPacketVersionImmovableKinds) {
+						imm_kind = legacy_node_immovable_kinds_bitbuffer.get();
+					} else {
 						imm_kind = node_immovable_kinds_file.unsigned_8();
-					} else {
-						throw UnhandledVersionError("MapPlayersViewPacket - Node Immovable kinds",
-															 node_immovable_kinds_file_version,
-															 kCurrentPacketVersionImmovableKinds);
 					}
 					MapObjectData mod =
 						read_unseen_immovable
@@ -670,17 +700,24 @@
 					f_player_field.map_object_descr[TCoords<>::None] = mod.map_object_descr;
 					f_player_field.constructionsite = mod.csi;
 
-					// Read in whether this field had a border the last time it was seen
-					if (border_file_version == kCurrentPacketVersionBorder) {
-						uint8_t borders = border_file.unsigned_8();
-						f_player_field.border    = borders & 1;
-						f_player_field.border_r  = borders & 2;
-						f_player_field.border_br = borders & 4;
-						f_player_field.border_bl = borders & 8;
-					} else {
-						throw UnhandledVersionError("MapPlayersViewPacket - Border file",
-															 border_file_version, kCurrentPacketVersionBorder);
-					}
+					// if there is a border file, read in whether this field had a border the last time it was seen
+					if (border_file_version >= 0) {
+						if (border_file_version < 1) {
+							f_player_field.border    = (legacy_border_bitbuffer.get() == 1);
+							f_player_field.border_r  = (legacy_border_bitbuffer.get() == 1);
+							f_player_field.border_br = (legacy_border_bitbuffer.get() == 1);
+							f_player_field.border_bl = (legacy_border_bitbuffer.get() == 1);
+						} else {
+							uint8_t borders = border_file.unsigned_8();
+							f_player_field.border    = borders & 1;
+							f_player_field.border_r  = borders & 2;
+							f_player_field.border_br = borders & 4;
+							f_player_field.border_bl = borders & 8;
+						}
+						} else {
+							throw UnhandledVersionError("MapPlayersViewPacket - Border file",
+																 border_file_version, kCurrentPacketVersionBorder);
+						}
 					break;
 				}
 				default:
@@ -719,19 +756,23 @@
 				} else if (f_everseen | bl_everseen | br_everseen) {
 					//  The player has seen the D triangle but does not see it now.
 					//  Load his information about the triangle from file.
-					if (terrains_file_version == kCurrentPacketVersionTerrains) {
+					if (terrains_file_version < kCurrentPacketVersionTerrains) {
+						try {f_player_field.terrains.d = legacy_terrains_bitbuffer.get();}
+						catch (const FileRead::FileBoundaryExceeded &) {
+							throw GameDataError
+								("MapPlayersViewPacket::read: player %u: in "
+								"\"%s\": node (%i, %i) t = D: unexpected end of file "
+								"while reading terrain",
+								plnum, terrains_filename, f.x, f.y);
+						}
+					} else {
 						f_player_field.terrains.d = terrains_file.unsigned_8();
-					} else {
-						throw UnhandledVersionError("MapPlayersViewPacket - Terrains",
-															 terrains_file_version, kCurrentPacketVersionTerrains);
 					}
 					uint8_t im_kind = 0;
-					if (triangle_immovable_kinds_file_version == kCurrentPacketVersionImmovableKinds) {
+					if (triangle_immovable_kinds_file_version < kCurrentPacketVersionImmovableKinds) {
+						im_kind = legacy_triangle_immovable_kinds_bitbuffer.get();
+					} else {
 						im_kind = triangle_immovable_kinds_file.unsigned_8();
-					} else {
-						throw UnhandledVersionError("MapPlayersViewPacket - Triangle Immovable kinds",
-															 triangle_immovable_kinds_file_version,
-															 kCurrentPacketVersionImmovableKinds);
 					}
 					MapObjectData mod =
 						read_unseen_immovable
@@ -748,19 +789,23 @@
 				} else if (f_everseen | br_everseen | r_everseen) {
 					//  The player has seen the R triangle but does not see it now.
 					//  Load his information about the triangle from file.
-					if (terrains_file_version == kCurrentPacketVersionTerrains) {
+					if (terrains_file_version < kCurrentPacketVersionTerrains) {
+						try {f_player_field.terrains.r = legacy_terrains_bitbuffer.get();}
+						catch (const FileRead::FileBoundaryExceeded &) {
+							throw GameDataError
+								("MapPlayersViewPacket::read: player %u: in "
+								"\"%s\": node (%i, %i) t = R: unexpected end of file "
+								"while reading terrain",
+								plnum, terrains_filename, f.x, f.y);
+						}
+					} else {
 						f_player_field.terrains.r = terrains_file.unsigned_8();
-					} else {
-						throw UnhandledVersionError("MapPlayersViewPacket - Terrains",
-															 terrains_file_version, kCurrentPacketVersionTerrains);
 					}
 					uint8_t im_kind = 0;
-					if (triangle_immovable_kinds_file_version == kCurrentPacketVersionImmovableKinds) {
+					if (triangle_immovable_kinds_file_version < kCurrentPacketVersionImmovableKinds) {
+						im_kind = legacy_triangle_immovable_kinds_bitbuffer.get();
+					} else {
 						im_kind = triangle_immovable_kinds_file.unsigned_8();
-					} else {
-						throw UnhandledVersionError("MapPlayersViewPacket - Triangle Immovable kinds",
-															 triangle_immovable_kinds_file_version,
-															 kCurrentPacketVersionImmovableKinds);
 					}
 					MapObjectData mod =
 						read_unseen_immovable
@@ -775,11 +820,17 @@
 					} else if (f_everseen | bl_everseen) {
 						//  The player has seen the SouthWest edge but does not see
 						//  it now. Load his information about this edge from file.
-						if (road_file_version == kCurrentPacketVersionRoads) {
+						if (road_file_version < kCurrentPacketVersionRoads) {
+							try {roads  = legacy_road_bitbuffer.get() << RoadType::kSouthWest;}
+							catch (const FileRead::FileBoundaryExceeded &) {
+								throw GameDataError
+									("MapPlayersViewPacket::read: player %u: in "
+									"\"%s\": node (%i, %i): unexpected end of file while "
+									"reading RoadType::kSouthWest",
+									plnum, roads_filename, f.x, f.y);
+							}
+						} else {
 							roads = roads_file.unsigned_8();
-						} else {
-							throw UnhandledVersionError("MapPlayersViewPacket - Road file",
-																 road_file_version, kCurrentPacketVersionRoads);
 						}
 					}
 					if (f_seen | br_seen) {
@@ -787,11 +838,17 @@
 					} else if (f_everseen | br_everseen) {
 						//  The player has seen the SouthEast edge but does not see
 						//  it now. Load his information about this edge from file.
-						if (road_file_version == kCurrentPacketVersionRoads) {
+						if (road_file_version < kCurrentPacketVersionRoads) {
+							try {roads |= legacy_road_bitbuffer.get() << RoadType::kSouthEast;}
+							catch (const FileRead::FileBoundaryExceeded &) {
+								throw GameDataError
+									("MapPlayersViewPacket::read: player %u: in "
+										"\"%s\": node (%i, %i): unexpected end of file while "
+										"reading RoadType::kSouthEast",
+										plnum, roads_filename, f.x, f.y);
+							}
+						} else {
 							roads |= roads_file.unsigned_8();
-						} else {
-							throw UnhandledVersionError("MapPlayersViewPacket - Road file",
-																 road_file_version, kCurrentPacketVersionRoads);
 						}
 					}
 					if (f_seen |  r_seen) {
@@ -799,11 +856,17 @@
 					} else if (f_everseen |  r_everseen) {
 						//  The player has seen the      East edge but does not see
 						//  it now. Load his information about this edge from file.
-						if (road_file_version == kCurrentPacketVersionRoads) {
+						if (road_file_version < kCurrentPacketVersionRoads) {
+							try {roads |= legacy_road_bitbuffer.get() << RoadType::kEast;}
+							catch (const FileRead::FileBoundaryExceeded &) {
+								throw GameDataError
+									("MapPlayersViewPacket::read: player %u: in "
+										"\"%s\": node (%i, %i): unexpected end of file while "
+										"reading RoadType::kEast",
+										plnum, roads_filename, f.x, f.y);
+							}
+						} else {
 							roads |= roads_file.unsigned_8();
-						} else {
-							throw UnhandledVersionError("MapPlayersViewPacket - Road file",
-																 road_file_version, kCurrentPacketVersionRoads);
 						}
 					}
 					roads |= f.field->get_roads() & mask;
@@ -816,20 +879,27 @@
 				//  geologic survey
 				try {
 					bool survey = false;
-					if (surveys_file_version == kCurrentPacketVersionSurveys) {
+					if (surveys_file_version < kCurrentPacketVersionSurveys) {
+						survey = (f_everseen & bl_everseen & br_everseen)
+							&& legacy_surveys_bitbuffer.get();
+					} else {
 						survey = (f_everseen & bl_everseen & br_everseen)
 							 && surveys_file.unsigned_8();
-					} else {
-						throw UnhandledVersionError("MapPlayersViewPacket - Surveys file",
-															 surveys_file_version, kCurrentPacketVersionSurveys);
 					}
 					if (survey) {
-						if (survey_amounts_file_version == kCurrentPacketVersionSurveyAmounts) {
+						if (survey_amounts_file_version < kCurrentPacketVersionSurveyAmounts) {
+							try {
+								f_player_field.resource_amounts.d =
+									legacy_surveys_amount_bitbuffer.get();
+							} catch (const FileRead::FileBoundaryExceeded &) {
+								throw GameDataError
+									("MapPlayersViewPacket::read: player %u: in "
+										"\"%s\": node (%i, %i) t = D: unexpected end of file "
+										"while reading resource amount of surveyed triangle",
+										plnum, survey_amounts_filename, f.x, f.y);
+							}
+						} else {
 							f_player_field.resource_amounts.d = survey_amounts_file.unsigned_8();
-						} else {
-							throw UnhandledVersionError("MapPlayersViewPacket - Survey amounts",
-																 survey_amounts_file_version,
-																 kCurrentPacketVersionSurveyAmounts);
 						}
 						try {
 							f_player_field.time_triangle_last_surveyed[TCoords<>::D] =
@@ -854,20 +924,27 @@
 				}
 				try {
 					bool survey = false;
-					if (surveys_file_version == kCurrentPacketVersionSurveys) {
+					if (surveys_file_version < kCurrentPacketVersionSurveys) {
+						survey = (f_everseen & br_everseen &  r_everseen)
+							&& legacy_surveys_bitbuffer.get();
+					} else {
 						survey = (f_everseen & br_everseen &  r_everseen)
 							&& surveys_file.unsigned_8();
-					} else {
-						throw UnhandledVersionError("MapPlayersViewPacket - Surveys file",
-															 surveys_file_version, kCurrentPacketVersionSurveys);
 					}
 					if (survey) {
-						if (survey_amounts_file_version == kCurrentPacketVersionSurveyAmounts) {
+						if (survey_amounts_file_version < kCurrentPacketVersionSurveyAmounts) {
+							try {
+								f_player_field.resource_amounts.r =
+									legacy_surveys_amount_bitbuffer.get();
+							} catch (const FileRead::FileBoundaryExceeded &) {
+								throw GameDataError
+									("MapPlayersViewPacket::read: player %u: in "
+									"\"%s\": node (%i, %i) t = R: unexpected end of file "
+									"while reading resource amount of surveyed triangle",
+									plnum, survey_amounts_filename, f.x, f.y);
+							}
+						} else {
 							f_player_field.resource_amounts.r = survey_amounts_file.unsigned_8();
-						} else {
-							throw UnhandledVersionError("MapPlayersViewPacket - Survey amounts",
-																 survey_amounts_file_version,
-																 kCurrentPacketVersionSurveyAmounts);
 						}
 						try {
 							f_player_field.time_triangle_last_surveyed[TCoords<>::R] =

=== modified file 'src/map_io/map_roaddata_packet.cc'
--- src/map_io/map_roaddata_packet.cc	2015-10-24 15:42:37 +0000
+++ src/map_io/map_roaddata_packet.cc	2015-10-29 08:43:55 +0000
@@ -55,11 +55,19 @@
 
 	try {
 		uint16_t const packet_version = fr.unsigned_16();
-		if (packet_version == kCurrentPacketVersion) {
+		if (1 <= packet_version && packet_version <= kCurrentPacketVersion) {
 			const Map   &       map        = egbase.map();
 			PlayerNumber const nr_players = map.get_nrplayers();
-			while (! fr.end_of_file()) {
+			for (;;) {
+				if (2 <= packet_version && fr.end_of_file())
+					break;
 				Serial const serial = fr.unsigned_32();
+				if (packet_version < 2 && serial == 0xffffffff) {
+					if (!fr.end_of_file())
+						throw GameDataError
+							("expected end of file after serial 0xffffffff");
+					break;
+				}
 				try {
 					Game& game = dynamic_cast<Game&>(egbase);
 					Road & road = mol.get<Road>(serial);
@@ -72,8 +80,10 @@
 					Player & plr = egbase.player(player_index);
 
 					road.set_owner(&plr);
-					road.m_busyness             = fr.unsigned_32();
-					road.m_busyness_last_update = fr.unsigned_32();
+					if (4 <= packet_version) {
+						road.m_busyness             = fr.unsigned_32();
+						road.m_busyness_last_update = fr.unsigned_32();
+					}
 					road.m_type = fr.unsigned_32();
 					{
 						uint32_t const flag_0_serial = fr.unsigned_32();
@@ -122,6 +132,12 @@
 					uint32_t const count = fr.unsigned_32();
 					if (!count)
 						throw GameDataError("no carrier slot");
+					if (packet_version <= 2 && 1 < count)
+						throw GameDataError
+							(
+						 	 "expected 1 but found %u carrier slots in road saved "
+						 	 "with packet version 2 (old)",
+							 count);
 
 					for (uint32_t i = 0; i < count; ++i) {
 						Carrier * carrier = nullptr;
@@ -149,7 +165,8 @@
 						} else {
 							carrier_request = nullptr;
 						}
-						uint8_t const carrier_type = fr.unsigned_32();
+						uint8_t const carrier_type =
+							packet_version < 3 ? 1 : fr.unsigned_32();
 
 						if
 							(i < road.m_carrier_slots.size() &&

=== modified file 'src/map_io/map_scripting_packet.cc'
--- src/map_io/map_scripting_packet.cc	2015-10-24 15:42:37 +0000
+++ src/map_io/map_scripting_packet.cc	2015-10-29 08:43:55 +0000
@@ -38,7 +38,7 @@
 namespace Widelands {
 
 namespace {
-constexpr uint32_t kCurrentPacketVersion = 2;
+constexpr uint32_t kCurrentPacketVersion = 1;
 }  // namespace
 /*
  * ========================================================================
@@ -58,17 +58,13 @@
 	FileRead fr;
 	if (g && fr.try_open(fs, "scripting/globals.dump"))
 	{
-		try {
-			const uint32_t packet_version = fr.unsigned_32();
-			if (packet_version == kCurrentPacketVersion) {
-				upcast(LuaGameInterface, lgi, &g->lua());
-				lgi->read_global_env(fr, mol, fr.unsigned_32());
-			} else {
-				throw UnhandledVersionError("MapScriptingPacket", packet_version, kCurrentPacketVersion);
-			}
-		} catch (const WException & e) {
-			throw GameDataError("scripting: %s", e.what());
+		const uint32_t sentinel = fr.unsigned_32();
+		const uint32_t packet_version = fr.unsigned_32();
+		if (sentinel != 0xDEADBEEF && packet_version != kCurrentPacketVersion) {
+			throw UnhandledVersionError("MapScriptingPacket", packet_version, kCurrentPacketVersion);
 		}
+		upcast(LuaGameInterface, lgi, &g->lua());
+		lgi->read_global_env(fr, mol, fr.unsigned_32());
 	}
 }
 
@@ -95,6 +91,7 @@
 	// Dump the global environment if this is a game and not in the editor
 	if (upcast(Game, g, &egbase)) {
 		FileWrite fw;
+		fw.unsigned_32(0xDEADBEEF);  // Sentinel, because there was no packet version.
 		fw.unsigned_32(kCurrentPacketVersion);
 		const FileWrite::Pos pos = fw.get_pos();
 		fw.unsigned_32(0); // N bytes written, follows below


Follow ups