1 /*
2 	This file is part of Warzone 2100.
3 	Copyright (C) 1999-2004  Eidos Interactive
4 	Copyright (C) 2005-2020  Warzone 2100 Project
5 
6 	Warzone 2100 is free software; you can redistribute it and/or modify
7 	it under the terms of the GNU General Public License as published by
8 	the Free Software Foundation; either version 2 of the License, or
9 	(at your option) any later version.
10 
11 	Warzone 2100 is distributed in the hope that it will be useful,
12 	but WITHOUT ANY WARRANTY; without even the implied warranty of
13 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 	GNU General Public License for more details.
15 
16 	You should have received a copy of the GNU General Public License
17 	along with Warzone 2100; if not, write to the Free Software
18 	Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20 /**
21  * @file init.c
22  *
23  * Game initialisation routines.
24  *
25  */
26 #include "lib/framework/frame.h"
27 
28 #include <string.h>
29 
30 #include "lib/framework/frameresource.h"
31 #include "lib/framework/file.h"
32 #include "lib/framework/physfs_ext.h"
33 #include "lib/framework/wzapp.h"
34 #include "lib/ivis_opengl/piemode.h"
35 #include "lib/ivis_opengl/piestate.h"
36 #include "lib/ivis_opengl/screen.h"
37 #include "lib/ivis_opengl/pieblitfunc.h"
38 #include "lib/ivis_opengl/tex.h"
39 #include "lib/ivis_opengl/imd.h"
40 #include "lib/netplay/netplay.h"
41 #include "lib/sound/audio_id.h"
42 #include "lib/sound/cdaudio.h"
43 #include "lib/sound/mixer.h"
44 
45 #include "init.h"
46 
47 #include "advvis.h"
48 #include "atmos.h"
49 #include "challenge.h"
50 #include "cmddroid.h"
51 #include "configuration.h"
52 #include "console.h"
53 #include "data.h"
54 #include "difficulty.h" // for "double up" and "biffer baker" cheats
55 #include "display.h"
56 #include "display3d.h"
57 #include "edit3d.h"
58 #include "effects.h"
59 #include "fpath.h"
60 #include "frend.h"
61 #include "frontend.h"
62 #include "game.h"
63 #include "gateway.h"
64 #include "hci.h"
65 #include "intdisplay.h"
66 #include "keymap.h"
67 #include "levels.h"
68 #include "lighting.h"
69 #include "loop.h"
70 #include "mapgrid.h"
71 #include "mechanics.h"
72 #include "miscimd.h"
73 #include "mission.h"
74 #include "modding.h"
75 #include "multiint.h"
76 #include "multigifts.h"
77 #include "multiplay.h"
78 #include "multistat.h"
79 #include "notifications.h"
80 #include "projectile.h"
81 #include "order.h"
82 #include "radar.h"
83 #include "research.h"
84 #include "lib/framework/cursors.h"
85 #include "text.h"
86 #include "transporter.h"
87 #include "warzoneconfig.h"
88 #include "main.h"
89 #include "wrappers.h"
90 #include "terrain.h"
91 #include "ingameop.h"
92 #include "qtscript.h"
93 #include "template.h"
94 #include "activity.h"
95 
96 #include <algorithm>
97 #include <unordered_map>
98 
99 static void initMiscVars();
100 
101 static const char UserMusicPath[] = "music";
102 
103 // FIXME Totally inappropriate place for this.
104 char fileLoadBuffer[FILE_LOAD_BUFFER_SIZE];
105 
106 IMAGEFILE *FrontImages;
107 
108 static wzSearchPath *searchPathRegistry = nullptr;
109 
110 // Each module in the game should have a call from here to initialise
111 // any globals and statics to there default values each time the game
112 // or frontend restarts.
113 //
InitialiseGlobals()114 static bool InitialiseGlobals()
115 {
116 	frontendInitVars();	// Initialise frontend globals and statics.
117 	statsInitVars();
118 	structureInitVars();
119 	if (!messageInitVars())
120 	{
121 		return false;
122 	}
123 	if (!researchInitVars())
124 	{
125 		return false;
126 	}
127 	featureInitVars();
128 	radarInitVars();
129 	Edit3DInitVars();
130 
131 	return true;
132 }
133 
134 
loadLevFile(const char * filename,searchPathMode pathMode,bool ignoreWrf,char const * realFileName)135 bool loadLevFile(const char *filename, searchPathMode pathMode, bool ignoreWrf, char const *realFileName)
136 {
137 	char *pBuffer;
138 	UDWORD size;
139 
140 	if (realFileName == nullptr)
141 	{
142 		debug(LOG_WZ, "Loading lev file: \"%s\", builtin\n", filename);
143 	}
144 	else
145 	{
146 		debug(LOG_WZ, "Loading lev file: \"%s\" from \"%s\"\n", filename, realFileName);
147 	}
148 
149 	if (!PHYSFS_exists(filename) || !loadFile(filename, &pBuffer, &size))
150 	{
151 		debug(LOG_ERROR, "File not found: %s\n", filename);
152 		return false; // only in NDEBUG case
153 	}
154 	if (!levParse(pBuffer, size, pathMode, ignoreWrf, realFileName))
155 	{
156 		debug(LOG_ERROR, "Parse error in %s\n", filename);
157 		free(pBuffer);
158 		return false;
159 	}
160 	free(pBuffer);
161 
162 	return true;
163 }
164 
165 
cleanSearchPath()166 static void cleanSearchPath()
167 {
168 	wzSearchPath *curSearchPath = searchPathRegistry, * tmpSearchPath = nullptr;
169 
170 	// Start at the lowest priority
171 	while (curSearchPath->lowerPriority)
172 	{
173 		curSearchPath = curSearchPath->lowerPriority;
174 	}
175 
176 	while (curSearchPath)
177 	{
178 		tmpSearchPath = curSearchPath->higherPriority;
179 		free(curSearchPath);
180 		curSearchPath = tmpSearchPath;
181 	}
182 	searchPathRegistry = nullptr;
183 }
184 
185 
186 /*!
187  * Register searchPath above the path with next lower priority
188  * For information about what can be a search path, refer to PhysFS documentation
189  */
registerSearchPath(const char path[],unsigned int priority)190 void registerSearchPath(const char path[], unsigned int priority)
191 {
192 	wzSearchPath *curSearchPath = searchPathRegistry, * tmpSearchPath = nullptr;
193 
194 	tmpSearchPath = (wzSearchPath *)malloc(sizeof(*tmpSearchPath));
195 	sstrcpy(tmpSearchPath->path, path);
196 	if (path[strlen(path) - 1] != *PHYSFS_getDirSeparator())
197 	{
198 		sstrcat(tmpSearchPath->path, PHYSFS_getDirSeparator());
199 	}
200 	tmpSearchPath->priority = priority;
201 
202 	debug(LOG_WZ, "registerSearchPath: Registering %s at priority %i", path, priority);
203 	if (!curSearchPath)
204 	{
205 		searchPathRegistry = tmpSearchPath;
206 		searchPathRegistry->lowerPriority = nullptr;
207 		searchPathRegistry->higherPriority = nullptr;
208 		return;
209 	}
210 
211 	while (curSearchPath->higherPriority && priority > curSearchPath->priority)
212 	{
213 		curSearchPath = curSearchPath->higherPriority;
214 	}
215 	while (curSearchPath->lowerPriority && priority < curSearchPath->priority)
216 	{
217 		curSearchPath = curSearchPath->lowerPriority;
218 	}
219 
220 	if (priority < curSearchPath->priority)
221 	{
222 		tmpSearchPath->lowerPriority = curSearchPath->lowerPriority;
223 		tmpSearchPath->higherPriority = curSearchPath;
224 	}
225 	else
226 	{
227 		tmpSearchPath->lowerPriority = curSearchPath;
228 		tmpSearchPath->higherPriority = curSearchPath->higherPriority;
229 	}
230 
231 	if (tmpSearchPath->lowerPriority)
232 	{
233 		tmpSearchPath->lowerPriority->higherPriority = tmpSearchPath;
234 	}
235 	if (tmpSearchPath->higherPriority)
236 	{
237 		tmpSearchPath->higherPriority->lowerPriority = tmpSearchPath;
238 	}
239 }
240 
241 
242 /*!
243  * \brief Rebuilds the PHYSFS searchPath with mode specific subdirs
244  *
245  * Priority:
246  * maps > mods > base > base.wz
247  */
rebuildSearchPath(searchPathMode mode,bool force,const char * current_map)248 bool rebuildSearchPath(searchPathMode mode, bool force, const char *current_map)
249 {
250 	static searchPathMode current_mode = mod_clean;
251 	static std::string current_current_map;
252 	wzSearchPath *curSearchPath = searchPathRegistry;
253 	char tmpstr[PATH_MAX] = "\0";
254 
255 	if (mode != current_mode || (current_map != nullptr ? current_map : "") != current_current_map || force ||
256 	    (use_override_mods && override_mod_list != getModList()))
257 	{
258 		if (mode != mod_clean)
259 		{
260 			rebuildSearchPath(mod_clean, false);
261 		}
262 
263 		current_mode = mode;
264 		current_current_map = current_map != nullptr ? current_map : "";
265 
266 		// Start at the lowest priority
267 		while (curSearchPath->lowerPriority)
268 		{
269 			curSearchPath = curSearchPath->lowerPriority;
270 		}
271 
272 		switch (mode)
273 		{
274 		case mod_clean:
275 			debug(LOG_WZ, "Cleaning up");
276 			clearLoadedMods();
277 
278 			while (curSearchPath)
279 			{
280 #ifdef DEBUG
281 				debug(LOG_WZ, "Removing [%s] from search path", curSearchPath->path);
282 #endif // DEBUG
283 				// Remove maps and mods
284 				removeSubdirs(curSearchPath->path, "maps");
285 				removeSubdirs(curSearchPath->path, "mods/music");
286 				removeSubdirs(curSearchPath->path, "mods/global");
287 				removeSubdirs(curSearchPath->path, "mods");
288 				removeSubdirs(curSearchPath->path, "mods/autoload");
289 				removeSubdirs(curSearchPath->path, "mods/campaign");
290 				removeSubdirs(curSearchPath->path, "mods/multiplay");
291 				removeSubdirs(curSearchPath->path, "mods/downloads");
292 
293 				// Remove multiplay patches
294 				sstrcpy(tmpstr, curSearchPath->path);
295 				sstrcat(tmpstr, "mp");
296 				WZ_PHYSFS_unmount(tmpstr);
297 				sstrcpy(tmpstr, curSearchPath->path);
298 				sstrcat(tmpstr, "mp.wz");
299 				WZ_PHYSFS_unmount(tmpstr);
300 
301 				// Remove plain dir
302 				WZ_PHYSFS_unmount(curSearchPath->path);
303 
304 				// Remove base files
305 				sstrcpy(tmpstr, curSearchPath->path);
306 				sstrcat(tmpstr, "base");
307 				WZ_PHYSFS_unmount(tmpstr);
308 				sstrcpy(tmpstr, curSearchPath->path);
309 				sstrcat(tmpstr, "base.wz");
310 				WZ_PHYSFS_unmount(tmpstr);
311 
312 				// remove video search path as well
313 				sstrcpy(tmpstr, curSearchPath->path);
314 				sstrcat(tmpstr, "sequences.wz");
315 				WZ_PHYSFS_unmount(tmpstr);
316 				curSearchPath = curSearchPath->higherPriority;
317 			}
318 			break;
319 		case mod_campaign:
320 			debug(LOG_WZ, "*** Switching to campaign mods ***");
321 			clearLoadedMods();
322 
323 			while (curSearchPath)
324 			{
325 				// make sure videos override included files
326 				sstrcpy(tmpstr, curSearchPath->path);
327 				sstrcat(tmpstr, "sequences.wz");
328 				PHYSFS_mount(tmpstr, NULL, PHYSFS_APPEND);
329 				curSearchPath = curSearchPath->higherPriority;
330 			}
331 			curSearchPath = searchPathRegistry;
332 			while (curSearchPath->lowerPriority)
333 			{
334 				curSearchPath = curSearchPath->lowerPriority;
335 			}
336 			while (curSearchPath)
337 			{
338 #ifdef DEBUG
339 				debug(LOG_WZ, "Adding [%s] to search path", curSearchPath->path);
340 #endif // DEBUG
341 				// Add global and campaign mods
342 				PHYSFS_mount(curSearchPath->path, NULL, PHYSFS_APPEND);
343 
344 				addSubdirs(curSearchPath->path, "mods/music", PHYSFS_APPEND, nullptr, false);
345 				addSubdirs(curSearchPath->path, "mods/global", PHYSFS_APPEND, use_override_mods ? &override_mods : &global_mods, true);
346 				addSubdirs(curSearchPath->path, "mods", PHYSFS_APPEND, use_override_mods ? &override_mods : &global_mods, true);
347 				addSubdirs(curSearchPath->path, "mods/autoload", PHYSFS_APPEND, use_override_mods ? &override_mods : nullptr, true);
348 				addSubdirs(curSearchPath->path, "mods/campaign", PHYSFS_APPEND, use_override_mods ? &override_mods : &campaign_mods, true);
349 				if (!WZ_PHYSFS_unmount(curSearchPath->path))
350 				{
351 					debug(LOG_WZ, "* Failed to remove path %s again", curSearchPath->path);
352 				}
353 
354 				// Add plain dir
355 				PHYSFS_mount(curSearchPath->path, NULL, PHYSFS_APPEND);
356 
357 				// Add base files
358 				sstrcpy(tmpstr, curSearchPath->path);
359 				sstrcat(tmpstr, "base");
360 				PHYSFS_mount(tmpstr, NULL, PHYSFS_APPEND);
361 				sstrcpy(tmpstr, curSearchPath->path);
362 				sstrcat(tmpstr, "base.wz");
363 				PHYSFS_mount(tmpstr, NULL, PHYSFS_APPEND);
364 
365 				curSearchPath = curSearchPath->higherPriority;
366 			}
367 			break;
368 		case mod_multiplay:
369 			debug(LOG_WZ, "*** Switching to multiplay mods ***");
370 			clearLoadedMods();
371 
372 			while (curSearchPath)
373 			{
374 				// make sure videos override included files
375 				sstrcpy(tmpstr, curSearchPath->path);
376 				sstrcat(tmpstr, "sequences.wz");
377 				PHYSFS_mount(tmpstr, NULL, PHYSFS_APPEND);
378 				curSearchPath = curSearchPath->higherPriority;
379 			}
380 			// Add the selected map first, for mapmod support
381 			if (current_map != nullptr)
382 			{
383 				WzString realPathAndDir = WzString::fromUtf8(PHYSFS_getRealDir(current_map)) + current_map;
384 				realPathAndDir.replace("/", PHYSFS_getDirSeparator()); // Windows fix
385 				PHYSFS_mount(realPathAndDir.toUtf8().c_str(), NULL, PHYSFS_APPEND);
386 			}
387 			curSearchPath = searchPathRegistry;
388 			while (curSearchPath->lowerPriority)
389 			{
390 				curSearchPath = curSearchPath->lowerPriority;
391 			}
392 			while (curSearchPath)
393 			{
394 #ifdef DEBUG
395 				debug(LOG_WZ, "Adding [%s] to search path", curSearchPath->path);
396 #endif // DEBUG
397 				// Add global and multiplay mods
398 				PHYSFS_mount(curSearchPath->path, NULL, PHYSFS_APPEND);
399 				addSubdirs(curSearchPath->path, "mods/music", PHYSFS_APPEND, nullptr, false);
400 
401 				// Only load if we are host or singleplayer (Initial mod load relies on this, too)
402 				if (ingame.side == InGameSide::HOST_OR_SINGLEPLAYER || !NetPlay.bComms)
403 				{
404 					addSubdirs(curSearchPath->path, "mods/global", PHYSFS_APPEND, use_override_mods ? &override_mods : &global_mods, true);
405 					addSubdirs(curSearchPath->path, "mods", PHYSFS_APPEND, use_override_mods ? &override_mods : &global_mods, true);
406 					addSubdirs(curSearchPath->path, "mods/autoload", PHYSFS_APPEND, use_override_mods ? &override_mods : nullptr, true);
407 					addSubdirs(curSearchPath->path, "mods/multiplay", PHYSFS_APPEND, use_override_mods ? &override_mods : &multiplay_mods, true);
408 				}
409 				else
410 				{
411 					std::vector<std::string> hashList;
412 					for (Sha256 &hash : game.modHashes)
413 					{
414 						hashList = {hash.toString()};
415 						addSubdirs(curSearchPath->path, "mods/downloads", PHYSFS_APPEND, &hashList, true);
416 					}
417 				}
418 				WZ_PHYSFS_unmount(curSearchPath->path);
419 
420 				// Add multiplay patches
421 				sstrcpy(tmpstr, curSearchPath->path);
422 				sstrcat(tmpstr, "mp");
423 				PHYSFS_mount(tmpstr, NULL, PHYSFS_APPEND);
424 				sstrcpy(tmpstr, curSearchPath->path);
425 				sstrcat(tmpstr, "mp.wz");
426 				PHYSFS_mount(tmpstr, NULL, PHYSFS_APPEND);
427 
428 				// Add plain dir
429 				PHYSFS_mount(curSearchPath->path, NULL, PHYSFS_APPEND);
430 
431 				// Add base files
432 				sstrcpy(tmpstr, curSearchPath->path);
433 				sstrcat(tmpstr, "base");
434 				PHYSFS_mount(tmpstr, NULL, PHYSFS_APPEND);
435 				sstrcpy(tmpstr, curSearchPath->path);
436 				sstrcat(tmpstr, "base.wz");
437 				PHYSFS_mount(tmpstr, NULL, PHYSFS_APPEND);
438 
439 				curSearchPath = curSearchPath->higherPriority;
440 			}
441 			break;
442 		default:
443 			debug(LOG_ERROR, "Can't switch to unknown mods %i", mode);
444 			return false;
445 		}
446 		if (use_override_mods && mode != mod_clean)
447 		{
448 			if (getModList() != override_mod_list)
449 			{
450 				debug(LOG_POPUP, _("The required mod could not be loaded: %s\n\nWarzone will try to load the game without it."), override_mod_list.c_str());
451 			}
452 			clearOverrideMods();
453 			current_mode = mod_override;
454 		}
455 
456 		// User's home dir must be first so we always see what we write
457 		WZ_PHYSFS_unmount(PHYSFS_getWriteDir());
458 		PHYSFS_mount(PHYSFS_getWriteDir(), NULL, PHYSFS_PREPEND);
459 
460 #ifdef DEBUG
461 		printSearchPath();
462 #endif // DEBUG
463 	}
464 	else if (use_override_mods)
465 	{
466 		// override mods are already the same as current mods, so no need to do anything
467 		clearOverrideMods();
468 	}
469 	return true;
470 }
471 
472 struct MapFileListPath
473 {
474 public:
MapFileListPathMapFileListPath475 	MapFileListPath(const std::string& platformIndependentNotation, const std::string& platformDependentNotation)
476 	: platformIndependent(platformIndependentNotation)
477 	, platformDependent(platformDependentNotation)
478 	{ }
479 	std::string platformIndependent;
480 	std::string platformDependent;
481 };
482 typedef std::vector<MapFileListPath> MapFileList;
listMapFiles()483 static MapFileList listMapFiles()
484 {
485 	MapFileList ret, filtered;
486 	std::vector<std::string> oldSearchPath;
487 
488 	WZ_PHYSFS_enumerateFiles("maps", [&](const char *i) -> bool {
489 		std::string wzfile = i;
490 		if (i[0] == '.' || wzfile.substr(wzfile.find_last_of('.') + 1) != "wz")
491 		{
492 			return true; // continue;
493 		}
494 
495 		std::string realFileName_platformIndependent = std::string("maps") + "/" + i;
496 		std::string realFileName_platformDependent = std::string("maps") + PHYSFS_getDirSeparator() + i;
497 		ret.push_back(MapFileListPath(realFileName_platformIndependent, realFileName_platformDependent));
498 		return true; // continue
499 	});
500 
501 	// save our current search path(s)
502 	debug(LOG_WZ, "Map search paths:");
503 	char **searchPath = PHYSFS_getSearchPath();
504 	for (char **i = searchPath; *i != nullptr; i++)
505 	{
506 		debug(LOG_WZ, "    [%s]", *i);
507 		oldSearchPath.push_back(*i);
508 		WZ_PHYSFS_unmount(*i);
509 	}
510 	PHYSFS_freeList(searchPath);
511 
512 	for (const auto &realFileName : ret)
513 	{
514 		std::string realFilePathAndName = PHYSFS_getWriteDir() + realFileName.platformDependent;
515 		if (PHYSFS_mount(realFilePathAndName.c_str(), NULL, PHYSFS_APPEND))
516 		{
517 			int unsafe = 0;
518 			WZ_PHYSFS_enumerateFiles("multiplay/maps", [&unsafe, &realFilePathAndName](const char *file) -> bool {
519 				std::string isDir = std::string("multiplay/maps/") + file;
520 				if (WZ_PHYSFS_isDirectory(isDir.c_str()))
521 				{
522 					return true; // continue;
523 				}
524 				std::string checkfile = file;
525 				debug(LOG_WZ, "checking ... %s", file);
526 				if (checkfile.substr(checkfile.find_last_of('.') + 1) == "gam")
527 				{
528 					if (unsafe++ > 1)
529 					{
530 						debug(LOG_ERROR, "Map packs are not supported! %s NOT added.", realFilePathAndName.c_str());
531 						return false; // break;
532 					}
533 				}
534 				return true; // continue
535 			});
536 			if (unsafe < 2)
537 			{
538 				filtered.push_back(realFileName);
539 			}
540 			WZ_PHYSFS_unmount(realFilePathAndName.c_str());
541 		}
542 		else
543 		{
544 			debug(LOG_POPUP, "Could not mount %s, because: %s.\nPlease delete or move the file specified.", realFilePathAndName.c_str(), WZ_PHYSFS_getLastError());
545 		}
546 	}
547 
548 	// restore our search path(s) again
549 	for (const auto &restorePaths : oldSearchPath)
550 	{
551 		PHYSFS_mount(restorePaths.c_str(), NULL, PHYSFS_APPEND);
552 	}
553 	debug(LOG_WZ, "Search paths restored");
554 	printSearchPath();
555 
556 	return filtered;
557 }
558 
559 // Map processing
560 struct WZmapInfo
561 {
562 	bool isMapMod;
563 	bool isRandom;
564 };
565 
566 typedef std::string MapName;
567 typedef std::unordered_map<MapName, WZmapInfo> WZMapInfo_Map;
568 WZMapInfo_Map WZ_Maps;
569 
findMap(char const * name)570 static inline WZMapInfo_Map::iterator findMap(char const *name)
571 {
572 	return name != nullptr? WZ_Maps.find(name) : WZ_Maps.end();
573 }
574 
CheckForMod(char const * mapFile)575 bool CheckForMod(char const *mapFile)
576 {
577 	auto it = findMap(mapFile);
578 	if (it != WZ_Maps.end())
579 	{
580 		return it->second.isMapMod;
581 	}
582 	if (mapFile != nullptr)
583 	{
584 		debug(LOG_ERROR, "Couldn't find map %s", mapFile);
585 	}
586 
587 	return false;
588 }
589 
CheckForRandom(char const * mapFile,char const * mapDataFile0)590 bool CheckForRandom(char const *mapFile, char const *mapDataFile0)
591 {
592 	auto it = findMap(mapFile);
593 	if (it != WZ_Maps.end())
594 	{
595 		return it->second.isRandom;
596 	}
597 
598 	if (mapFile != nullptr) {
599 		debug(LOG_ERROR, "Couldn't find map %s", mapFile);
600 	}
601 	else if (mapDataFile0 != nullptr && strlen(mapDataFile0) > 4)
602 	{
603 		std::string fn = mapDataFile0;
604 		fn.resize(fn.size() - 4);
605 		fn += "/game.js";
606 		return PHYSFS_exists(fn.c_str());
607 	}
608 
609 	return false;
610 }
611 
612 // Mount the archive under the mountpoint, and enumerate the archive according to lookin
CheckInMap(const char * archive,const char * mountpoint,const std::vector<const char * > & lookin_list)613 static std::pair<bool, bool> CheckInMap(const char *archive, const char *mountpoint, const std::vector<const char *>& lookin_list)
614 {
615 	bool mapmod = false;
616 	bool isRandom = false;
617 
618 	if (!PHYSFS_mount(archive, mountpoint, PHYSFS_APPEND))
619 	{
620 		// We already checked to see if this was valid before, and now, something went seriously wrong.
621 		debug(LOG_FATAL, "Could not mount %s, because: %s. Please delete the file, and run the game again. Game will now exit.", archive, WZ_PHYSFS_getLastError());
622 		exit(-1);
623 	}
624 
625 	for (auto lookin : lookin_list)
626 	{
627 		std::string checkpath = lookin;
628 		checkpath.append("/");
629 		WZ_PHYSFS_enumerateFiles(lookin, [&](const char *file) -> bool {
630 			std::string checkfile = file;
631 			if (WZ_PHYSFS_isDirectory((checkpath + checkfile).c_str()))
632 			{
633 				if (checkfile.compare("wrf") == 0 || checkfile.compare("stats") == 0 || checkfile.compare("components") == 0
634 					|| checkfile.compare("effects") == 0 || checkfile.compare("messages") == 0
635 					|| checkfile.compare("audio") == 0 || checkfile.compare("sequenceaudio") == 0 || checkfile.compare("misc") == 0
636 					|| checkfile.compare("features") == 0 || checkfile.compare("script") == 0 || checkfile.compare("structs") == 0
637 					|| checkfile.compare("tileset") == 0 || checkfile.compare("images") == 0 || checkfile.compare("texpages") == 0
638 					|| checkfile.compare("skirmish") == 0 || checkfile.compare("shaders") == 0 || checkfile.compare("fonts") == 0
639 					|| checkfile.compare("icons") == 0)
640 				{
641 					debug(LOG_WZ, "Detected: %s %s" , archive, checkfile.c_str());
642 					mapmod = true;
643 					return false; // break;
644 				}
645 			}
646 			return true; // continue
647 		});
648 
649 		std::string maps = checkpath + "/multiplay/maps";
650 		WZ_PHYSFS_enumerateFiles(maps.c_str(), [&](const char *file) -> bool {
651 			if (WZ_PHYSFS_isDirectory((maps + "/" + file).c_str()) && PHYSFS_exists((maps + "/" + file + "/game.js").c_str()))
652 			{
653 				isRandom = true;
654 				return false; // break;
655 			}
656 			return true; // continue
657 		});
658 	}
659 
660 	if (!WZ_PHYSFS_unmount(archive))
661 	{
662 		debug(LOG_ERROR, "Could not unmount %s, %s", archive, WZ_PHYSFS_getLastError());
663 	}
664 	return {mapmod, isRandom};
665 }
666 
buildMapList()667 bool buildMapList()
668 {
669 	if (!loadLevFile("gamedesc.lev", mod_campaign, false, nullptr))
670 	{
671 		return false;
672 	}
673 	loadLevFile("addon.lev", mod_multiplay, false, nullptr);
674 	WZ_Maps.clear();
675 	MapFileList realFileNames = listMapFiles();
676 	const std::vector<const char *> lookin_list = { "WZMap", "WZMap/multiplay" };
677 	for (auto &realFileName : realFileNames)
678 	{
679 		struct WZmapInfo CurrentMap;
680 		const char * pRealDirStr = PHYSFS_getRealDir(realFileName.platformIndependent.c_str());
681 		if (!pRealDirStr)
682 		{
683 			debug(LOG_ERROR, "Failed to find realdir for: %s", realFileName.platformIndependent.c_str());
684 			continue; // skip
685 		}
686 		std::string realFilePathAndName = pRealDirStr + realFileName.platformDependent;
687 
688 		PHYSFS_mount(realFilePathAndName.c_str(), NULL, PHYSFS_APPEND);
689 
690 		WZ_PHYSFS_enumerateFiles("", [&](const char *file) -> bool {
691 			size_t len = strlen(file);
692 			if (len > 10 && !strcasecmp(file + (len - 10), ".addon.lev"))  // Do not add addon.lev again
693 			{
694 				loadLevFile(file, mod_multiplay, true, realFileName.platformIndependent.c_str());
695 			}
696 			// add support for X player maps using a new name to prevent conflicts.
697 			if (len > 13 && !strcasecmp(file + (len - 13), ".xplayers.lev"))
698 			{
699 				loadLevFile(file, mod_multiplay, true, realFileName.platformIndependent.c_str());
700 			}
701 			return true; // continue
702 		});
703 
704 		if (WZ_PHYSFS_unmount(realFilePathAndName.c_str()) == 0)
705 		{
706 			debug(LOG_ERROR, "Could not unmount %s, %s", realFilePathAndName.c_str(), WZ_PHYSFS_getLastError());
707 		}
708 
709 		auto chk = CheckInMap(realFilePathAndName.c_str(), "WZMap", lookin_list);
710 
711 		const std::string& MapName = realFileName.platformIndependent;
712 		CurrentMap.isMapMod = chk.first;
713 		CurrentMap.isRandom = chk.second;
714 		WZ_Maps.insert(WZMapInfo_Map::value_type(MapName, std::move(CurrentMap)));
715 	}
716 
717 	return true;
718 }
719 
720 // ////////////////////////////////////////////////////////////////////////////
721 // ////////////////////////////////////////////////////////////////////////////
722 // Called once on program startup.
723 //
systemInitialise(float horizScaleFactor,float vertScaleFactor)724 bool systemInitialise(float horizScaleFactor, float vertScaleFactor)
725 {
726 	if (!widgInitialise())
727 	{
728 		return false;
729 	}
730 
731 	if (!notificationsInitialize())
732 	{
733 		return false;
734 	}
735 
736 	buildMapList();
737 
738 	// Initialize render engine
739 	if (!pie_Initialise())
740 	{
741 		debug(LOG_ERROR, "Unable to initialise renderer");
742 		return false;
743 	}
744 
745 	if (!audio_Init(droidAudioTrackStopped, war_GetHRTFMode(), war_getSoundEnabled()))
746 	{
747 		debug(LOG_SOUND, "Continuing without audio");
748 	}
749 	if (war_getSoundEnabled() && war_GetMusicEnabled())
750 	{
751 		cdAudio_Open(UserMusicPath);
752 	}
753 	else
754 	{
755 		debug(LOG_SOUND, "Music disabled");
756 	}
757 
758 	if (!dataInitLoadFuncs()) // Pass all the data loading functions to the framework library
759 	{
760 		return false;
761 	}
762 
763 	// Initialize the iVis text rendering module
764 	wzSceneBegin("Main menu loop");
765 	iV_TextInit(horizScaleFactor, vertScaleFactor);
766 
767 	pie_InitRadar();
768 
769 	readAIs();
770 
771 	return true;
772 }
773 
774 // ////////////////////////////////////////////////////////////////////////////
775 // ////////////////////////////////////////////////////////////////////////////
776 // Called once at program shutdown.
777 //
systemShutdown()778 void systemShutdown()
779 {
780 	pie_ShutdownRadar();
781 	clearLoadedMods();
782 	flushConsoleMessages();
783 
784 	shutdownEffectsSystem();
785 	wzSceneEnd(nullptr);  // Might want to end the "Main menu loop" or "Main game loop".
786 	keyMappings.clear();
787 
788 	// free up all the load functions (all the data should already have been freed)
789 	resReleaseAll();
790 
791 	if (!multiShutdown()) // ajl. init net stuff
792 	{
793 		debug(LOG_FATAL, "Unable to multiShutdown() cleanly!");
794 		abort();
795 	}
796 
797 	// shut down various databases
798 	shutdownKnownPlayers();
799 
800 	debug(LOG_MAIN, "shutting down audio subsystems");
801 
802 	debug(LOG_MAIN, "shutting down CD audio");
803 	cdAudio_Close();
804 
805 	if (audio_Disabled() == false && !audio_Shutdown())
806 	{
807 		debug(LOG_FATAL, "Unable to audio_Shutdown() cleanly!");
808 		abort();
809 	}
810 
811 	debug(LOG_MAIN, "shutting down graphics subsystem");
812 	levShutDown();
813 	notificationsShutDown();
814 	widgShutDown();
815 	fpathShutdown();
816 	mapShutdown();
817 	debug(LOG_MAIN, "shutting down everything else");
818 	pal_ShutDown();		// currently unused stub
819 	frameShutDown();	// close screen / SDL / resources / cursors / trig
820 	screenShutDown();
821 	gfx_api::context::get().shutdown();
822 	cleanSearchPath();	// clean PHYSFS search paths
823 	debug_exit();		// cleanup debug routines
824 	PHYSFS_deinit();	// cleanup PHYSFS (If failure, state of PhysFS is undefined, and probably badly screwed up.)
825 	// NOTE: Exception handler is cleaned via atexit(ExchndlShutdown);
826 }
827 
828 /***************************************************************************/
829 
830 // ////////////////////////////////////////////////////////////////////////////
831 // ////////////////////////////////////////////////////////////////////////////
832 // Called At Frontend Startup.
833 
frontendInitialise(const char * ResourceFile)834 bool frontendInitialise(const char *ResourceFile)
835 {
836 	debug(LOG_WZ, "== Initializing frontend == : %s", ResourceFile);
837 
838 	if (!InitialiseGlobals())				// Initialise all globals and statics everywhere.
839 	{
840 		return false;
841 	}
842 
843 	if (!stringsInitialise())				// Initialise the string system
844 	{
845 		return false;
846 	}
847 
848 	if (!objInitialise())					// Initialise the object system
849 	{
850 		return false;
851 	}
852 
853 	if (!allocPlayerPower())	 //set up the PlayerPower for each player - this should only be done ONCE now
854 	{
855 		return false;
856 	}
857 
858 	debug(LOG_MAIN, "frontEndInitialise: loading resource file .....");
859 	if (!resLoad(ResourceFile, 0))
860 	{
861 		//need the object heaps to have been set up before loading in the save game
862 		return false;
863 	}
864 
865 	if (!dispInitialise())					// Initialise the display system
866 	{
867 		return false;
868 	}
869 
870 	FrontImages = (IMAGEFILE *)resGetData("IMG", "frontend.img");
871 	/* Shift the interface initialisation here temporarily so that it
872 		can pick up the stats after they have been loaded */
873 	if (!intInitialise())
874 	{
875 		return false;
876 	}
877 
878 	// reinitialise key mappings
879 	keyInitMappings(false);
880 
881 	// Set the default uncoloured cursor here, since it looks slightly
882 	// better for menus and such.
883 	wzSetCursor(CURSOR_DEFAULT);
884 
885 	SetFormAudioIDs(-1, ID_SOUND_WINDOWCLOSE);			// disable the open noise since distorted in 3dfx builds.
886 
887 	initMiscVars();
888 
889 	gameTimeInit();
890 
891 	// hit me with some funky beats....
892 	cdAudio_PlayTrack(SONG_FRONTEND);
893 
894 	return true;
895 }
896 
897 
frontendShutdown()898 bool frontendShutdown()
899 {
900 	debug(LOG_WZ, "== Shutting down frontend ==");
901 
902 	saveConfig();// save settings to registry.
903 
904 	if (!mechanicsShutdown())
905 	{
906 		return false;
907 	}
908 
909 	interfaceShutDown();
910 
911 	//do this before shutting down the iV library
912 	resReleaseAllData();
913 
914 	if (!objShutdown())
915 	{
916 		return false;
917 	}
918 
919 	ResearchRelease();
920 
921 	debug(LOG_TEXTURE, "=== frontendShutdown ===");
922 	modelShutdown();
923 	pie_TexShutDown();
924 	pie_TexInit(); // ready for restart
925 	freeComponentLists();
926 	statsShutDown();
927 
928 	return true;
929 }
930 
931 
932 /******************************************************************************/
933 /*                       Initialisation before data is loaded                 */
934 
935 
936 
stageOneInitialise()937 bool stageOneInitialise()
938 {
939 	debug(LOG_WZ, "== stageOneInitialise ==");
940 	wzSceneEnd("Main menu loop");
941 	wzSceneBegin("Main game loop");
942 
943 	// Initialise all globals and statics everywhere.
944 	if (!InitialiseGlobals())
945 	{
946 		return false;
947 	}
948 
949 	if (!stringsInitialise())	/* Initialise the string system */
950 	{
951 		return false;
952 	}
953 
954 	if (!objInitialise())		/* Initialise the object system */
955 	{
956 		return false;
957 	}
958 
959 	if (!droidInit())
960 	{
961 		return false;
962 	}
963 
964 	if (!initViewData())
965 	{
966 		return false;
967 	}
968 
969 	if (!grpInitialise())
970 	{
971 		return false;
972 	}
973 
974 	if (!aiInitialise())		/* Initialise the AI system */ // pregame
975 	{
976 		return false;
977 	}
978 
979 	if (!allocPlayerPower())	/*set up the PlayerPower for each player - this should only be done ONCE now*/
980 	{
981 		return false;
982 	}
983 
984 	// initialise the visibility stuff
985 	if (!visInitialise())
986 	{
987 		return false;
988 	}
989 
990 	if (!proj_InitSystem())
991 	{
992 		return false;
993 	}
994 
995 	if (!gridInitialise())
996 	{
997 		return false;
998 	}
999 
1000 	initMission();
1001 	initTransporters();
1002 	scriptInit();
1003 
1004 	gameTimeInit();
1005 	transitionInit();
1006 	resetScroll();
1007 
1008 	return true;
1009 }
1010 
1011 
1012 /******************************************************************************/
1013 /*                       Shutdown after data is released                      */
1014 
stageOneShutDown()1015 bool stageOneShutDown()
1016 {
1017 	debug(LOG_WZ, "== stageOneShutDown ==");
1018 
1019 	atmosSetWeatherType(WT_NONE); // reset weather and free its data
1020 	wzPerfShutdown();
1021 
1022 	pie_FreeShaders();
1023 
1024 	if (audio_Disabled() == false)
1025 	{
1026 		sound_CheckAllUnloaded();
1027 	}
1028 
1029 	proj_Shutdown();
1030 
1031 	releaseMission();
1032 
1033 	if (!aiShutdown())
1034 	{
1035 		return false;
1036 	}
1037 
1038 	if (!objShutdown())
1039 	{
1040 		return false;
1041 	}
1042 
1043 	grpShutDown();
1044 
1045 	ResearchRelease();
1046 
1047 	//free up the gateway stuff?
1048 	gwShutDown();
1049 
1050 	shutdownTerrain();
1051 
1052 	if (!mapShutdown())
1053 	{
1054 		return false;
1055 	}
1056 
1057 	gridShutDown();
1058 
1059 	debug(LOG_TEXTURE, "== stageOneShutDown ==");
1060 	modelShutdown();
1061 	pie_TexShutDown();
1062 
1063 	// Use mod_multiplay as the default (campaign might have set it to mod_singleplayer)
1064 	rebuildSearchPath(mod_multiplay, true);
1065 	pie_TexInit(); // restart it
1066 
1067 	initMiscVars();
1068 	wzSceneEnd("Main game loop");
1069 	wzSceneBegin("Main menu loop");
1070 
1071 	return true;
1072 }
1073 
1074 
1075 // ////////////////////////////////////////////////////////////////////////////
1076 // Initialise after the base data is loaded but before final level data is loaded
1077 
stageTwoInitialise()1078 bool stageTwoInitialise()
1079 {
1080 	int i;
1081 
1082 	debug(LOG_WZ, "== stageTwoInitialise ==");
1083 
1084 	// prevent "double up" and "biffer baker" cheats from messing up damage modifiers
1085 	resetDamageModifiers();
1086 
1087 	// make sure we clear on loading; this a bad hack to fix a bug when
1088 	// loading a savegame where we are building a lassat
1089 	for (i = 0; i < MAX_PLAYERS; i++)
1090 	{
1091 		setLasSatExists(false, i);
1092 	}
1093 
1094 	if (!dispInitialise())		/* Initialise the display system */
1095 	{
1096 		return false;
1097 	}
1098 
1099 	if (!initMiscImds())			/* Set up the explosions */
1100 	{
1101 		debug(LOG_FATAL, "Can't find all the explosions graphics?");
1102 		abort();
1103 		return false;
1104 	}
1105 
1106 	if (!cmdDroidInit())
1107 	{
1108 		return false;
1109 	}
1110 
1111 	/* Shift the interface initialisation here temporarily so that it
1112 		can pick up the stats after they have been loaded */
1113 
1114 	if (!intInitialise())
1115 	{
1116 		return false;
1117 	}
1118 
1119 	if (!initMessage())			/* Initialise the message heaps */
1120 	{
1121 		return false;
1122 	}
1123 
1124 	if (!gwInitialise())
1125 	{
1126 		return false;
1127 	}
1128 
1129 	if (!initScripts())		// Initialise the new javascript system
1130 	{
1131 		return false;
1132 	}
1133 
1134 	// reinitialise key mappings
1135 	keyInitMappings(false);
1136 
1137 	// Set the default uncoloured cursor here, since it looks slightly
1138 	// better for menus and such.
1139 	wzSetCursor(CURSOR_DEFAULT);
1140 
1141 	SetFormAudioIDs(ID_SOUND_WINDOWOPEN, ID_SOUND_WINDOWCLOSE);
1142 
1143 	// Setup game queues.
1144 	// Don't ask why this doesn't go in stage three. In fact, don't even ask me what stage one/two/three is supposed to mean, it seems about as descriptive as stage doStuff, stage doMoreStuff and stage doEvenMoreStuff...
1145 	debug(LOG_MAIN, "Init game queues, I am %d.", selectedPlayer);
1146 	sendQueuedDroidInfo();  // Discard any pending orders which could later get flushed into the game queue.
1147 	for (i = 0; i < MAX_PLAYERS; ++i)
1148 	{
1149 		NETinitQueue(NETgameQueue(i));
1150 
1151 		if (!myResponsibility(i))
1152 		{
1153 			NETsetNoSendOverNetwork(NETgameQueue(i));
1154 		}
1155 	}
1156 
1157 	// NOTE:
1158 	// - Loading savegames calls `fPathDroidRoute` (from `loadSaveDroid`) before `stageThreeInitialise`
1159 	//   is called, hence removing this call to `fpathInitialise` will currently break loading certain
1160 	//   savegames (if they interact with the fPath system).
1161 	if (!fpathInitialise())
1162 	{
1163 		return false;
1164 	}
1165 
1166 	debug(LOG_MAIN, "stageTwoInitialise: done");
1167 
1168 	return true;
1169 }
1170 
1171 // ////////////////////////////////////////////////////////////////////////////
1172 // ////////////////////////////////////////////////////////////////////////////
1173 // Free up after level specific data has been released but before base data is released
1174 //
stageTwoShutDown()1175 bool stageTwoShutDown()
1176 {
1177 	debug(LOG_WZ, "== stageTwoShutDown ==");
1178 
1179 	fpathShutdown();
1180 
1181 	cdAudio_Stop();
1182 
1183 	freeAllStructs();
1184 	freeAllDroids();
1185 	freeAllFeatures();
1186 	freeAllFlagPositions();
1187 
1188 	if (!messageShutdown())
1189 	{
1190 		return false;
1191 	}
1192 
1193 	if (!mechanicsShutdown())
1194 	{
1195 		return false;
1196 	}
1197 
1198 
1199 	if (!ShutdownRadar())
1200 	{
1201 		return false;
1202 	}
1203 
1204 	interfaceShutDown();
1205 
1206 	cmdDroidShutDown();
1207 
1208 	//free up the gateway stuff?
1209 	gwShutDown();
1210 
1211 	if (!mapShutdown())
1212 	{
1213 		return false;
1214 	}
1215 
1216 	shutdown3DView();
1217 
1218 	return true;
1219 }
1220 
stageThreeInitialise()1221 bool stageThreeInitialise()
1222 {
1223 	STRUCTURE *psStr;
1224 	UDWORD i;
1225 	DROID *psDroid;
1226 	bool fromSave = (getSaveGameType() == GTYPE_SAVE_START || getSaveGameType() == GTYPE_SAVE_MIDMISSION);
1227 
1228 	debug(LOG_WZ, "== stageThreeInitialise ==");
1229 
1230 	loopMissionState = LMS_NORMAL;
1231 
1232 	if (!InitRadar()) 	// After resLoad cause it needs the game palette initialised.
1233 	{
1234 		return false;
1235 	}
1236 
1237 	// reset the clock to normal speed
1238 	gameTimeResetMod();
1239 
1240 	if (!init3DView())	// Initialise 3d view stuff. After resLoad cause it needs the game palette initialised.
1241 	{
1242 		return false;
1243 	}
1244 
1245 	effectResetUpdates();
1246 	initLighting(0, 0, mapWidth, mapHeight);
1247 	pie_InitLighting();
1248 
1249 	if (fromSave)
1250 	{
1251 		// these two lines are the biggest hack in the world.
1252 		// the reticule seems to get detached from 'reticuleup'
1253 		// this forces it back in sync...
1254 		intRemoveReticule();
1255 		intAddReticule();
1256 	}
1257 
1258 	if (bMultiPlayer)
1259 	{
1260 		multiGameInit();
1261 		initTemplates();
1262 	}
1263 
1264 	preProcessVisibility();
1265 
1266 	prepareScripts(getLevelLoadType() == GTYPE_SAVE_MIDMISSION || getLevelLoadType() == GTYPE_SAVE_START);
1267 
1268 	if (!fpathInitialise())
1269 	{
1270 		return false;
1271 	}
1272 
1273 	mapInit();
1274 	gridReset();
1275 
1276 	//if mission screen is up, close it.
1277 	if (MissionResUp)
1278 	{
1279 		intRemoveMissionResultNoAnim();
1280 	}
1281 
1282 	// Re-inititialise some static variables.
1283 
1284 	bInTutorial = false;
1285 	rangeOnScreen = false;
1286 
1287 	if (fromSave && ActivityManager::instance().getCurrentGameMode() == ActivitySink::GameMode::CHALLENGE)
1288 	{
1289 		challengeActive = true;
1290 	}
1291 
1292 	resizeRadar();
1293 
1294 	setAllPauseStates(false);
1295 
1296 	/* decide if we have to create teams, ONLY in multiplayer mode!*/
1297 	if (bMultiPlayer && alliancesSharedVision(game.alliance))
1298 	{
1299 		createTeamAlliances();
1300 
1301 		/* Update ally vision for pre-placed structures and droids */
1302 		for (i = 0; i < MAX_PLAYERS; i++)
1303 		{
1304 			if (i != selectedPlayer)
1305 			{
1306 				/* Structures */
1307 				for (psStr = apsStructLists[i]; psStr; psStr = psStr->psNext)
1308 				{
1309 					if (aiCheckAlliances(psStr->player, selectedPlayer))
1310 					{
1311 						visTilesUpdate((BASE_OBJECT *)psStr);
1312 					}
1313 				}
1314 
1315 				/* Droids */
1316 				for (psDroid = apsDroidLists[i]; psDroid; psDroid = psDroid->psNext)
1317 				{
1318 					if (aiCheckAlliances(psDroid->player, selectedPlayer))
1319 					{
1320 						visTilesUpdate((BASE_OBJECT *)psDroid);
1321 					}
1322 				}
1323 			}
1324 		}
1325 	}
1326 
1327 	countUpdate();
1328 
1329 	if (getLevelLoadType() == GTYPE_SAVE_MIDMISSION || getLevelLoadType() == GTYPE_SAVE_START)
1330 	{
1331 		triggerEvent(TRIGGER_GAME_LOADED);
1332 	}
1333 	else
1334 	{
1335 		if (getDebugMappingStatus())
1336 		{
1337 			triggerEventCheatMode(true);
1338 		}
1339 		triggerEvent(TRIGGER_GAME_INIT);
1340 		playerBuiltHQ = structureExists(selectedPlayer, REF_HQ, true, false);
1341 	}
1342 
1343 	// Start / randomize in-game music
1344 	cdAudio_PlayTrack(SONG_INGAME);
1345 
1346 	return true;
1347 }
1348 
1349 /*****************************************************************************/
1350 /*      Shutdown before any data is released                                 */
1351 
stageThreeShutDown()1352 bool stageThreeShutDown()
1353 {
1354 	debug(LOG_WZ, "== stageThreeShutDown ==");
1355 
1356 	setHostLaunch(HostLaunch::Normal);
1357 
1358 	removeSpotters();
1359 
1360 	// There is an asymmetry in scripts initialization and destruction, due
1361 	// the many different ways scripts get loaded.
1362 	if (!shutdownScripts())
1363 	{
1364 		return false;
1365 	}
1366 
1367 	challengesUp = false;
1368 	challengeActive = false;
1369 	isInGamePopupUp = false;
1370 	InGameOpUp = false;
1371 	bInTutorial = false;
1372 
1373 	shutdownTemplates();
1374 
1375 	// make sure any button tips are gone.
1376 	widgReset();
1377 
1378 	audio_StopAll();
1379 
1380 	if (bMultiPlayer)
1381 	{
1382 		multiGameShutdown();
1383 	}
1384 
1385 	//call this here before mission data is released
1386 	if (!missionShutDown())
1387 	{
1388 		return false;
1389 	}
1390 
1391 	setScriptWinLoseVideo(PLAY_NONE);
1392 
1393 	return true;
1394 }
1395 
1396 // Reset the game between campaigns
campaignReset()1397 bool campaignReset()
1398 {
1399 	debug(LOG_MAIN, "campaignReset");
1400 	gwShutDown();
1401 	mapShutdown();
1402 	shutdownTerrain();
1403 	// when the terrain textures are reloaded we need to reset the radar
1404 	// otherwise it will end up as a terrain texture somehow
1405 	ShutdownRadar();
1406 	InitRadar();
1407 	return true;
1408 }
1409 
1410 // Reset the game when loading a save game
saveGameReset()1411 bool saveGameReset()
1412 {
1413 	debug(LOG_MAIN, "saveGameReset");
1414 
1415 	cdAudio_Stop();
1416 
1417 	freeAllStructs();
1418 	freeAllDroids();
1419 	freeAllFeatures();
1420 	freeAllFlagPositions();
1421 	initMission();
1422 	initTransporters();
1423 
1424 	//free up the gateway stuff?
1425 	gwShutDown();
1426 	intResetScreen(true);
1427 
1428 	if (!mapShutdown())
1429 	{
1430 		return false;
1431 	}
1432 
1433 	freeMessages();
1434 
1435 	return true;
1436 }
1437 
1438 // --- Miscellaneous Initialisation stuff that really should be done each restart
initMiscVars()1439 static void initMiscVars()
1440 {
1441 	selectedPlayer = 0;
1442 	realSelectedPlayer = 0;
1443 	godMode = false;
1444 
1445 	radarOnScreen = true;
1446 	radarPermitted = true;
1447 	allowDesign = true;
1448 	includeRedundantDesigns = false;
1449 	enableConsoleDisplay(true);
1450 
1451 	for (unsigned n = 0; n < MAX_PLAYERS; ++n)
1452 	{
1453 		processDebugMappings(n, false);
1454 	}
1455 }
1456