1 /*
2  * Copyright (C) 2007-2020 by the Widelands Development Team
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17  *
18  */
19 
20 #include "map_io/map_players_view_packet.h"
21 
22 #include "base/log.h"
23 #include "base/macros.h"
24 #include "base/wexception.h"
25 #include "economy/flag.h"
26 #include "economy/road.h"
27 #include "economy/waterway.h"
28 #include "io/fileread.h"
29 #include "io/filesystem/filesystem_exceptions.h"
30 #include "io/filewrite.h"
31 #include "logic/editor_game_base.h"
32 #include "logic/field.h"
33 #include "logic/game_data_error.h"
34 #include "logic/map_objects/tribes/tribe_descr.h"
35 #include "logic/map_objects/world/world.h"
36 #include "logic/player.h"
37 
38 namespace Widelands {
39 
40 #define PLAYERDIRNAME_TEMPLATE "player/%u"
41 #define DIRNAME_TEMPLATE PLAYERDIRNAME_TEMPLATE "/view"
42 
43 constexpr uint8_t kCurrentPacketVersionUnseenTimes = 1;
44 #define UNSEEN_TIMES_FILENAME_TEMPLATE DIRNAME_TEMPLATE "/unseen_times_%u"
45 
46 constexpr uint8_t kCurrentPacketVersionImmovableKinds = 2;
47 #define NODE_IMMOVABLE_KINDS_FILENAME_TEMPLATE DIRNAME_TEMPLATE "/node_immovable_kinds_%u"
48 #define TRIANGLE_IMMOVABLE_KINDS_FILENAME_TEMPLATE DIRNAME_TEMPLATE "/triangle_immovable_kinds_%u"
49 
50 constexpr uint8_t kCurrentPacketVersionImmovables = 2;
51 #define NODE_IMMOVABLES_FILENAME_TEMPLATE DIRNAME_TEMPLATE "/node_immovables_%u"
52 #define TRIANGLE_IMMOVABLES_FILENAME_TEMPLATE DIRNAME_TEMPLATE "/triangle_immovables_%u"
53 
54 constexpr uint8_t kCurrentPacketVersionRoads = 2;
55 #define ROADS_FILENAME_TEMPLATE DIRNAME_TEMPLATE "/roads_%u"
56 
57 constexpr uint8_t kCurrentPacketVersionTerrains = 2;
58 #define TERRAINS_FILENAME_TEMPLATE DIRNAME_TEMPLATE "/terrains_%u"
59 
60 constexpr uint8_t kCurrentPacketVersionOwners = 0;
61 #define OWNERS_FILENAME_TEMPLATE DIRNAME_TEMPLATE "/owners_%u"
62 
63 constexpr uint8_t kCurrentPacketVersionSurveys = 2;
64 #define SURVEYS_FILENAME_TEMPLATE DIRNAME_TEMPLATE "/surveys_%u"
65 
66 constexpr uint8_t kCurrentPacketVersionSurveyAmounts = 2;
67 #define SURVEY_AMOUNTS_FILENAME_TEMPLATE DIRNAME_TEMPLATE "/survey_amounts_%u"
68 
69 constexpr uint8_t kCurrentPacketVersionSurveyTimes = 1;
70 #define SURVEY_TIMES_FILENAME_TEMPLATE DIRNAME_TEMPLATE "/survey_times_%u"
71 
72 constexpr uint8_t kCurrentPacketVersionHidden = 1;
73 #define HIDDEN_FILENAME_TEMPLATE DIRNAME_TEMPLATE "/hidden_%u"
74 
75 constexpr uint8_t kCurrentPacketVersionVision = 1;
76 #define VISION_FILENAME_TEMPLATE DIRNAME_TEMPLATE "/vision_%u"
77 
78 constexpr uint8_t kCurrentPacketVersionBorder = 1;
79 #define BORDER_FILENAME_TEMPLATE DIRNAME_TEMPLATE "/border_%u"
80 
81 #define FILENAME_SIZE 64
82 
83 enum {
84 	UNSEEN_NONE = 0,
85 	UNSEEN_TRIBEORWORLD = 1,
86 	UNSEEN_FLAG = 2,
87 	UNSEEN_BUILDING = 3,
88 	UNSEEN_PORTDOCK = 4
89 };
90 
91 //  The map is traversed by row and column. In each step we process of one map
92 //  field (which is 1 node, 2 triangles and 3 edges that are stored together).
93 //  For this processing we need to keep track of 4 nodes:
94 //  *  f: the node of the processed field
95 //  * bl: the bottom left neighbour of f
96 //  * br: the bottom right neighbour of f
97 //  *  r: the right neighbour of f
98 //
99 //  The layout of the processing region is as follows:
100 //
101 //                     f------ r
102 //                    / \     /
103 //                   /  \    /
104 //                  / D \ R /
105 //                 /    \  /
106 //                /     \ /
107 //              bl------br
108 
109 struct MapObjectData {
MapObjectDataWidelands::MapObjectData110 	MapObjectData() : map_object_descr(nullptr) {
111 	}
112 	const MapObjectDescr* map_object_descr;
113 	ConstructionsiteInformation csi;
114 };
115 
116 namespace {
117 #define OPEN_INPUT_FILE(filetype, file, filename, filename_template, version)                      \
118 	char(filename)[FILENAME_SIZE];                                                                  \
119 	snprintf(filename, sizeof(filename), filename_template, plnum, version);                        \
120 	filetype file;                                                                                  \
121 	try {                                                                                           \
122 		(file).open(fs, filename);                                                                   \
123 	} catch (const FileError&) {                                                                    \
124 		throw GameDataError("MapPlayersViewPacket::read: player %u:Could not open "                  \
125 		                    "\"%s\" for reading. This file should exist when \"%s\" exists",         \
126 		                    static_cast<unsigned int>(plnum), filename, unseen_times_filename);      \
127 	}
128 
129 // Try to find the file with newest fitting version number
130 #define OPEN_INPUT_FILE_NEW_VERSION(filetype, file, filename, fileversion, filename_template,      \
131                                     version)                                                       \
132 	uint8_t fileversion = version;                                                                  \
133 	filetype file;                                                                                  \
134 	char(filename)[FILENAME_SIZE];                                                                  \
135 	for (;; --fileversion) {                                                                        \
136 		snprintf(filename, sizeof(filename), filename_template, plnum, fileversion);                 \
137 		try {                                                                                        \
138 			(file).open(fs, filename);                                                                \
139 			break;                                                                                    \
140 		} catch (...) {                                                                              \
141 			if (fileversion == 0)                                                                     \
142 				throw GameDataError("MapPlayersViewPacket::read: player %u:Could not open "            \
143 				                    "\"%s\" for reading. This file should exist when \"%s\" exists",   \
144 				                    static_cast<unsigned int>(plnum), filename,                        \
145 				                    unseen_times_filename);                                            \
146 		}                                                                                            \
147 	}
148 
149 // Using this macro, if no file exists, fileversion will be set to -1
150 #define OPEN_INPUT_FILE_NEW_VERSION_SILENT(filetype, file, filename, fileversion, file_templ, v)   \
151 	int8_t fileversion = v;                                                                         \
152 	filetype file;                                                                                  \
153 	char(filename)[FILENAME_SIZE];                                                                  \
154 	for (; fileversion >= -1; --fileversion) {                                                      \
155 		snprintf(filename, sizeof(filename), file_templ, plnum, fileversion);                        \
156 		try {                                                                                        \
157 			(file).open(fs, filename);                                                                \
158 			break;                                                                                    \
159 		} catch (...) {                                                                              \
160 		}                                                                                            \
161 	}
162 
163 #define CHECK_TRAILING_BYTES(file, filename)                                                       \
164 	if (!(file).end_of_file())                                                                      \
165 		throw GameDataError("MapPlayersViewPacket::read: player %u:"                                 \
166 		                    "Found %" PRIuS " trailing bytes in \"%s\"",                             \
167 		                    static_cast<unsigned int>(plnum),                                        \
168 		                    static_cast<size_t>((file).get_size() - (file).get_pos()), filename);
169 
170 // Errors for the Read* functions.
171 struct TribeImmovableNonexistent : public FileRead::DataError {
TribeImmovableNonexistentWidelands::__anon3db69d170211::TribeImmovableNonexistent172 	explicit TribeImmovableNonexistent(const std::string& Name)
173 	   : DataError("immovable type \"%s\" does not seem to be a tribe immovable", Name.c_str()),
174 	     name(Name) {
175 	}
176 
177 	std::string name;
178 };
179 struct WorldImmovableNonexistent : public FileRead::DataError {
WorldImmovableNonexistentWidelands::__anon3db69d170211::WorldImmovableNonexistent180 	explicit WorldImmovableNonexistent(char const* const Name)
181 	   : DataError("world does not define immovable type \"%s\"", Name), name(Name) {
182 	}
183 	char const* const name;
184 };
185 struct BuildingNonexistent : public FileRead::DataError {
BuildingNonexistentWidelands::__anon3db69d170211::BuildingNonexistent186 	explicit BuildingNonexistent(char const* const Name)
187 	   : DataError("tribes do not define building type \"%s\"", Name), name(Name) {
188 	}
189 	char const* const name;
190 };
191 
192 // reads an immovable depending on whether it is a tribe or world immovable
read_immovable_type(StreamRead * fr,const EditorGameBase & egbase,const TribesLegacyLookupTable & tribes_lookup_table,const WorldLegacyLookupTable & world_lookup_table)193 const ImmovableDescr& read_immovable_type(StreamRead* fr,
194                                           const EditorGameBase& egbase,
195                                           const TribesLegacyLookupTable& tribes_lookup_table,
196                                           const WorldLegacyLookupTable& world_lookup_table) {
197 	uint8_t owner = fr->unsigned_8();
198 	char const* const name = fr->c_string();
199 	if (owner == static_cast<uint8_t>(MapObjectDescr::OwnerType::kWorld)) {
200 		DescriptionIndex const index =
201 		   egbase.world().get_immovable_index(world_lookup_table.lookup_immovable(name));
202 		if (index == Widelands::INVALID_INDEX) {
203 			throw WorldImmovableNonexistent(name);
204 		}
205 		return *egbase.world().get_immovable_descr(index);
206 	} else {
207 		assert(owner == static_cast<uint8_t>(MapObjectDescr::OwnerType::kTribe));
208 		DescriptionIndex const index =
209 		   egbase.tribes().immovable_index(tribes_lookup_table.lookup_immovable(name));
210 		if (index == Widelands::INVALID_INDEX) {
211 			throw TribeImmovableNonexistent(name);
212 		}
213 		return *egbase.tribes().get_immovable_descr(index);
214 	}
215 }
216 
217 // Reads a c_string and interprets it as the name of an immovable type.
218 //
219 // \returns a reference to the building type description.
220 //
221 // \throws Building_Nonexistent if there is no building type with that name
read_building_type(StreamRead * fr,const EditorGameBase & egbase)222 const BuildingDescr& read_building_type(StreamRead* fr, const EditorGameBase& egbase) {
223 	char const* const name = fr->c_string();
224 	DescriptionIndex const index = egbase.tribes().building_index(name);
225 	if (!egbase.tribes().building_exists(index)) {
226 		throw BuildingNonexistent(name);
227 	}
228 	return *egbase.tribes().get_building_descr(index);
229 }
230 
231 // Encode a Immovable_Type into 'wr'.
write_immovable_type(StreamWrite * wr,const ImmovableDescr & immovable)232 void write_immovable_type(StreamWrite* wr, const ImmovableDescr& immovable) {
233 	wr->unsigned_8(static_cast<uint8_t>(immovable.owner_type()));
234 	wr->string(immovable.name());
235 }
236 
237 // Encode a Building_Type into 'wr'.
write_building_type(StreamWrite * wr,const BuildingDescr & building)238 void write_building_type(StreamWrite* wr, const BuildingDescr& building) {
239 	wr->string(building.name());
240 }
241 
242 }  // namespace
243 
244 inline static MapObjectData
read_unseen_immovable(const EditorGameBase & egbase,uint8_t & immovable_kind,FileRead & immovables_file,const TribesLegacyLookupTable & tribes_lookup_table,const WorldLegacyLookupTable & world_lookup_table,uint8_t & version)245 read_unseen_immovable(const EditorGameBase& egbase,
246                       uint8_t& immovable_kind,
247                       FileRead& immovables_file,
248                       const TribesLegacyLookupTable& tribes_lookup_table,
249                       const WorldLegacyLookupTable& world_lookup_table,
250                       uint8_t& version) {
251 	MapObjectData m;
252 	try {
253 		switch (immovable_kind) {
254 		case UNSEEN_NONE:  //  The player sees no immovable.
255 			m.map_object_descr = nullptr;
256 			break;
257 		case UNSEEN_TRIBEORWORLD:  //  The player sees a tribe or world immovable.
258 			m.map_object_descr =
259 			   &read_immovable_type(&immovables_file, egbase, tribes_lookup_table, world_lookup_table);
260 			break;
261 		case UNSEEN_FLAG:  //  The player sees a flag.
262 			m.map_object_descr = &g_flag_descr;
263 			break;
264 		case UNSEEN_BUILDING:  //  The player sees a building.
265 			m.map_object_descr = &read_building_type(&immovables_file, egbase);
266 			if (version == kCurrentPacketVersionImmovables) {
267 				// Read data from immovables file
268 				if (immovables_file.unsigned_8() == 1) {  // the building is a constructionsite
269 					m.csi.becomes = &read_building_type(&immovables_file, egbase);
270 					if (immovables_file.unsigned_8() == 1) {
271 						m.csi.was = &read_building_type(&immovables_file, egbase);
272 					}
273 					m.csi.totaltime = immovables_file.unsigned_32();
274 					m.csi.completedtime = immovables_file.unsigned_32();
275 				}
276 			} else {
277 				throw UnhandledVersionError(
278 				   "MapPlayersViewPacket", version, kCurrentPacketVersionImmovables);
279 			}
280 			break;
281 		case UNSEEN_PORTDOCK:  // The player sees a port dock
282 			m.map_object_descr = &g_portdock_descr;
283 			break;
284 		default:
285 			throw GameDataError("Unknown immovable-kind type %d", immovable_kind);
286 		}
287 	} catch (const WException& e) {
288 		throw GameDataError("unseen immovable: %s", e.what());
289 	}
290 	return m;
291 }
292 
read(FileSystem & fs,EditorGameBase & egbase,bool const skip,MapObjectLoader &,const TribesLegacyLookupTable & tribes_lookup_table,const WorldLegacyLookupTable & world_lookup_table)293 void MapPlayersViewPacket::read(FileSystem& fs,
294                                 EditorGameBase& egbase,
295                                 bool const skip,
296                                 MapObjectLoader&,
297                                 const TribesLegacyLookupTable& tribes_lookup_table,
298                                 const WorldLegacyLookupTable& world_lookup_table)
299 
300 {
301 	if (skip) {
302 		return;
303 	}
304 
305 	const Map& map = egbase.map();
306 	const uint16_t mapwidth = map.get_width();
307 	const uint16_t mapheight = map.get_height();
308 	Field& first_field = map[0];
309 	const PlayerNumber nr_players = map.get_nrplayers();
310 	iterate_players_existing(plnum, nr_players, egbase, player) {
311 		Player::Field* const player_fields = player->fields_.get();
312 		uint32_t const gametime = egbase.get_gametime();
313 
314 		char unseen_times_filename[FILENAME_SIZE];
315 		snprintf(unseen_times_filename, sizeof(unseen_times_filename), UNSEEN_TIMES_FILENAME_TEMPLATE,
316 		         static_cast<unsigned int>(plnum), kCurrentPacketVersionUnseenTimes);
317 		FileRead unseen_times_file;
318 		struct NotFound {};
319 
320 		if (!unseen_times_file.try_open(fs, unseen_times_filename)) {
321 			log("MapPlayersViewPacket::read: WARNING: Could not open "
322 			    "\"%s\" for reading. Assuming that the game is from an old "
323 			    "version without player point of view. Will give player %u "
324 			    "knowledge of unseen nodes, edges and triangles (but not "
325 			    "resources).",
326 			    unseen_times_filename, static_cast<unsigned int>(plnum));
327 
328 			for (FCoords first_in_row(Coords(0, 0), &first_field); first_in_row.y < mapheight;
329 			     ++first_in_row.y, first_in_row.field += mapwidth) {
330 				FCoords r = first_in_row, br = map.bl_n(r);
331 				MapIndex r_index = r.field - &first_field;
332 				MapIndex br_index = br.field - &first_field;
333 				Player::Field* r_player_field = player_fields + r_index;
334 				Player::Field* br_player_field = player_fields + br_index;
335 				Vision r_vision = r_player_field->vision;
336 				Vision br_vision = br_player_field->vision;
337 				do {
338 					const FCoords f = r;
339 					Player::Field& f_player_field = *r_player_field;
340 					const Vision f_vision = r_vision, bl_vision = br_vision;
341 					move_r(mapwidth, r, r_index);
342 					move_r(mapwidth, br, br_index);
343 					r_player_field = player_fields + r_index;
344 					br_player_field = player_fields + br_index;
345 					r_vision = r_player_field->vision;
346 					br_vision = br_player_field->vision;
347 
348 					f_player_field.time_node_last_unseen = gametime;
349 
350 					if (f_vision) {  //  node
351 						//  owner
352 						f_player_field.owner = f.field->get_owned_by();
353 						assert(f_player_field.owner < 0x20);
354 
355 						//  map_object_descr
356 						const MapObjectDescr* map_object_descr;
357 						if (const BaseImmovable* base_immovable = f.field->get_immovable()) {
358 							map_object_descr = &base_immovable->descr();
359 							if (Road::is_road_descr(map_object_descr) ||
360 							    Waterway::is_waterway_descr(map_object_descr)) {
361 								map_object_descr = nullptr;
362 							} else if (upcast(Building const, building, base_immovable)) {
363 								if (building->get_position() != f) {
364 									//  TODO(unknown): This is not the building's main position
365 									//  so we can not see it. But it should be
366 									//  possible to see it from a distance somehow.
367 									map_object_descr = nullptr;
368 								}
369 							}
370 						} else {
371 							map_object_descr = nullptr;
372 						}
373 						f_player_field.map_object_descr = map_object_descr;
374 					}
375 
376 					{  //  triangles
377 						//  Must be initialized because the rendering code is
378 						//  accessing it even for triangles that the player does not
379 						//  see (it is the darkening that actually hides the ground
380 						//  from the player).
381 						Field::Terrains terrains;
382 						terrains.d = terrains.r = 0;
383 
384 						if (f_vision | bl_vision | br_vision) {
385 							terrains.d = f.field->terrain_d();
386 						}
387 						if (f_vision | br_vision | r_vision) {
388 							terrains.r = f.field->terrain_r();
389 						}
390 						f_player_field.terrains = terrains;
391 					}
392 
393 					{  //  edges
394 						if (f_vision | bl_vision) {
395 							f_player_field.r_sw = f.field->get_road(WALK_SW);
396 						}
397 						if (f_vision | br_vision) {
398 							f_player_field.r_se = f.field->get_road(WALK_SE);
399 						}
400 						if (f_vision | r_vision) {
401 							f_player_field.r_e = f.field->get_road(WALK_E);
402 						}
403 					}
404 
405 					//  The player is not given information about resources that he
406 					//  has discovered, because there is no such information in old
407 					//  savegames, except for the resource indicators that are
408 					//  merely immovables with limited lifetime.
409 				} while (r.x);
410 			}
411 			return;
412 		}
413 
414 		// Verify the vision values
415 		FileRead vision_file;
416 		bool have_vision = false;
417 
418 		try {
419 			char fname[FILENAME_SIZE];
420 			snprintf(fname, sizeof(fname), VISION_FILENAME_TEMPLATE, static_cast<unsigned int>(plnum),
421 			         kCurrentPacketVersionVision);
422 			vision_file.open(fs, fname);
423 			have_vision = true;
424 		} catch (...) {
425 		}
426 
427 		if (have_vision) {
428 			for (FCoords first_in_row(Coords(0, 0), &first_field); first_in_row.y < mapheight;
429 			     ++first_in_row.y, first_in_row.field += mapwidth) {
430 				FCoords r = first_in_row;
431 				MapIndex r_index = r.field - &first_field;
432 				Player::Field* r_player_field = player_fields + r_index;
433 				do {
434 					Player::Field& f_player_field = *r_player_field;
435 					move_r(mapwidth, r, r_index);
436 					r_player_field = player_fields + r_index;
437 
438 					uint32_t file_vision = vision_file.unsigned_32();
439 
440 					// There used to be a check here that the calculated, and the
441 					// loaded vision were the same. I removed this check, because
442 					// scripting could have given the player a permanent view of
443 					// this field. That's why we save this stuff in the first place!
444 					if (file_vision != f_player_field.vision) {
445 						f_player_field.vision = file_vision;
446 					}
447 				} while (r.x);
448 			}
449 
450 			log("Vision check successful for player %u\n", static_cast<unsigned int>(plnum));
451 		}
452 
453 		// Read the player's knowledge about all fields
454 		OPEN_INPUT_FILE_NEW_VERSION(FileRead, node_immovable_kinds_file,
455 		                            node_immovable_kinds_filename, node_immovable_kinds_file_version,
456 		                            NODE_IMMOVABLE_KINDS_FILENAME_TEMPLATE,
457 		                            kCurrentPacketVersionImmovableKinds)
458 
459 		OPEN_INPUT_FILE_NEW_VERSION(FileRead, node_immovables_file, node_immovables_filename,
460 		                            node_immovables_file_version, NODE_IMMOVABLES_FILENAME_TEMPLATE,
461 		                            kCurrentPacketVersionImmovables)
462 
463 		OPEN_INPUT_FILE_NEW_VERSION(FileRead, roads_file, roads_filename, road_file_version,
464 		                            ROADS_FILENAME_TEMPLATE, kCurrentPacketVersionRoads)
465 
466 		OPEN_INPUT_FILE_NEW_VERSION(FileRead, terrains_file, terrains_filename, terrains_file_version,
467 		                            TERRAINS_FILENAME_TEMPLATE, kCurrentPacketVersionTerrains)
468 
469 		OPEN_INPUT_FILE_NEW_VERSION(
470 		   FileRead, triangle_immovable_kinds_file, triangle_immovable_kinds_filename,
471 		   triangle_immovable_kinds_file_version, TRIANGLE_IMMOVABLE_KINDS_FILENAME_TEMPLATE,
472 		   kCurrentPacketVersionImmovableKinds)
473 
474 		OPEN_INPUT_FILE_NEW_VERSION(FileRead, triangle_immovables_file, triangle_immovables_filename,
475 		                            triangle_immovables_file_version,
476 		                            TRIANGLE_IMMOVABLES_FILENAME_TEMPLATE,
477 		                            kCurrentPacketVersionImmovables)
478 
479 		OPEN_INPUT_FILE(FileRead, owners_file, owners_filename, OWNERS_FILENAME_TEMPLATE,
480 		                kCurrentPacketVersionOwners)
481 
482 		OPEN_INPUT_FILE_NEW_VERSION(FileRead, surveys_file, surveys_filename, surveys_file_version,
483 		                            SURVEYS_FILENAME_TEMPLATE, kCurrentPacketVersionSurveys)
484 
485 		OPEN_INPUT_FILE_NEW_VERSION(FileRead, survey_amounts_file, survey_amounts_filename,
486 		                            survey_amounts_file_version, SURVEY_AMOUNTS_FILENAME_TEMPLATE,
487 		                            kCurrentPacketVersionSurveyAmounts)
488 
489 		OPEN_INPUT_FILE(FileRead, survey_times_file, survey_times_filename,
490 		                SURVEY_TIMES_FILENAME_TEMPLATE, kCurrentPacketVersionSurveyTimes)
491 
492 		OPEN_INPUT_FILE_NEW_VERSION(FileRead, border_file, border_filename, border_file_version,
493 		                            BORDER_FILENAME_TEMPLATE, kCurrentPacketVersionBorder)
494 
495 		OPEN_INPUT_FILE_NEW_VERSION_SILENT(FileRead, hidden_file, hidden_filename,
496 		                                   hidden_file_version, HIDDEN_FILENAME_TEMPLATE,
497 		                                   kCurrentPacketVersionHidden)
498 
499 		for (FCoords first_in_row(Coords(0, 0), &first_field); first_in_row.y < mapheight;
500 		     ++first_in_row.y, first_in_row.field += mapwidth) {
501 			FCoords r = first_in_row, br = map.bl_n(r);
502 			MapIndex r_index = r.field - &first_field;
503 			MapIndex br_index = br.field - &first_field;
504 			Player::Field* r_player_field = player_fields + r_index;
505 			Player::Field* br_player_field = player_fields + br_index;
506 			Vision r_vision = r_player_field->vision;
507 			Vision br_vision = br_player_field->vision;
508 			bool r_everseen = r_vision, r_seen = 1 < r_vision;
509 			bool br_everseen = br_vision, br_seen = 1 < br_vision;
510 			do {
511 				const FCoords f = r;
512 				Player::Field& f_player_field = *r_player_field;
513 				const Vision f_vision = r_vision;
514 				const bool f_everseen = r_everseen;
515 				const bool bl_everseen = br_everseen;
516 				const bool f_seen = r_seen;
517 				const bool bl_seen = br_seen;
518 				move_r(mapwidth, r, r_index);
519 				move_r(mapwidth, br, br_index);
520 				r_player_field = player_fields + r_index;
521 				br_player_field = player_fields + br_index;
522 				r_vision = r_player_field->vision;
523 				br_vision = br_player_field->vision;
524 				r_everseen = r_vision;
525 				r_seen = 1 < r_vision;
526 				br_everseen = br_vision;
527 				br_seen = 1 < br_vision;
528 
529 				//  Store the player's view of ownership in these
530 				//  temporary variables and save it in the player when set.
531 				PlayerNumber owner = 0;
532 
533 				switch (f_vision) {  //  owner and map_object_descr
534 				case 0:
535 					//  The player has never seen this node, so he has no
536 					//  information about it. Neither should he be informed about
537 					//  it now.
538 					break;
539 				case 1: {
540 					//  The player has seen the node but does not see it now. Load
541 					//  his information about the node from file.
542 					try {
543 						f_player_field.time_node_last_unseen = unseen_times_file.unsigned_32();
544 					} catch (const FileRead::FileBoundaryExceeded&) {
545 						throw GameDataError("MapPlayersViewPacket::read: player %u: in "
546 						                    "\"%s\":%" PRIuS ": node (%i, %i): unexpected end of file "
547 						                    "while reading time_node_last_unseen",
548 						                    static_cast<unsigned int>(plnum), unseen_times_filename,
549 						                    static_cast<size_t>(unseen_times_file.get_pos() - 4), f.x,
550 						                    f.y);
551 					}
552 
553 					try {
554 						owner = owners_file.unsigned_8();
555 					} catch (const FileRead::FileBoundaryExceeded&) {
556 						throw GameDataError("MapPlayersViewPacket::read: player %u: in "
557 						                    "\"%s\":%" PRIuS ": node (%i, %i): unexpected end of file "
558 						                    "while reading owner",
559 						                    static_cast<unsigned int>(plnum), unseen_times_filename,
560 						                    static_cast<size_t>(unseen_times_file.get_pos() - 1), f.x,
561 						                    f.y);
562 					}
563 					if (nr_players < owner) {
564 						throw GameDataError("MapPlayersViewPacket::read: player %u: in "
565 						                    "\"%s\":%" PRIuS " & 0xf: node (%i, %i): Player thinks that "
566 						                    "this node is owned by player %u, but there are only %u "
567 						                    "players",
568 						                    static_cast<unsigned int>(plnum), owners_filename,
569 						                    static_cast<size_t>(owners_file.get_pos() - 1), f.x, f.y,
570 						                    owner, nr_players);
571 					}
572 					uint8_t imm_kind = 0;
573 					if (node_immovable_kinds_file_version == kCurrentPacketVersionImmovableKinds) {
574 						imm_kind = node_immovable_kinds_file.unsigned_8();
575 					} else {
576 						throw UnhandledVersionError("MapPlayersViewPacket - Node Immovable kinds",
577 						                            node_immovable_kinds_file_version,
578 						                            kCurrentPacketVersionImmovableKinds);
579 					}
580 					MapObjectData mod =
581 					   read_unseen_immovable(egbase, imm_kind, node_immovables_file, tribes_lookup_table,
582 					                         world_lookup_table, node_immovables_file_version);
583 					f_player_field.map_object_descr = mod.map_object_descr;
584 					f_player_field.constructionsite = mod.csi;
585 
586 					// Read in whether this field had a border the last time it was seen
587 					if (border_file_version == kCurrentPacketVersionBorder) {
588 						uint8_t borders = border_file.unsigned_8();
589 						f_player_field.border = borders & 1;
590 						f_player_field.border_r = borders & 2;
591 						f_player_field.border_br = borders & 4;
592 						f_player_field.border_bl = borders & 8;
593 					} else {
594 						throw UnhandledVersionError("MapPlayersViewPacket - Border file",
595 						                            border_file_version, kCurrentPacketVersionBorder);
596 					}
597 					break;
598 				}
599 				default:
600 					//  The player currently sees the node. Therefore his
601 					//  information about the node has not been saved. Fill in the
602 					//  information from the game state.
603 
604 					//  owner
605 					owner = f.field->get_owned_by();
606 					assert(owner <= nr_players);
607 
608 					//  map_object_descr
609 					const MapObjectDescr* map_object_descr;
610 					if (const BaseImmovable* base_immovable = f.field->get_immovable()) {
611 						map_object_descr = &base_immovable->descr();
612 						if (Road::is_road_descr(map_object_descr) ||
613 						    Waterway::is_waterway_descr(map_object_descr)) {
614 							map_object_descr = nullptr;
615 						} else if (upcast(Building const, building, base_immovable)) {
616 							if (building->get_position() != f) {
617 								//  TODO(unknown): This is not the building's main position so
618 								//  we can not see it. But it should be possible
619 								//  to see it from a distance somehow.
620 								map_object_descr = nullptr;
621 							}
622 						}
623 					} else {
624 						map_object_descr = nullptr;
625 					}
626 					f_player_field.map_object_descr = map_object_descr;
627 					break;
628 				}
629 
630 				//  triangles
631 				if (f_seen | bl_seen | br_seen) {
632 					//  The player currently sees the D triangle. Therefore his
633 					//  information about the triangle has not been saved. Fill in
634 					//  the information from the game state.
635 					f_player_field.terrains.d = f.field->terrain_d();
636 				} else if (f_everseen | bl_everseen | br_everseen) {
637 					//  The player has seen the D triangle but does not see it now.
638 					//  Load his information about the triangle from file.
639 					if (terrains_file_version == kCurrentPacketVersionTerrains) {
640 						f_player_field.terrains.d = terrains_file.unsigned_8();
641 					} else {
642 						throw UnhandledVersionError("MapPlayersViewPacket - Terrains",
643 						                            terrains_file_version, kCurrentPacketVersionTerrains);
644 					}
645 					uint8_t im_kind = 0;
646 					if (triangle_immovable_kinds_file_version == kCurrentPacketVersionImmovableKinds) {
647 						im_kind = triangle_immovable_kinds_file.unsigned_8();
648 					} else {
649 						throw UnhandledVersionError("MapPlayersViewPacket - Triangle Immovable kinds",
650 						                            triangle_immovable_kinds_file_version,
651 						                            kCurrentPacketVersionImmovableKinds);
652 					}
653 					// We read and ignore the immovable information on the D
654 					// triangle. This was done because there were vague plans of
655 					// suporting immovables on the triangles instead as on the
656 					// nodes.
657 					// TODO(sirver): Remove this logic the next time we break
658 					// savegame compatibility.
659 					read_unseen_immovable(egbase, im_kind, triangle_immovables_file, tribes_lookup_table,
660 					                      world_lookup_table, triangle_immovables_file_version);
661 				}
662 				if (f_seen | br_seen | r_seen) {
663 					//  The player currently sees the R triangle. Therefore his
664 					//  information about the triangle has not been saved. Fill in
665 					//  the information from the game state.
666 					f_player_field.terrains.r = f.field->terrain_r();
667 				} else if (f_everseen | br_everseen | r_everseen) {
668 					//  The player has seen the R triangle but does not see it now.
669 					//  Load his information about the triangle from file.
670 					if (terrains_file_version == kCurrentPacketVersionTerrains) {
671 						f_player_field.terrains.r = terrains_file.unsigned_8();
672 					} else {
673 						throw UnhandledVersionError("MapPlayersViewPacket - Terrains",
674 						                            terrains_file_version, kCurrentPacketVersionTerrains);
675 					}
676 					uint8_t im_kind = 0;
677 					if (triangle_immovable_kinds_file_version == kCurrentPacketVersionImmovableKinds) {
678 						im_kind = triangle_immovable_kinds_file.unsigned_8();
679 					} else {
680 						throw UnhandledVersionError("MapPlayersViewPacket - Triangle Immovable kinds",
681 						                            triangle_immovable_kinds_file_version,
682 						                            kCurrentPacketVersionImmovableKinds);
683 					}
684 					// We read and ignore the immovable information on the D
685 					// triangle. This was done because there were vague plans of
686 					// suporting immovables on the triangles instead as on the
687 					// nodes.
688 					read_unseen_immovable(egbase, im_kind, triangle_immovables_file, tribes_lookup_table,
689 					                      world_lookup_table, triangle_immovables_file_version);
690 				}
691 
692 				{  //  edges
693 					if (f_seen | bl_seen) {
694 						f_player_field.r_sw = f.field->get_road(WALK_SW);
695 					} else if (f_everseen | bl_everseen) {
696 						//  The player has seen the SouthWest edge but does not see
697 						//  it now. Load his information about this edge from file.
698 						if (road_file_version == kCurrentPacketVersionRoads) {
699 							f_player_field.r_sw = static_cast<RoadSegment>(roads_file.unsigned_8());
700 						} else {
701 							throw UnhandledVersionError("MapPlayersViewPacket - Road file",
702 							                            road_file_version, kCurrentPacketVersionRoads);
703 						}
704 					}
705 					if (f_seen | br_seen) {
706 						f_player_field.r_se = f.field->get_road(WALK_SE);
707 					} else if (f_everseen | br_everseen) {
708 						//  The player has seen the SouthEast edge but does not see
709 						//  it now. Load his information about this edge from file.
710 						if (road_file_version == kCurrentPacketVersionRoads) {
711 							f_player_field.r_se = static_cast<RoadSegment>(roads_file.unsigned_8());
712 						} else {
713 							throw UnhandledVersionError("MapPlayersViewPacket - Road file",
714 							                            road_file_version, kCurrentPacketVersionRoads);
715 						}
716 					}
717 					if (f_seen | r_seen) {
718 						f_player_field.r_e = f.field->get_road(WALK_E);
719 					} else if (f_everseen | r_everseen) {
720 						//  The player has seen the      East edge but does not see
721 						//  it now. Load his information about this edge from file.
722 						if (road_file_version == kCurrentPacketVersionRoads) {
723 							f_player_field.r_e = static_cast<RoadSegment>(roads_file.unsigned_8());
724 						} else {
725 							throw UnhandledVersionError("MapPlayersViewPacket - Road file",
726 							                            road_file_version, kCurrentPacketVersionRoads);
727 						}
728 					}
729 				}
730 
731 				//  Now save this information in the player field.
732 				f_player_field.owner = owner;
733 
734 				//  geologic survey
735 				try {
736 					bool survey = false;
737 					if (surveys_file_version == kCurrentPacketVersionSurveys) {
738 						survey = (f_everseen & bl_everseen & br_everseen) && surveys_file.unsigned_8();
739 					} else {
740 						throw UnhandledVersionError("MapPlayersViewPacket - Surveys file",
741 						                            surveys_file_version, kCurrentPacketVersionSurveys);
742 					}
743 					if (survey) {
744 						if (survey_amounts_file_version == kCurrentPacketVersionSurveyAmounts) {
745 							f_player_field.resource_amounts.d = survey_amounts_file.unsigned_8();
746 						} else {
747 							throw UnhandledVersionError("MapPlayersViewPacket - Survey amounts",
748 							                            survey_amounts_file_version,
749 							                            kCurrentPacketVersionSurveyAmounts);
750 						}
751 						try {
752 							f_player_field
753 							   .time_triangle_last_surveyed[static_cast<int>(TriangleIndex::D)] =
754 							   survey_times_file.unsigned_32();
755 						} catch (const FileRead::FileBoundaryExceeded&) {
756 							throw GameDataError(
757 							   "MapPlayersViewPacket::read: player %u: in "
758 							   "\"%s\":%" PRIuS ": node (%i, %i) t = D: unexpected end of "
759 							   "file while reading time_triangle_last_surveyed",
760 							   static_cast<unsigned int>(plnum), survey_times_filename,
761 							   static_cast<size_t>(survey_times_file.get_pos() - 4), f.x, f.y);
762 						}
763 					}
764 				} catch (const FileRead::FileBoundaryExceeded&) {
765 					throw GameDataError("MapPlayersViewPacket::read: player %u: in \"%s\": "
766 					                    "node (%i, %i) t = D: unexpected end of file while reading "
767 					                    "survey bit",
768 					                    static_cast<unsigned int>(plnum), surveys_filename, f.x, f.y);
769 				}
770 				try {
771 					bool survey = false;
772 					if (surveys_file_version == kCurrentPacketVersionSurveys) {
773 						survey = (f_everseen & br_everseen & r_everseen) && surveys_file.unsigned_8();
774 					} else {
775 						throw UnhandledVersionError("MapPlayersViewPacket - Surveys file",
776 						                            surveys_file_version, kCurrentPacketVersionSurveys);
777 					}
778 					if (survey) {
779 						if (survey_amounts_file_version == kCurrentPacketVersionSurveyAmounts) {
780 							f_player_field.resource_amounts.r = survey_amounts_file.unsigned_8();
781 						} else {
782 							throw UnhandledVersionError("MapPlayersViewPacket - Survey amounts",
783 							                            survey_amounts_file_version,
784 							                            kCurrentPacketVersionSurveyAmounts);
785 						}
786 						try {
787 							f_player_field
788 							   .time_triangle_last_surveyed[static_cast<int>(TriangleIndex::R)] =
789 							   survey_times_file.unsigned_32();
790 						} catch (const FileRead::FileBoundaryExceeded&) {
791 							throw GameDataError(
792 							   "MapPlayersViewPacket::read: player %u: in "
793 							   "\"%s\":%" PRIuS ": node (%i, %i) t = R: unexpected end of "
794 							   "file while reading time_triangle_last_surveyed",
795 							   static_cast<unsigned int>(plnum), survey_times_filename,
796 							   static_cast<size_t>(survey_times_file.get_pos() - 4), f.x, f.y);
797 						}
798 					}
799 				} catch (const FileRead::FileBoundaryExceeded&) {
800 					throw GameDataError("MapPlayersViewPacket::read: player %u: in \"%s\": "
801 					                    "node (%i, %i) t = R: unexpected end of file while reading "
802 					                    "survey bit",
803 					                    static_cast<unsigned int>(plnum), surveys_filename, f.x, f.y);
804 				}
805 			} while (r.x);
806 		}
807 
808 		// Read the number of explicitly hidden fields and then loop through them
809 		if (hidden_file_version == kCurrentPacketVersionHidden) {
810 			const uint32_t no_of_hidden_fields = hidden_file.unsigned_32();
811 			for (uint32_t i = 0; i < no_of_hidden_fields; ++i) {
812 				player->hidden_fields_.insert(
813 				   std::make_pair(hidden_file.unsigned_32(), hidden_file.unsigned_16()));
814 			}
815 		} else if (hidden_file_version < 0) {
816 			// TODO(GunChleoc): Savegame compatibility - remove after Build 20
817 			log("MapPlayersViewPacket - No hidden fields to read for Player %d - probably an old save "
818 			    "file\n",
819 			    static_cast<unsigned int>(plnum));
820 		} else {
821 			throw UnhandledVersionError("MapPlayersViewPacket - Hidden fields file",
822 			                            hidden_file_version, kCurrentPacketVersionHidden);
823 		}
824 
825 		CHECK_TRAILING_BYTES(unseen_times_file, unseen_times_filename)
826 		CHECK_TRAILING_BYTES(node_immovable_kinds_file, node_immovable_kinds_filename)
827 		CHECK_TRAILING_BYTES(node_immovables_file, node_immovables_filename)
828 		CHECK_TRAILING_BYTES(roads_file, roads_filename)
829 		CHECK_TRAILING_BYTES(terrains_file, terrains_filename)
830 		CHECK_TRAILING_BYTES(triangle_immovable_kinds_file, triangle_immovable_kinds_filename)
831 		CHECK_TRAILING_BYTES(triangle_immovables_file, triangle_immovables_filename)
832 		CHECK_TRAILING_BYTES(owners_file, owners_filename)
833 		CHECK_TRAILING_BYTES(surveys_file, surveys_filename)
834 		CHECK_TRAILING_BYTES(survey_amounts_file, survey_amounts_filename)
835 		CHECK_TRAILING_BYTES(survey_times_file, survey_times_filename)
836 		CHECK_TRAILING_BYTES(border_file, border_filename)
837 		CHECK_TRAILING_BYTES(hidden_file, hidden_filename)
838 	}
839 }
840 
write_unseen_immovable(MapObjectData const * map_object_data,FileWrite & immovable_kinds_file,FileWrite & immovables_file)841 inline static void write_unseen_immovable(MapObjectData const* map_object_data,
842                                           FileWrite& immovable_kinds_file,
843                                           FileWrite& immovables_file) {
844 	MapObjectDescr const* const map_object_descr = map_object_data->map_object_descr;
845 	const ConstructionsiteInformation& csi = map_object_data->csi;
846 	assert(!Road::is_road_descr(map_object_descr) && !Waterway::is_waterway_descr(map_object_descr));
847 	uint8_t immovable_kind = 255;
848 
849 	if (!map_object_descr) {
850 		immovable_kind = UNSEEN_NONE;
851 	} else if (upcast(ImmovableDescr const, immovable_descr, map_object_descr)) {
852 		immovable_kind = UNSEEN_TRIBEORWORLD;
853 		write_immovable_type(&immovables_file, *immovable_descr);
854 	} else if (map_object_descr->type() == MapObjectType::FLAG) {
855 		immovable_kind = UNSEEN_FLAG;
856 	} else if (upcast(BuildingDescr const, building_descr, map_object_descr)) {
857 		immovable_kind = UNSEEN_BUILDING;
858 		write_building_type(&immovables_file, *building_descr);
859 		if (!csi.becomes) {
860 			immovables_file.unsigned_8(0);
861 		} else {
862 			// the building is a constructionsite
863 			immovables_file.unsigned_8(1);
864 			write_building_type(&immovables_file, *csi.becomes);
865 			if (!csi.was) {
866 				immovables_file.unsigned_8(0);
867 			} else {
868 				// constructionsite is an enhancement, therefor we write down the enhancement
869 				immovables_file.unsigned_8(1);
870 				write_building_type(&immovables_file, *csi.was);
871 			}
872 			immovables_file.unsigned_32(csi.totaltime);
873 			immovables_file.unsigned_32(csi.completedtime);
874 		}
875 	} else if (map_object_descr->type() == MapObjectType::PORTDOCK) {
876 		immovable_kind = UNSEEN_PORTDOCK;
877 	} else {
878 		// We should never get here.. output some information about the situation.
879 		log("\nwidelands_map_players_view_data_packet.cc::write_unseen_immovable(): ");
880 		log("%s %s was not expected.\n", typeid(*map_object_descr).name(),
881 		    map_object_descr->name().c_str());
882 		NEVER_HERE();
883 	}
884 	immovable_kinds_file.unsigned_8(immovable_kind);
885 }
886 
887 #define WRITE(file, filename_template, version)                                                    \
888 	snprintf(filename, sizeof(filename), filename_template, plnum, version);                        \
889 	(file).write(fs, filename);
890 
write(FileSystem & fs,EditorGameBase & egbase,MapObjectSaver &)891 void MapPlayersViewPacket::write(FileSystem& fs, EditorGameBase& egbase, MapObjectSaver&) {
892 	fs.ensure_directory_exists("player");
893 	const Map& map = egbase.map();
894 	const uint16_t mapwidth = map.get_width();
895 	const uint16_t mapheight = map.get_height();
896 	// TODO(unknown): make first_field const when FCoords has been templatized so it can
897 	// have "const Field * field;"
898 	Field& first_field = map[0];
899 	const PlayerNumber nr_players = map.get_nrplayers();
900 	iterate_players_existing_const(
901 	   plnum, nr_players, egbase,
902 	   player) if (const Player::Field* const player_fields = player->fields_.get()) {
903 		FileWrite unseen_times_file;
904 		FileWrite node_immovable_kinds_file;
905 		FileWrite node_immovables_file;
906 		FileWrite roads_file;
907 		FileWrite terrains_file;
908 		FileWrite triangle_immovable_kinds_file;
909 		FileWrite triangle_immovables_file;
910 		FileWrite owners_file;
911 		FileWrite surveys_file;
912 		FileWrite survey_amounts_file;
913 		FileWrite survey_times_file;
914 		FileWrite hidden_file;
915 		FileWrite vision_file;
916 		FileWrite border_file;
917 		for (FCoords first_in_row(Coords(0, 0), &first_field); first_in_row.y < mapheight;
918 		     ++first_in_row.y, first_in_row.field += mapwidth) {
919 			FCoords r = first_in_row, br = map.bl_n(r);
920 			MapIndex r_index = r.field - &first_field;
921 			MapIndex br_index = br.field - &first_field;
922 			const Player::Field* r_player_field = player_fields + r_index;
923 			const Player::Field* br_player_field = player_fields + br_index;
924 			Vision r_vision = r_player_field->vision;
925 			Vision br_vision = br_player_field->vision;
926 			bool r_everseen = r_vision, r_seen = 1 < r_vision;
927 			bool br_everseen = br_vision, br_seen = 1 < br_vision;
928 			do {
929 				const Player::Field& f_player_field = *r_player_field;
930 				const bool f_everseen = r_everseen;
931 				const bool bl_everseen = br_everseen;
932 				const bool f_seen = r_seen;
933 				const bool bl_seen = br_seen;
934 				move_r(mapwidth, r, r_index);
935 				move_r(mapwidth, br, br_index);
936 				r_player_field = player_fields + r_index;
937 				br_player_field = player_fields + br_index;
938 				r_vision = r_player_field->vision;
939 				br_vision = br_player_field->vision;
940 				r_everseen = r_vision;
941 				r_seen = 1 < r_vision;
942 				br_everseen = br_vision;
943 				br_seen = 1 < br_vision;
944 
945 				vision_file.unsigned_32(f_player_field.vision);
946 
947 				if (!f_seen) {
948 
949 					if (f_everseen) {  //  node
950 						unseen_times_file.unsigned_32(f_player_field.time_node_last_unseen);
951 						assert(f_player_field.owner < 0x20);
952 						owners_file.unsigned_8(f_player_field.owner);
953 						MapObjectData mod;
954 						mod.map_object_descr = f_player_field.map_object_descr;
955 						mod.csi = f_player_field.constructionsite;
956 						write_unseen_immovable(&mod, node_immovable_kinds_file, node_immovables_file);
957 
958 						// write whether this field had a border the last time it was seen
959 						uint8_t borders = 0;
960 						borders |= f_player_field.border;
961 						borders |= f_player_field.border_r << 1;
962 						borders |= f_player_field.border_br << 2;
963 						borders |= f_player_field.border_bl << 3;
964 						border_file.unsigned_8(borders);
965 					}
966 
967 					//  triangles
968 					if
969 					   //  the player does not see the D triangle now but has
970 					   //  seen it
971 					   ((!bl_seen) & (!br_seen) & (f_everseen | bl_everseen | br_everseen)) {
972 						terrains_file.unsigned_8(f_player_field.terrains.d);
973 						MapObjectData mod;
974 						mod.map_object_descr = nullptr;
975 						write_unseen_immovable(
976 						   &mod, triangle_immovable_kinds_file, triangle_immovables_file);
977 					}
978 					if
979 					   //  the player does not see the R triangle now but has
980 					   //  seen it
981 					   ((!br_seen) & (!r_seen) & (f_everseen | br_everseen | r_everseen)) {
982 						terrains_file.unsigned_8(f_player_field.terrains.r);
983 						MapObjectData mod;
984 						mod.map_object_descr = nullptr;
985 						write_unseen_immovable(
986 						   &mod, triangle_immovable_kinds_file, triangle_immovables_file);
987 					}
988 
989 					//  edges
990 					if ((!bl_seen) && (f_everseen || bl_everseen)) {
991 						roads_file.unsigned_8(f_player_field.road_sw());
992 					}
993 					if ((!br_seen) && (f_everseen || br_everseen)) {
994 						roads_file.unsigned_8(f_player_field.road_se());
995 					}
996 					if ((!r_seen) && (f_everseen || r_everseen)) {
997 						roads_file.unsigned_8(f_player_field.road_e());
998 					}
999 				}
1000 
1001 				//  geologic survey
1002 				if (f_everseen & bl_everseen & br_everseen) {
1003 					const uint32_t time_last_surveyed =
1004 					   f_player_field.time_triangle_last_surveyed[static_cast<int>(TriangleIndex::D)];
1005 					const uint8_t has_info = time_last_surveyed != 0xffffffff;
1006 					surveys_file.unsigned_8(has_info);
1007 					if (has_info) {
1008 						survey_amounts_file.unsigned_8(f_player_field.resource_amounts.d);
1009 						survey_times_file.unsigned_32(time_last_surveyed);
1010 					}
1011 				}
1012 				if (f_everseen & br_everseen & r_everseen) {
1013 					const uint32_t time_last_surveyed =
1014 					   f_player_field.time_triangle_last_surveyed[static_cast<int>(TriangleIndex::R)];
1015 					const uint8_t has_info = time_last_surveyed != 0xffffffff;
1016 					surveys_file.unsigned_8(has_info);
1017 					if (has_info) {
1018 						survey_amounts_file.unsigned_8(f_player_field.resource_amounts.r);
1019 						survey_times_file.unsigned_32(time_last_surveyed);
1020 					}
1021 				}
1022 			} while (r.x);
1023 		}
1024 		// Write the number of explicitly hidden fields and then loop through them
1025 		hidden_file.unsigned_32(player->hidden_fields_.size());
1026 		for (const auto& hidden : player->hidden_fields_) {
1027 			hidden_file.unsigned_32(hidden.first);
1028 			hidden_file.unsigned_16(hidden.second);
1029 		}
1030 
1031 		char filename[FILENAME_SIZE];
1032 
1033 		fs.ensure_directory_exists(
1034 		   (boost::format(PLAYERDIRNAME_TEMPLATE) % static_cast<unsigned int>(plnum)).str());
1035 		fs.ensure_directory_exists(
1036 		   (boost::format(DIRNAME_TEMPLATE) % static_cast<unsigned int>(plnum)).str());
1037 
1038 		WRITE(unseen_times_file, UNSEEN_TIMES_FILENAME_TEMPLATE, kCurrentPacketVersionUnseenTimes)
1039 
1040 		WRITE(node_immovable_kinds_file, NODE_IMMOVABLE_KINDS_FILENAME_TEMPLATE,
1041 		      kCurrentPacketVersionImmovableKinds)
1042 
1043 		WRITE(
1044 		   node_immovables_file, NODE_IMMOVABLES_FILENAME_TEMPLATE, kCurrentPacketVersionImmovables)
1045 
1046 		WRITE(roads_file, ROADS_FILENAME_TEMPLATE, kCurrentPacketVersionRoads)
1047 
1048 		WRITE(terrains_file, TERRAINS_FILENAME_TEMPLATE, kCurrentPacketVersionTerrains)
1049 
1050 		WRITE(triangle_immovable_kinds_file, TRIANGLE_IMMOVABLE_KINDS_FILENAME_TEMPLATE,
1051 		      kCurrentPacketVersionImmovableKinds)
1052 
1053 		WRITE(triangle_immovables_file, TRIANGLE_IMMOVABLES_FILENAME_TEMPLATE,
1054 		      kCurrentPacketVersionImmovables)
1055 
1056 		WRITE(owners_file, OWNERS_FILENAME_TEMPLATE, kCurrentPacketVersionOwners)
1057 
1058 		WRITE(surveys_file, SURVEYS_FILENAME_TEMPLATE, kCurrentPacketVersionSurveys)
1059 
1060 		WRITE(
1061 		   survey_amounts_file, SURVEY_AMOUNTS_FILENAME_TEMPLATE, kCurrentPacketVersionSurveyAmounts)
1062 
1063 		WRITE(survey_times_file, SURVEY_TIMES_FILENAME_TEMPLATE, kCurrentPacketVersionSurveyTimes)
1064 
1065 		WRITE(hidden_file, HIDDEN_FILENAME_TEMPLATE, kCurrentPacketVersionHidden)
1066 
1067 		WRITE(vision_file, VISION_FILENAME_TEMPLATE, kCurrentPacketVersionVision)
1068 
1069 		WRITE(border_file, BORDER_FILENAME_TEMPLATE, kCurrentPacketVersionBorder)
1070 	}
1071 }
1072 }  // namespace Widelands
1073