1 #include "DefaultContentManager.h"
2 
3 #include "Exceptions.h"
4 #include "game/Directories.h"
5 #include "game/Strategic/Strategic_Status.h"
6 #include "game/Tactical/Items.h"
7 #include "game/Tactical/Weapons.h"
8 
9 // XXX: GameRes.h should be integrated to ContentManager
10 #include "game/GameRes.h"
11 
12 // XXX
13 #include "game/GameState.h"
14 
15 #include "sgp/FileMan.h"
16 #include "sgp/MemMan.h"
17 
18 #include "AmmoTypeModel.h"
19 #include "CacheSectorsModel.h"
20 #include "CalibreModel.h"
21 #include "ContentMusic.h"
22 #include "DealerInventory.h"
23 #include "DealerModel.h"
24 #include "JsonObject.h"
25 #include "JsonUtility.h"
26 #include "LoadingScreenModel.h"
27 #include "MagazineModel.h"
28 #include "RustInterface.h"
29 #include "ShippingDestinationModel.h"
30 #include "WeaponModels.h"
31 #include "army/ArmyCompositionModel.h"
32 #include "army/GarrisonGroupModel.h"
33 #include "army/PatrolGroupModel.h"
34 #include "mercs/MERCListingModel.h"
35 #include "mercs/MercProfileInfo.h"
36 #include "mercs/RPCSmallFaceModel.h"
37 #include "policy/DefaultGamePolicy.h"
38 #include "policy/DefaultIMPPolicy.h"
39 #include "policy/DefaultStrategicAIPolicy.h"
40 #include "strategic/BloodCatPlacementsModel.h"
41 #include "strategic/BloodCatSpawnsModel.h"
42 #include "strategic/CreatureLairModel.h"
43 #include "strategic/FactParamsModel.h"
44 #include "strategic/MineModel.h"
45 #include "strategic/SamSiteModel.h"
46 #include "strategic/SamSiteAirControlModel.h"
47 #include "strategic/SectorLandTypes.h"
48 #include "strategic/StrategicMapSecretModel.h"
49 #include "strategic/TownModel.h"
50 #include "strategic/TraversibilityMapping.h"
51 #include "strategic/MovementCostsModel.h"
52 #include "strategic/NpcPlacementModel.h"
53 #include "strategic/UndergroundSectorModel.h"
54 #include "tactical/MapItemReplacementModel.h"
55 #include "tactical/NpcActionParamsModel.h"
56 
57 #include "Logger.h"
58 #include "Strategic_AI.h"
59 #include "Soldier_Profile_Type.h"
60 
61 #include "rapidjson/error/en.h"
62 #include <string_theory/format>
63 #include <string_theory/string>
64 
65 #include <stdexcept>
66 
67 #define BASEDATADIR    "data"
68 
69 #define MAPSDIR        "maps"
70 #define RADARMAPSDIR   "radarmaps"
71 #define TILESETSDIR    "tilesets"
72 
73 #define PRINT_OPENING_FILES (0)
74 
75 #define DIALOGUESIZE 240
76 
77 // XXX: Issue #135
78 // We need a safe way to create temporary directories - unique and random for every process
79 // Boost probably provides this functionality
80 #define NEW_TEMP_DIR "temp"
81 
82 const MercProfileInfo EMPTY_MERC_PROFILE_INFO;
83 
LoadEncryptedData(ST::string & err_msg,STRING_ENC_TYPE encType,SGPFile * File,UINT32 seek_chars,UINT32 read_chars)84 static ST::string LoadEncryptedData(ST::string& err_msg, STRING_ENC_TYPE encType, SGPFile* File, UINT32 seek_chars, UINT32 read_chars)
85 {
86 	FileSeek(File, seek_chars * 2, FILE_SEEK_FROM_START);
87 
88 	ST::utf16_buffer buf(read_chars, u'\0');
89 	FileRead(File, buf.data(), sizeof(char16_t) * read_chars);
90 
91 	buf[read_chars - 1] = u'\0';
92 	for (char16_t* i = buf.data(); *i != u'\0'; ++i)
93 	{
94 		/* "Decrypt" the ROT-1 "encrypted" data */
95 		char16_t c = (*i > 33 ? *i - 1 : *i);
96 
97 		if(encType == SE_RUSSIAN)
98 		{
99 			/* The Russian data files are incorrectly encoded. The original texts seem to
100 			 * be encoded in CP1251, but then they were converted from CP1252 (!) to
101 			 * UTF-16 to store them in the data files. Undo this damage here. */
102 			if (0xC0 <= c && c <= 0xFF) c += 0x0350;
103 		}
104 		else
105 		{
106 			if(encType == SE_ENGLISH)
107 			{
108 				/* The English data files are incorrectly encoded. The original texts seem
109 				 * to be encoded in CP437, but then they were converted from CP1252 (!) to
110 				 * UTF-16 to store them in the data files. Undo this damage here. This
111 				 * problem only occurs for a few lines by Malice. */
112 				switch (c)
113 				{
114 					case 128: c = 0x00C7; break; // Ç
115 					case 130: c = 0x00E9; break; // é
116 					case 135: c = 0x00E7; break; // ç
117 				}
118 			}
119 			else if(encType == SE_POLISH)
120 			{
121 				/* The Polish data files are incorrectly encoded. The original texts seem to
122 				 * be encoded in CP1250, but then they were converted from CP1252 (!) to
123 				 * UTF-16 to store them in the data files. Undo this damage here.
124 				 * Also the format code for centering texts differs. */
125 				switch (c)
126 				{
127 					case 143: c = 0x0179; break;
128 					case 163: c = 0x0141; break;
129 					case 165: c = 0x0104; break;
130 					case 175: c = 0x017B; break;
131 					case 179: c = 0x0142; break;
132 					case 182: c = 179;    break; // not a char, but a format code (centering)
133 					case 185: c = 0x0105; break;
134 					case 191: c = 0x017C; break;
135 					case 198: c = 0x0106; break;
136 					case 202: c = 0x0118; break;
137 					case 209: c = 0x0143; break;
138 					case 230: c = 0x0107; break;
139 					case 234: c = 0x0119; break;
140 					case 241: c = 0x0144; break;
141 					case 338: c = 0x015A; break;
142 					case 339: c = 0x015B; break;
143 					case 376: c = 0x017A; break;
144 				}
145 			}
146 
147 			/* Cyrillic texts (by Ivan Dolvich) in the non-Russian versions are encoded
148 			 * in some wild manner. Undo this damage here. */
149 			if (0x044D <= c && c <= 0x0452) // cyrillic A to IE
150 			{
151 				c += -0x044D + 0x0410;
152 			}
153 			else if (c == 0x0453) // cyrillic IO
154 			{
155 				c = 0x0401;
156 			}
157 			else if (0x0454 <= c && c <= 0x0467) // cyrillic ZHE to SHCHA
158 			{
159 				c += -0x0454 + 0x0416;
160 			}
161 			else if (0x0468 <= c && c <= 0x046C) // cyrillic YERU to YA
162 			{
163 				c += -0x0468 + 0x042B;
164 			}
165 		}
166 
167 		*i = c;
168 	}
169 	return st_checked_buffer_to_string(err_msg, buf);
170 }
171 
DefaultContentManager(GameVersion gameVersion,const ST::string & userHomeDir,const ST::string & gameResRootPath,const ST::string & externalizedDataPath)172 DefaultContentManager::DefaultContentManager(GameVersion gameVersion,
173 						const ST::string &userHomeDir,
174 						const ST::string &gameResRootPath,
175 						const ST::string &externalizedDataPath
176 	)
177 	:m_gameVersion(gameVersion),
178 	mNormalGunChoice(ARMY_GUN_LEVELS),
179 	mExtendedGunChoice(ARMY_GUN_LEVELS),
180 	m_vfs(Vfs_create())
181 {
182 	/*
183 	 * Searching actual paths to directories 'Data' and 'Data/Tilecache', 'Data/Maps'
184 	 * On case-sensitive filesystems that might be tricky: if such directories
185 	 * exist we should use them.  If doesn't exist, then use lowercased names.
186 	 */
187 
188 	m_userHomeDir = userHomeDir;
189 	m_gameResRootPath = gameResRootPath;
190 	m_externalizedDataPath = externalizedDataPath;
191 
192 	RustPointer<char> path{Fs_resolveExistingComponents(BASEDATADIR, m_gameResRootPath.c_str(), true)};
193 	m_dataDir = path.get();
194 
195 	path.reset(Fs_resolveExistingComponents(TILECACHEDIR, m_dataDir.c_str(), true));
196 	m_tileDir = path.get();
197 
198 	m_bobbyRayNewInventory = NULL;
199 	m_bobbyRayUsedInventory = NULL;
200 	m_impPolicy = NULL;
201 	m_gamePolicy = NULL;
202 	m_strategicAIPolicy = NULL;
203 
204 	m_cacheSectors = NULL;
205 	m_movementCosts = NULL;
206 	m_loadingScreenModel = NULL;
207 	m_samSitesAirControl = NULL;
208 }
209 
init(EngineOptions * engine_options)210 void DefaultContentManager::init(EngineOptions* engine_options)
211 {
212 	auto succeeded = Vfs_init_from_engine_options(m_vfs.get(), engine_options);
213 	if (!succeeded) {
214 		RustPointer<char> err{ getRustError() };
215 		auto error = ST::format("Failed to build virtual file system (VFS): {}", err.get());
216 		SLOGE(error);
217 		throw std::runtime_error(error.c_str());
218 	}
219 }
220 
221 template <class T>
deleteElements(std::vector<const T * > vec)222 void deleteElements(std::vector<const T*> vec)
223 {
224 	for (auto elem : vec)
225 	{
226 		delete elem;
227 	}
228 	vec.clear();
229 }
230 
231 template <typename K, class V>
deleteElements(std::map<K,const V * > map)232 void deleteElements(std::map<K, const V*> map)
233 {
234 	for (auto& kv : map)
235 	{
236 		delete kv.second;
237 	}
238 	map.clear();
239 }
240 
~DefaultContentManager()241 DefaultContentManager::~DefaultContentManager()
242 {
243 	for (const ItemModel* item : m_items)
244 	{
245 		delete item;
246 	}
247 	m_items.clear();
248 	m_magazineMap.clear();
249 	m_magazines.clear();
250 	m_weaponMap.clear();
251 	m_itemMap.clear();
252 	m_mapItemReplacements.clear();
253 
254 	m_armyCompositions.clear();
255 	m_garrisonGroups.clear();
256 	m_patrolGroups.clear();
257 
258 	for (const CalibreModel* calibre : m_calibres)
259 	{
260 		delete calibre;
261 	}
262 	m_calibres.clear();
263 	m_calibreMap.clear();
264 
265 	for (const AmmoTypeModel* ammoType : m_ammoTypes)
266 	{
267 		delete ammoType;
268 	}
269 	m_ammoTypes.clear();
270 	m_ammoTypeMap.clear();
271 
272 	deleteElements(m_dealersInventory);
273 	delete m_bobbyRayNewInventory;
274 	delete m_bobbyRayUsedInventory;
275 
276 	deleteElements(m_dealers);
277 
278 	delete m_impPolicy;
279 	delete m_gamePolicy;
280 	delete m_strategicAIPolicy;
281 	delete m_loadingScreenModel;
282 
283 	deleteElements(m_newStrings);
284 	deleteElements(m_landTypeStrings);
285 	for (const ST::string *str : m_calibreNames)
286 	{
287 		delete str;
288 	}
289 	for (const ST::string *str : m_calibreNamesBobbyRay)
290 	{
291 		delete str;
292 	}
293 
294 	deleteElements(m_bloodCatPlacements);
295 	deleteElements(m_bloodCatSpawns);
296 	deleteElements(m_creatureLairs);
297 	deleteElements(m_factParams);
298 	deleteElements(m_mines);
299 	deleteElements(m_npcActionParams);
300 	deleteElements(m_npcPlacements);
301 	deleteElements(m_samSites);
302 	deleteElements(m_mapSecrets);
303 	deleteElements(m_shippingDestinations);
304 	deleteElements(m_shippingDestinationNames);
305 	deleteElements(m_towns);
306 	deleteElements(m_townNames);
307 	deleteElements(m_townNameLocatives);
308 	deleteElements(m_undergroundSectors);
309 	deleteElements(m_rpcSmallFaces);
310 	deleteElements(m_MERCListings);
311 	deleteElements(m_mercProfileInfo);
312 	deleteElements(m_mercProfiles);
313 
314 	m_sectorLandTypes.clear();
315 
316 	delete m_cacheSectors;
317 	delete m_movementCosts;
318 	delete m_samSitesAirControl;
319 }
320 
getBobbyRayNewInventory() const321 const DealerInventory* DefaultContentManager::getBobbyRayNewInventory() const
322 {
323 	return m_bobbyRayNewInventory;
324 }
325 
getBobbyRayUsedInventory() const326 const DealerInventory* DefaultContentManager::getBobbyRayUsedInventory() const
327 {
328 	return m_bobbyRayUsedInventory;
329 }
330 
getDealer(uint8_t dealerID) const331 const DealerModel* DefaultContentManager::getDealer(uint8_t dealerID) const
332 {
333 	return m_dealers[dealerID];
334 }
335 
getDealers() const336 const std::vector<const DealerModel*> DefaultContentManager::getDealers() const
337 {
338 	return m_dealers;
339 }
340 
getShippingDestinations() const341 const std::vector<const ShippingDestinationModel*>& DefaultContentManager::getShippingDestinations() const
342 {
343 	return m_shippingDestinations;
344 }
345 
getShippingDestination(uint8_t locationId) const346 const ShippingDestinationModel* DefaultContentManager::getShippingDestination(uint8_t locationId) const
347 {
348 	return m_shippingDestinations[locationId];
349 }
350 
getPrimaryShippingDestination() const351 const ShippingDestinationModel* DefaultContentManager::getPrimaryShippingDestination() const
352 {
353 	for (auto dest : m_shippingDestinations)
354 	{
355 		if (dest->isPrimary)
356 		{
357 			return dest;
358 		}
359 	}
360 	throw DataError("Bobby Ray primary destination is not defined");
361 }
362 
getShippingDestinationName(uint8_t index) const363 const ST::string* DefaultContentManager::getShippingDestinationName(uint8_t index) const
364 {
365 	return m_shippingDestinationNames[index];
366 }
367 
getNpcActionParams(uint16_t actionCode) const368 const NpcActionParamsModel* DefaultContentManager::getNpcActionParams(uint16_t actionCode) const
369 {
370 	auto it = m_npcActionParams.find(actionCode);
371 	if (it != m_npcActionParams.end())
372 	{
373 		return it->second;
374 	}
375 	return &NpcActionParamsModel::empty;
376 }
377 
getFactParams(Fact fact) const378 const FactParamsModel* DefaultContentManager::getFactParams(Fact fact) const
379 {
380 	auto it = m_factParams.find(fact);
381 	if (it != m_factParams.end())
382 	{
383 		return it->second;
384 	}
385 	return &FactParamsModel::empty;
386 }
387 
388 /** Get map file path. */
getMapPath(const ST::string & mapName) const389 ST::string DefaultContentManager::getMapPath(const ST::string& mapName) const
390 {
391 	ST::string result = MAPSDIR;
392 	result += '/';
393 	result += mapName.c_str();
394 
395 	STLOGD("map file {}", result);
396 
397 	return result;
398 }
399 
400 /** Get radar map resource name. */
getRadarMapResourceName(const ST::string & mapName) const401 ST::string DefaultContentManager::getRadarMapResourceName(const ST::string &mapName) const
402 {
403 	ST::string result = RADARMAPSDIR;
404 	result += "/";
405 	result += mapName;
406 
407 	STLOGD("map file {}", result);
408 
409 	return result;
410 }
411 
412 /** Get tileset resource name. */
getTilesetResourceName(int number,const ST::string & fileName) const413 ST::string DefaultContentManager::getTilesetResourceName(int number, const ST::string& fileName) const
414 {
415 	return ST::format("{}/{}/{}", TILESETSDIR, number, fileName);
416 }
417 
418 
419 /** Get tileset db resource name. */
getTilesetDBResName() const420 ST::string DefaultContentManager::getTilesetDBResName() const
421 {
422 	return BINARYDATADIR "/ja2set.dat";
423 }
424 
425 /** Open map for reading. */
openMapForReading(const ST::string & mapName) const426 SGPFile* DefaultContentManager::openMapForReading(const ST::string& mapName) const
427 {
428 	return openGameResForReading(getMapPath(mapName));
429 }
430 
431 /** Get directory for storing new map file. */
getNewMapFolder() const432 ST::string DefaultContentManager::getNewMapFolder() const
433 {
434 	return FileMan::joinPaths(m_dataDir, MAPSDIR);
435 }
436 
437 /** Get all available maps. */
getAllMaps() const438 std::vector<ST::string> DefaultContentManager::getAllMaps() const
439 {
440 	return FindFilesInDir(MAPSDIR, "dat", true, true, true);
441 }
442 
443 /** Get all available tilecache. */
getAllTilecache() const444 std::vector<ST::string> DefaultContentManager::getAllTilecache() const
445 {
446 	return FindFilesInDir(m_tileDir, "jsd", true, false);
447 }
448 
449 /** Open temporary file for writing. */
openTempFileForWriting(const ST::string & filename,bool truncate) const450 SGPFile* DefaultContentManager::openTempFileForWriting(const ST::string& filename, bool truncate) const
451 {
452 	ST::string path = FileMan::joinPaths(NEW_TEMP_DIR, filename);
453 	return FileMan::openForWriting(path, truncate);
454 }
455 
456 /** Open temporary file for appending. */
openTempFileForAppend(const ST::string & filename) const457 SGPFile* DefaultContentManager::openTempFileForAppend(const ST::string& filename) const
458 {
459 	ST::string path = FileMan::joinPaths(NEW_TEMP_DIR, filename);
460 	return FileMan::openForAppend(path);
461 }
462 
463 /* Open temporary file for reading. */
openTempFileForReading(const ST::string & filename) const464 SGPFile* DefaultContentManager::openTempFileForReading(const ST::string& filename) const
465 {
466 	ST::string path = FileMan::joinPaths(NEW_TEMP_DIR, filename);
467 	RustPointer<File> file(File_open(path.c_str(), FILE_OPEN_READ));
468 	if (!file)
469 	{
470 		RustPointer<char> err(getRustError());
471 		ST::string buf = ST::format("DefaultContentManager::openTempFileForReading: {}", err.get());
472 		throw std::runtime_error(buf.to_std_string());
473 	}
474 	return FileMan::getSGPFileFromFile(file.release());
475 }
476 
477 /** Delete temporary file. */
deleteTempFile(const ST::string & filename) const478 void DefaultContentManager::deleteTempFile(const ST::string& filename) const
479 {
480 	ST::string path = FileMan::joinPaths(NEW_TEMP_DIR, filename);
481 	FileDelete(path);
482 }
483 
484 /* Open a game resource file for reading.
485  *
486  * First trying to open the file normally. It will work if the path is absolute
487  * and the file is found or path is relative to the current directory (game
488  * settings directory) and file is present.
489  * If file is not found, try to find it relatively to 'Data' directory.
490  * If file is not found, try to find the file in libraries located in 'Data' directory; */
openGameResForReading(const ST::string & filename) const491 SGPFile* DefaultContentManager::openGameResForReading(const ST::string& filename) const
492 {
493 	{
494 		RustPointer<File> file = FileMan::openFileForReading(filename);
495 		if (file)
496 		{
497 			STLOGD("Opened file (current dir): '{}'", filename);
498 			return FileMan::getSGPFileFromFile(file.release());
499 		}
500 	}
501 
502 	RustPointer<VfsFile> vfile(VfsFile_open(m_vfs.get(), filename.c_str()));
503 	if (!vfile)
504 	{
505 		RustPointer<char> err{getRustError()};
506 		throw std::runtime_error(ST::format("openGameResForReading: {}", err.get()).to_std_string());
507 	}
508 	STLOGD("Opened file (vfs): '{}'", filename);
509 	SGPFile *file = new SGPFile{};
510 	file->flags = SGPFILE_NONE;
511 	file->u.vfile = vfile.release();
512 	return file;
513 }
514 
515 /** Open user's private file (e.g. saved game, settings) for reading. */
openUserPrivateFileForReading(const ST::string & filename) const516 SGPFile* DefaultContentManager::openUserPrivateFileForReading(const ST::string& filename) const
517 {
518 	RustPointer<File> file = FileMan::openFileForReading(filename);
519 	if (!file)
520 	{
521 		RustPointer<char> err(getRustError());
522 		ST::string buf = ST::format("DefaultContentManager::openUserPrivateFileForReading: {}", err.get());
523 		throw std::runtime_error(buf.to_std_string());
524 	}
525 	return FileMan::getSGPFileFromFile(file.release());
526 }
527 
528 /* Checks if a game resource exists. */
doesGameResExists(const ST::string & filename) const529 bool DefaultContentManager::doesGameResExists(const ST::string& filename) const
530 {
531 	if(FileMan::checkFileExistance(m_externalizedDataPath, filename))
532 	{
533 		return true;
534 	}
535 	else
536 	{
537 		RustPointer<File> file(File_open(filename.c_str(), FILE_OPEN_READ));
538 		if (!file)
539 		{
540 			ST::string path = ST::format("{}/{}", m_dataDir, filename);
541 			file.reset(File_open(path.c_str(), FILE_OPEN_READ));
542 			if (!file)
543 			{
544 				RustPointer<VfsFile> vfile(VfsFile_open(m_vfs.get(), filename.c_str()));
545 				return static_cast<bool>(vfile);
546 			}
547 		}
548 
549 		return true;
550 	}
551 }
552 
getScreenshotFolder() const553 ST::string DefaultContentManager::getScreenshotFolder() const
554 {
555 	return m_userHomeDir;
556 }
557 
getVideoCaptureFolder() const558 ST::string DefaultContentManager::getVideoCaptureFolder() const
559 {
560 	return m_userHomeDir;
561 }
562 
563 /** Get folder for saved games. */
getSavedGamesFolder() const564 ST::string DefaultContentManager::getSavedGamesFolder() const
565 {
566 	return FileMan::joinPaths(m_userHomeDir, "SavedGames");
567 }
568 
569 /** Load encrypted string from game resource file. */
loadEncryptedString(const ST::string & fileName,uint32_t seek_chars,uint32_t read_chars) const570 ST::string DefaultContentManager::loadEncryptedString(const ST::string& fileName, uint32_t seek_chars, uint32_t read_chars) const
571 {
572 	AutoSGPFile File(openGameResForReading(fileName));
573 	ST::string err_msg;
574 	ST::string str = LoadEncryptedData(err_msg, getStringEncType(), File, seek_chars, read_chars);
575 	if (!err_msg.empty())
576 	{
577 		STLOGW("DefaultContentManager::loadEncryptedString '{}' {} {}: {}", fileName, seek_chars, read_chars, err_msg);
578 	}
579 	return str;
580 }
581 
loadEncryptedString(SGPFile * File,uint32_t seek_chars,uint32_t read_chars) const582 ST::string DefaultContentManager::loadEncryptedString(SGPFile* File, uint32_t seek_chars, uint32_t read_chars) const
583 {
584 	ST::string err_msg;
585 	ST::string str = LoadEncryptedData(err_msg, getStringEncType(), File, seek_chars, read_chars);
586 	if (!err_msg.empty())
587 	{
588 		STLOGW("DefaultContentManager::loadEncryptedString ? {} {}: {}", seek_chars, read_chars, err_msg);
589 	}
590 	return str;
591 }
592 
593 
594 /** Load dialogue quote from file. */
loadDialogQuoteFromFile(const ST::string & fileName,int quote_number)595 ST::string* DefaultContentManager::loadDialogQuoteFromFile(const ST::string& fileName, int quote_number)
596 {
597 	AutoSGPFile File(openGameResForReading(fileName));
598 	ST::string err_msg;
599 	ST::string quote = LoadEncryptedData(err_msg, getStringEncType(), File, quote_number * DIALOGUESIZE, DIALOGUESIZE);
600 	if (!err_msg.empty())
601 	{
602 		STLOGW("DefaultContentManager::loadDialogQuoteFromFile '{}' {}: {}", fileName, quote_number, err_msg);
603 	}
604 	return new ST::string(quote);
605 }
606 
607 /** Load all dialogue quotes for a character. */
loadAllDialogQuotes(STRING_ENC_TYPE encType,const ST::string & fileName,std::vector<ST::string * > & quotes) const608 void DefaultContentManager::loadAllDialogQuotes(STRING_ENC_TYPE encType, const ST::string& fileName, std::vector<ST::string*> &quotes) const
609 {
610 	AutoSGPFile File(openGameResForReading(fileName));
611 	uint32_t fileSize = FileGetSize(File);
612 	uint32_t numQuotes = fileSize / DIALOGUESIZE / 2;
613 
614 	for(uint32_t i = 0; i < numQuotes; i++)
615 	{
616 		ST::string err;
617 		ST::string quote = LoadEncryptedData(err, encType, File, i * DIALOGUESIZE, DIALOGUESIZE);
618 		if (!err.empty())
619 		{
620 			STLOGW("DefaultContentManager::loadAllDialogQuotes '{}' {}: {}", fileName, i, err);
621 		}
622 		quotes.push_back(new ST::string(quote));
623 	}
624 }
625 
626 /** Get weapons with the give index. */
getWeapon(uint16_t itemIndex)627 const WeaponModel* DefaultContentManager::getWeapon(uint16_t itemIndex)
628 {
629 	return getItem(itemIndex)->asWeapon();
630 }
631 
getWeaponByName(const ST::string & internalName)632 const WeaponModel* DefaultContentManager::getWeaponByName(const ST::string &internalName)
633 {
634 	std::map<ST::string, const WeaponModel*>::const_iterator it = m_weaponMap.find(internalName);
635 	if(it == m_weaponMap.end())
636 	{
637 		STLOGE("weapon '{}' is not found", internalName);
638 		throw std::runtime_error(ST::format("weapon '{}' is not found", internalName).to_std_string());
639 	}
640 	return it->second;//m_weaponMap[internalName];
641 }
642 
getMagazineByName(const ST::string & internalName)643 const MagazineModel* DefaultContentManager::getMagazineByName(const ST::string &internalName)
644 {
645 	if(m_magazineMap.find(internalName) == m_magazineMap.end())
646 	{
647 		STLOGE("magazine '{}' is not found", internalName);
648 		throw std::runtime_error(ST::format("magazine '{}' is not found", internalName).to_std_string());
649 	}
650 	return m_magazineMap[internalName];
651 }
652 
getMagazineByItemIndex(uint16_t itemIndex)653 const MagazineModel* DefaultContentManager::getMagazineByItemIndex(uint16_t itemIndex)
654 {
655 	return getItem(itemIndex)->asAmmo();
656 }
657 
getMagazines() const658 const std::vector<const MagazineModel*>& DefaultContentManager::getMagazines() const
659 {
660 	return m_magazines;
661 }
662 
getCalibre(uint8_t index)663 const CalibreModel* DefaultContentManager::getCalibre(uint8_t index)
664 {
665 	return m_calibres[index];
666 }
667 
getCalibreName(uint8_t index) const668 const ST::string* DefaultContentManager::getCalibreName(uint8_t index) const
669 {
670 	return m_calibreNames[index];
671 }
672 
getCalibreNameForBobbyRay(uint8_t index) const673 const ST::string* DefaultContentManager::getCalibreNameForBobbyRay(uint8_t index) const
674 {
675 	return m_calibreNamesBobbyRay[index];
676 }
677 
getAmmoType(uint8_t index)678 const AmmoTypeModel* DefaultContentManager::getAmmoType(uint8_t index)
679 {
680 	return m_ammoTypes[index];
681 }
682 
loadWeapons()683 bool DefaultContentManager::loadWeapons()
684 {
685 	auto document = readJsonDataFile("weapons.json");
686 	if (document->IsArray())
687 	{
688 		const rapidjson::Value& a = document->GetArray();
689 		for (rapidjson::SizeType i = 0; i < a.Size(); i++)
690 		{
691 			JsonObjectReader obj(a[i]);
692 			WeaponModel *w = WeaponModel::deserialize(obj, m_calibreMap);
693 			STLOGD("Loaded weapon {} {}", w->getItemIndex(), w->getInternalName());
694 
695 			if (w->getItemIndex() > MAX_WEAPONS)
696 			{
697 				STLOGE("Weapon index must be in the interval 0 - {}", MAX_WEAPONS);
698 				return false;
699 			}
700 
701 			m_items[w->getItemIndex()] = w;
702 			m_weaponMap.insert(std::make_pair(w->getInternalName(), w));
703 		}
704 	}
705 
706 	return true;
707 }
708 
loadItems()709 bool DefaultContentManager::loadItems()
710 {
711 	auto document = readJsonDataFile("items.json");
712 	for (auto& el : document->GetArray())
713 	{
714 		JsonObjectReader obj(el);
715 		auto* item = ItemModel::deserialize(obj);
716 		if (item->getItemIndex() <= MAX_WEAPONS || item->getItemIndex() > MAXITEMS)
717 		{
718 			ST::string err = ST::format("Item index must be in the interval {} - {}", MAX_WEAPONS+1, MAXITEMS);
719 			throw DataError(err);
720 		}
721 
722 		m_items[item->getItemIndex()] = item;
723 	}
724 
725 	return true;
726 }
727 
loadMagazines()728 bool DefaultContentManager::loadMagazines()
729 {
730 	auto document = readJsonDataFile("magazines.json");
731 	if(document->IsArray())
732 	{
733 		const rapidjson::Value& a = document->GetArray();
734 		for (rapidjson::SizeType i = 0; i < a.Size(); i++)
735 		{
736 			JsonObjectReader obj(a[i]);
737 			MagazineModel *mag = MagazineModel::deserialize(obj, m_calibreMap, m_ammoTypeMap);
738 			STLOGD("Loaded magazine {} {}", mag->getItemIndex(), mag->getInternalName());
739 
740 			if((mag->getItemIndex() < FIRST_AMMO) || (mag->getItemIndex() > LAST_AMMO))
741 			{
742 				STLOGE("Magazine item index must be in the interval {} - {}", FIRST_AMMO, LAST_AMMO);
743 				return false;
744 			}
745 
746 			m_magazines.push_back(mag);
747 			m_items[mag->getItemIndex()] = mag;
748 			m_magazineMap.insert(std::make_pair(mag->getInternalName(), mag));
749 		}
750 	}
751 
752 	return true;
753 }
754 
loadCalibres()755 bool DefaultContentManager::loadCalibres()
756 {
757 	auto document = readJsonDataFile("calibres.json");
758 	if (document->IsArray()) {
759 		const rapidjson::Value& a = document->GetArray();
760 		for (rapidjson::SizeType i = 0; i < a.Size(); i++)
761 		{
762 			JsonObjectReader obj(a[i]);
763 			CalibreModel *calibre = CalibreModel::deserialize(obj);
764 			STLOGD("Loaded calibre {} {}", calibre->index, calibre->internalName);
765 
766 			if(m_calibres.size() <= calibre->index)
767 			{
768 				m_calibres.resize(calibre->index + 1);
769 			}
770 
771 			m_calibres[calibre->index] = calibre;
772 		}
773 	}
774 
775 	for (const CalibreModel* calibre : m_calibres)
776 	{
777 		m_calibreMap.insert(std::make_pair(ST::string(calibre->internalName), calibre));
778 	}
779 
780 	return true;
781 }
782 
loadAmmoTypes()783 bool DefaultContentManager::loadAmmoTypes()
784 {
785 	auto document = readJsonDataFile("ammo_types.json");
786 	if(document->IsArray()) {
787 		const rapidjson::Value& a = document->GetArray();
788 		for (rapidjson::SizeType i = 0; i < a.Size(); i++)
789 		{
790 			JsonObjectReader obj(a[i]);
791 			AmmoTypeModel *ammoType = AmmoTypeModel::deserialize(obj);
792 			STLOGD("Loaded ammo type {} {}", ammoType->index, ammoType->internalName);
793 
794 			if(m_ammoTypes.size() <= ammoType->index)
795 			{
796 				m_ammoTypes.resize(ammoType->index + 1);
797 			}
798 
799 			m_ammoTypes[ammoType->index] = ammoType;
800 		}
801 	}
802 
803 	for (const AmmoTypeModel* ammoType : m_ammoTypes)
804 	{
805 		m_ammoTypeMap.insert(std::make_pair(ST::string(ammoType->internalName), ammoType));
806 	}
807 
808 	return true;
809 }
810 
loadMusicModeList(const MusicMode mode,rapidjson::Value & array)811 bool DefaultContentManager::loadMusicModeList(const MusicMode mode, rapidjson::Value &array)
812 {
813 	std::vector<const ST::string*>* musicModeList = new std::vector<const ST::string*>();
814 
815 	std::vector<ST::string> utf8_encoded;
816 	JsonUtility::parseListStrings(array, utf8_encoded);
817 	for (const ST::string &str : utf8_encoded)
818 	{
819 		musicModeList->push_back(new ST::string(str));
820 		STLOGD("Loaded music {}", str);
821 	}
822 
823 	m_musicMap[mode] = musicModeList;
824 
825 	return true;
826 }
827 
loadMusic()828 bool DefaultContentManager::loadMusic()
829 {
830 	auto document = readJsonDataFile("music.json");
831 	if(!document->IsObject()) {
832 		SLOGE("music.json has wrong structure");
833 		return false;
834 	}
835 
836 	auto obj = document->GetObject();
837 	SLOGD("Loading main_menu music");
838 	loadMusicModeList(MUSIC_MAIN_MENU, obj["main_menu"]);
839 	SLOGD("Loading main_menu music");
840 	loadMusicModeList(MUSIC_LAPTOP, obj["laptop"]);
841 	SLOGD("Loading tactical music");
842 	loadMusicModeList(MUSIC_TACTICAL_NOTHING, obj["tactical"]);
843 	SLOGD("Loading tactical_enemypresent music");
844 	loadMusicModeList(MUSIC_TACTICAL_ENEMYPRESENT, obj["tactical_enemypresent"]);
845 	SLOGD("Loading tactical_battle music");
846 	loadMusicModeList(MUSIC_TACTICAL_BATTLE, obj["tactical_battle"]);
847 	SLOGD("Loading tactical_creature music");
848 	loadMusicModeList(MUSIC_TACTICAL_CREATURE_NOTHING, obj["tactical_creature"]);
849 	SLOGD("Loading tactical_creature_enemypresent music");
850 	loadMusicModeList(MUSIC_TACTICAL_CREATURE_ENEMYPRESENT, obj["tactical_creature_enemypresent"]);
851 	SLOGD("Loading tactical_creature_battle music");
852 	loadMusicModeList(MUSIC_TACTICAL_CREATURE_BATTLE, obj["tactical_creature_battle"]);
853 	SLOGD("Loading tactical_victory music");
854 	loadMusicModeList(MUSIC_TACTICAL_VICTORY, obj["tactical_victory"]);
855 	SLOGD("Loading tactical_defeat music");
856 	loadMusicModeList(MUSIC_TACTICAL_DEFEAT, obj["tactical_defeat"]);
857 
858 	return true;
859 }
860 
readWeaponTable(const ST::string & fileName,std::vector<std::vector<const WeaponModel * >> & weaponTable)861 bool DefaultContentManager::readWeaponTable(
862 	const ST::string& fileName,
863 	std::vector<std::vector<const WeaponModel*> > & weaponTable)
864 {
865 	auto document = readJsonDataFile(fileName);
866 	if(document->IsArray())
867 	{
868 		const rapidjson::Value& a = document->GetArray();
869 		for (rapidjson::SizeType i = 0; i < a.Size(); i++)
870 		{
871 			std::vector<ST::string> weaponNames;
872 			if(JsonUtility::parseListStrings(a[i], weaponNames))
873 			{
874 				for (const ST::string &weapon : weaponNames)
875 				{
876 					weaponTable[i].push_back(getWeaponByName(weapon));
877 				}
878 			}
879 		}
880 	}
881 
882 	return true;
883 }
884 
getNormalGunChoice() const885 const std::vector<std::vector<const WeaponModel*> > & DefaultContentManager::getNormalGunChoice() const
886 {
887 	return mNormalGunChoice;
888 }
889 
getExtendedGunChoice() const890 const std::vector<std::vector<const WeaponModel*> > & DefaultContentManager::getExtendedGunChoice() const
891 {
892 	return mExtendedGunChoice;
893 }
894 
getGarrisonGroups() const895 const std::vector<GARRISON_GROUP>& DefaultContentManager::getGarrisonGroups() const
896 {
897 	return m_garrisonGroups;
898 }
899 
getPatrolGroups() const900 const std::vector<PATROL_GROUP>& DefaultContentManager::getPatrolGroups() const
901 {
902 	return m_patrolGroups;
903 }
904 
getArmyCompositions() const905 const std::vector<ARMY_COMPOSITION>& DefaultContentManager::getArmyCompositions() const
906 {
907 	return m_armyCompositions;
908 }
909 
loadArmyData()910 bool DefaultContentManager::loadArmyData()
911 {
912 	readWeaponTable("army-gun-choice-normal.json", mNormalGunChoice);
913 	readWeaponTable("army-gun-choice-extended.json", mExtendedGunChoice);
914 
915 	auto jsonAC = readJsonDataFile("army-compositions.json");
916 	auto armyCompModels = ArmyCompositionModel::deserialize(*jsonAC);
917 	ArmyCompositionModel::validateData(armyCompModels);
918 
919 	std::map<ST::string, uint8_t> mapping;
920 	for (auto& armyComp : armyCompModels)
921 	{
922 		mapping[armyComp->name] = armyComp->compositionId;
923 		m_armyCompositions.push_back(armyComp->toArmyComposition());
924 	}
925 	deleteElements(armyCompModels);
926 	armyCompModels.clear();
927 
928 	auto jsonGG = readJsonDataFile("army-garrison-groups.json");
929 	for (auto& element : jsonGG->GetArray())
930 	{
931 		auto obj = JsonObjectReader(element);
932 		m_garrisonGroups.push_back(
933 			GarrisonGroupModel::deserialize(obj, mapping)
934 		);
935 	}
936 	GarrisonGroupModel::validateData(m_garrisonGroups);
937 
938 	auto jsonPG = readJsonDataFile("army-patrol-groups.json");
939 	for (auto& element : jsonPG->GetArray())
940 	{
941 		m_patrolGroups.push_back(
942 			PatrolGroupModel::deserialize(element)
943 		);
944 	}
945 	PatrolGroupModel::validateData(m_patrolGroups);
946 
947 	return true;
948 }
949 
loadStringRes(const ST::string & name,std::vector<const ST::string * > & strings) const950 void DefaultContentManager::loadStringRes(const ST::string& name, std::vector<const ST::string*> &strings) const
951 {
952 	ST::string fullName(name);
953 
954 	switch(m_gameVersion)
955 	{
956 	case GameVersion::DUTCH:        fullName += "-dut";   break;
957 	case GameVersion::ENGLISH:      fullName += "-eng";   break;
958 	case GameVersion::FRENCH:       fullName += "-fr";    break;
959 	case GameVersion::GERMAN:       fullName += "-ger";   break;
960 	case GameVersion::ITALIAN:      fullName += "-it";    break;
961 	case GameVersion::POLISH:       fullName += "-pl";    break;
962 	case GameVersion::RUSSIAN:
963 	case GameVersion::RUSSIAN_GOLD: fullName += "-rus";   break;
964 	default:
965 	{
966 		throw std::runtime_error(ST::format("unknown game version {}", static_cast<int>(m_gameVersion)).to_std_string());
967 	}
968 	}
969 
970 	fullName += ".json";
971 	auto json = readJsonDataFile(fullName.c_str());
972 	std::vector<ST::string> utf8_encoded;
973 	JsonUtility::parseListStrings(*json, utf8_encoded);
974 	for (const ST::string &str : utf8_encoded)
975 	{
976 		strings.push_back(new ST::string(str));
977 	}
978 }
979 
980 /** Load the game data. */
loadGameData()981 bool DefaultContentManager::loadGameData()
982 {
983 	m_items.resize(MAXITEMS);
984 	bool result = loadItems()
985 		&& loadCalibres()
986 		&& loadAmmoTypes()
987 		&& loadMagazines()
988 		&& loadWeapons()
989 		&& loadArmyData()
990 		&& loadMusic();
991 
992 	for (const ItemModel *item : m_items)
993 	{
994 		m_itemMap.insert(std::make_pair(item->getInternalName(), item));
995 	}
996 
997 	auto replacement_json = readJsonDataFile("tactical-map-item-replacements.json");
998 	m_mapItemReplacements = MapItemReplacementModel::deserialize(replacement_json.get(), this);
999 
1000 	loadAllDealersAndInventory();
1001 
1002 	auto game_json = readJsonDataFile("game.json");
1003 	m_gamePolicy = new DefaultGamePolicy(game_json.get());
1004 
1005 	auto imp_json = readJsonDataFile("imp.json");
1006 	m_impPolicy = new DefaultIMPPolicy(imp_json.get(), this);
1007 
1008 	auto sai_json = readJsonDataFile("strategic-ai-policy.json");
1009 	m_strategicAIPolicy = new DefaultStrategicAIPolicy(sai_json.get());
1010 
1011 	loadStringRes("strings/shipping-destinations", m_shippingDestinationNames);
1012 
1013 	auto shippingDestJson = readJsonDataFile("shipping-destinations.json");
1014 	for (auto& element : shippingDestJson->GetArray())
1015 	{
1016 		auto r = JsonObjectReader(element);
1017 		m_shippingDestinations.push_back(ShippingDestinationModel::deserialize(r));
1018 	}
1019 	ShippingDestinationModel::validateData(m_shippingDestinations, m_shippingDestinationNames);
1020 
1021 	auto loadScreensList = readJsonDataFile("loading-screens.json");
1022 	auto loadScreensMapping = readJsonDataFile("loading-screens-mapping.json");
1023 	m_loadingScreenModel = LoadingScreenModel::deserialize(*loadScreensList, *loadScreensMapping);
1024 	m_loadingScreenModel->validateData(this);
1025 
1026 	loadStringRes("strings/ammo-calibre", m_calibreNames);
1027 	loadStringRes("strings/ammo-calibre-bobbyray", m_calibreNamesBobbyRay);
1028 
1029 	loadStringRes("strings/new-strings", m_newStrings);
1030 	loadStringRes("strings/strategic-map-land-types", m_landTypeStrings);
1031 
1032 	loadStrategicLayerData();
1033 	loadTacticalLayerData();
1034 	loadMercsData();
1035 
1036 	return result;
1037 }
1038 
readJsonDataFile(const ST::string & fileName) const1039 std::unique_ptr<rapidjson::Document> DefaultContentManager::readJsonDataFile(const ST::string& fileName) const
1040 {
1041 	AutoSGPFile f(openGameResForReading(fileName));
1042 	ST::string jsonData = FileMan::fileReadText(f);
1043 
1044 	auto document = std::make_unique<rapidjson::Document>();
1045 	if (document->Parse<rapidjson::kParseCommentsFlag>(jsonData.c_str()).HasParseError())
1046 	{
1047 		ST::string errorMessage = ST::format("Failed to parse {} (at location {}) {} ",
1048 			fileName,
1049 			document->GetErrorOffset(),
1050 			rapidjson::GetParseError_En(document->GetParseError())
1051 		);
1052 		throw DataError(errorMessage);
1053 	}
1054 
1055 	return document;
1056 }
1057 
loadDealerInventory(const ST::string & fileName)1058 const DealerInventory * DefaultContentManager::loadDealerInventory(const ST::string& fileName)
1059 {
1060 	return new DealerInventory(readJsonDataFile(fileName).get(), this);
1061 }
1062 
loadAllDealersAndInventory()1063 bool DefaultContentManager::loadAllDealersAndInventory()
1064 {
1065 	auto json = readJsonDataFile("dealers.json");
1066 	int index = 0;
1067 	for (auto& element : json->GetArray())
1068 	{
1069 		m_dealers.push_back(DealerModel::deserialize(element, index++));
1070 	}
1071 	DealerModel::validateData(m_dealers);
1072 
1073 	m_dealersInventory = std::vector<const DealerInventory*>(m_dealers.size());
1074 	for (auto dealer : m_dealers)
1075 	{
1076 		ST::string filename = dealer->getInventoryDataFileName();
1077 		m_dealersInventory[dealer->dealerID] = loadDealerInventory(filename.c_str());
1078 	}
1079 	m_bobbyRayNewInventory                        = loadDealerInventory("bobby-ray-inventory-new.json");
1080 	m_bobbyRayUsedInventory                       = loadDealerInventory("bobby-ray-inventory-used.json");
1081 	return true;
1082 }
1083 
getItem(uint16_t itemIndex) const1084 const ItemModel* DefaultContentManager::getItem(uint16_t itemIndex) const
1085 {
1086 	if(itemIndex >= m_items.size())
1087 	{
1088 		return nullptr;
1089 	}
1090 	return m_items[itemIndex];
1091 }
1092 
getItemByName(const ST::string & internalName) const1093 const ItemModel* DefaultContentManager::getItemByName(const ST::string &internalName) const
1094 {
1095 	std::map<ST::string, const ItemModel*>::const_iterator it = m_itemMap.find(internalName);
1096 	if(it == m_itemMap.end())
1097 	{
1098 		STLOGE("item '{}' is not found", internalName);
1099 		throw std::runtime_error(ST::format("item '{}' is not found", internalName).to_std_string());
1100 	}
1101 	return it->second;
1102 }
1103 
getMapItemReplacements() const1104 const std::map<uint16_t, uint16_t> DefaultContentManager::getMapItemReplacements() const
1105 {
1106 	return m_mapItemReplacements;
1107 }
1108 
getDealerInventory(int dealerId) const1109 const DealerInventory* DefaultContentManager::getDealerInventory(int dealerId) const
1110 {
1111 	return m_dealersInventory[dealerId];
1112 }
1113 
getMusicForMode(MusicMode mode) const1114 const ST::string* DefaultContentManager::getMusicForMode(MusicMode mode) const {
1115 	const uint32_t index = Random((uint32_t)m_musicMap.find(mode)->second->size());
1116 	const ST::string* chosen = m_musicMap.find(mode)->second->at(index);
1117 
1118 	STLOGD("Choosing music index {} of {} for: '{}'", index, m_musicMap.find(mode)->second->size(), chosen);
1119 	return chosen;
1120 }
1121 
getIMPPolicy() const1122 const IMPPolicy* DefaultContentManager::getIMPPolicy() const
1123 {
1124 	return m_impPolicy;
1125 }
1126 
getGamePolicy() const1127 const GamePolicy* DefaultContentManager::getGamePolicy() const
1128 {
1129 	return m_gamePolicy;
1130 }
1131 
getStrategicAIPolicy() const1132 const StrategicAIPolicy* DefaultContentManager::getStrategicAIPolicy() const
1133 {
1134 	return m_strategicAIPolicy;
1135 }
1136 
getNewString(size_t stringId) const1137 const ST::string* DefaultContentManager::getNewString(size_t stringId) const
1138 {
1139 	if(stringId >= m_newStrings.size())
1140 	{
1141 		ST::string message = ST::format("new string {} is not found", stringId);
1142 		SLOGE(message.c_str());
1143 		throw std::runtime_error(message.c_str());
1144 	}
1145 	else
1146 	{
1147 		return m_newStrings[stringId];
1148 	}
1149 }
1150 
getLandTypeString(size_t index) const1151 const ST::string& DefaultContentManager::getLandTypeString(size_t index) const
1152 {
1153 	return *m_landTypeStrings.at(index);
1154 }
1155 
loadStrategicLayerData()1156 bool DefaultContentManager::loadStrategicLayerData()
1157 {
1158 	auto json = readJsonDataFile("strategic-bloodcat-placements.json");
1159 	for (auto& element : json->GetArray()) {
1160 		auto obj = JsonObjectReader(element);
1161 		m_bloodCatPlacements.push_back(
1162 			BloodCatPlacementsModel::deserialize(obj)
1163 		);
1164 	}
1165 
1166 	json = readJsonDataFile("strategic-bloodcat-spawns.json");
1167 	for (auto& element : json->GetArray())
1168 	{
1169 		auto obj = JsonObjectReader(element);
1170 		m_bloodCatSpawns.push_back(
1171 			BloodCatSpawnsModel::deserialize(obj)
1172 		);
1173 	}
1174 
1175 	json = readJsonDataFile("strategic-map-creature-lairs.json");
1176 	for (auto& element : json->GetArray())
1177 	{
1178 		m_creatureLairs.push_back(
1179 			CreatureLairModel::deserialize(element)
1180 		);
1181 	}
1182 
1183 	json = readJsonDataFile("strategic-fact-params.json");
1184 	for (auto& element : json->GetArray())
1185 	{
1186 		auto params = FactParamsModel::deserialize(element);
1187 		m_factParams[params->fact] = params;
1188 	}
1189 
1190 	json = readJsonDataFile("strategic-mines.json");
1191 	auto arr = json->GetArray();
1192 	for (rapidjson::SizeType i = 0; i < arr.Size(); i++)
1193 	{
1194 		m_mines.push_back(
1195 			MineModel::deserialize(i, arr[i].GetObject())
1196 		);
1197 	}
1198 	MineModel::validateData(m_mines);
1199 
1200 	json = readJsonDataFile("strategic-map-sam-sites.json");
1201 	for (auto& element : json->GetArray())
1202 	{
1203 		auto samSite = SamSiteModel::deserialize(element);
1204 		m_samSites.push_back(samSite);
1205 	}
1206 	SamSiteModel::validateData(m_samSites);
1207 
1208 	json = readJsonDataFile("strategic-map-sam-sites-air-control.json");
1209 	m_samSitesAirControl = SamSiteAirControlModel::deserialize(*json);
1210 	SamSiteAirControlModel::validateData(m_samSitesAirControl, m_samSites.size());
1211 
1212 	json = readJsonDataFile("strategic-map-towns.json");
1213 	for (auto& element : json->GetArray())
1214 	{
1215 		auto town = TownModel::deserialize(element);
1216 		m_towns.insert(std::make_pair(town->townId, town));
1217 	}
1218 
1219 	loadStringRes("strings/strategic-map-town-names", m_townNames);
1220 	loadStringRes("strings/strategic-map-town-name-locatives", m_townNameLocatives);
1221 
1222 	json = readJsonDataFile("strategic-map-underground-sectors.json");
1223 	for (auto& element : json->GetArray())
1224 	{
1225 		auto ugSector = UndergroundSectorModel::deserialize(element);
1226 		m_undergroundSectors.push_back(ugSector);
1227 	}
1228 	UndergroundSectorModel::validateData(m_undergroundSectors);
1229 
1230 	json = readJsonDataFile("strategic-map-traversibility-ratings.json");
1231 	auto travRatingMap = TraversibilityMapping::deserialize(*json);
1232 
1233 	json = readJsonDataFile("strategic-map-movement-costs.json");
1234 	m_movementCosts = MovementCostsModel::deserialize(*json, travRatingMap);
1235 
1236 	json = readJsonDataFile("strategic-map-sectors-descriptions.json");
1237 	m_sectorLandTypes = SectorLandTypes::deserialize(*json, travRatingMap);
1238 
1239 	json = readJsonDataFile("strategic-map-secrets.json");
1240 	for (auto& element : json->GetArray())
1241 	{
1242 		auto secret = StrategicMapSecretModel::deserialize(element, travRatingMap);
1243 		m_mapSecrets.push_back(secret);
1244 	}
1245 	StrategicMapSecretModel::validateData(m_mapSecrets, m_samSites);
1246 
1247 	json = readJsonDataFile("strategic-map-npc-placements.json");
1248 	for (auto& element : json->GetArray())
1249 	{
1250 		auto placement = NpcPlacementModel::deserialize(element);
1251 		m_npcPlacements.insert(std::make_pair(placement->profileId, placement));
1252 	}
1253 
1254 	CreatureLairModel::validateData(m_creatureLairs, m_undergroundSectors, m_mines.size());
1255 
1256 	json = readJsonDataFile("strategic-map-cache-sectors.json");
1257 	m_cacheSectors = CacheSectorsModel::deserialize(*json);
1258 
1259 	return true;
1260 }
1261 
loadTacticalLayerData()1262 bool DefaultContentManager::loadTacticalLayerData()
1263 {
1264 	auto json = readJsonDataFile("tactical-npc-action-params.json");
1265 	for (auto& element : json->GetArray())
1266 	{
1267 		auto params = NpcActionParamsModel::deserialize(element);
1268 		m_npcActionParams[params->actionCode] = params;
1269 	}
1270 
1271 	return true;
1272 }
1273 
loadMercsData()1274 bool DefaultContentManager::loadMercsData()
1275 {
1276 	auto json = readJsonDataFile("mercs-rpc-small-faces.json");
1277 	for (auto& element : json->GetArray())
1278 	{
1279 		auto face = RPCSmallFaceModel::deserialize(element);
1280 		m_rpcSmallFaces[face->ubProfileID] = face;
1281 	}
1282 
1283 	json = readJsonDataFile("mercs-MERC-listings.json");
1284 	int i = 0;
1285 	for (auto& element : json->GetArray())
1286 	{
1287 		auto item = MERCListingModel::deserialize(i++, element);
1288 		m_MERCListings.push_back(item);
1289 	}
1290 	MERCListingModel::validateData(m_MERCListings);
1291 
1292 	MercProfileInfo::load = [=](uint8_t p) { return this->getMercProfileInfo(p); };
1293 	json = readJsonDataFile("mercs-profile-info.json");
1294 	for (auto& element : json->GetArray())
1295 	{
1296 		auto profileInfo = MercProfileInfo::deserialize(element);
1297 		ProfileID profileID = profileInfo->profileID;
1298 		m_mercProfileInfo[profileID] = profileInfo;
1299 		m_mercProfiles.push_back(new MercProfile(profileID));
1300 	}
1301 	MercProfileInfo::validateData(m_mercProfileInfo);
1302 
1303 	return true;
1304 }
1305 
getBloodCatPlacements() const1306 const std::vector<const BloodCatPlacementsModel*>& DefaultContentManager::getBloodCatPlacements() const
1307 {
1308 	return m_bloodCatPlacements;
1309 }
1310 
getBloodCatSpawns() const1311 const std::vector<const BloodCatSpawnsModel*>& DefaultContentManager::getBloodCatSpawns() const
1312 {
1313 	return m_bloodCatSpawns;
1314 }
1315 
getBloodCatSpawnsOfSector(uint8_t sectorId) const1316 const BloodCatSpawnsModel* DefaultContentManager::getBloodCatSpawnsOfSector(uint8_t sectorId) const
1317 {
1318 	for ( auto spawns : m_bloodCatSpawns )
1319 	{
1320 		if ( spawns->sectorId == sectorId )
1321 		{
1322 			return spawns;
1323 		}
1324 	}
1325 	return NULL;
1326 }
1327 
getCreatureLairs() const1328 const std::vector<const CreatureLairModel*>& DefaultContentManager::getCreatureLairs() const
1329 {
1330 	return m_creatureLairs;
1331 }
1332 
getCreatureLair(uint8_t lairId) const1333 const CreatureLairModel* DefaultContentManager::getCreatureLair(uint8_t lairId) const
1334 {
1335 	for (auto lair : m_creatureLairs)
1336 	{
1337 		if (lair->lairId == lairId)
1338 		{
1339 			return lair;
1340 		}
1341 	}
1342 	return NULL;
1343 }
1344 
getCreatureLairByMineId(uint8_t mineId) const1345 const CreatureLairModel* DefaultContentManager::getCreatureLairByMineId(uint8_t mineId) const
1346 {
1347 	for (auto lair : m_creatureLairs)
1348 	{
1349 		if (lair->associatedMineId == mineId)
1350 		{
1351 			return lair;
1352 		}
1353 	}
1354 	return NULL;
1355 }
1356 
getMineForSector(uint8_t sectorX,uint8_t sectorY,uint8_t sectorZ) const1357 const MineModel* DefaultContentManager::getMineForSector(uint8_t sectorX, uint8_t sectorY, uint8_t sectorZ) const
1358 {
1359 	auto sectorId = SECTOR(sectorX, sectorY);
1360 	for (auto m : m_mines)
1361 	{
1362 		if (sectorZ == 0 && sectorId == m->entranceSector) return m;
1363 		for (auto s : m->mineSectors)
1364 		{
1365 			if (s[0] == sectorId && s[1] == sectorZ) return m;
1366 		}
1367 	}
1368 
1369 	return NULL;
1370 }
1371 
getMine(uint8_t mineId) const1372 const MineModel* DefaultContentManager::getMine(uint8_t mineId) const
1373 {
1374 	return m_mines[mineId];
1375 }
1376 
getMines() const1377 const std::vector<const MineModel*>& DefaultContentManager::getMines() const
1378 {
1379 	return m_mines;
1380 }
1381 
getTown(int8_t townId) const1382 const TownModel* DefaultContentManager::getTown(int8_t townId) const
1383 {
1384 	auto iter = m_towns.find(townId);
1385 	return (iter != m_towns.end()) ? iter->second : NULL;
1386 }
1387 
getSamSites() const1388 const std::vector<const SamSiteModel*>& DefaultContentManager::getSamSites() const
1389 {
1390 	return m_samSites;
1391 }
1392 
findSamIDBySector(uint8_t sectorId) const1393 const int8_t DefaultContentManager::findSamIDBySector(uint8_t sectorId) const
1394 {
1395 	for (size_t i = 0; i < m_samSites.size(); i++)
1396 	{
1397 		if (m_samSites[i]->sectorId == sectorId)
1398 		{
1399 			return static_cast<int8_t>(i);
1400 		}
1401 	}
1402 	return -1;
1403 }
1404 
findSamSiteBySector(uint8_t sectorId) const1405 const SamSiteModel* DefaultContentManager::findSamSiteBySector(uint8_t sectorId) const
1406 {
1407 	auto i = findSamIDBySector(sectorId);
1408 	return (i > -1) ? m_samSites[i] : NULL;
1409 }
1410 
getControllingSamSite(uint8_t sectorId) const1411 const int8_t DefaultContentManager::getControllingSamSite(uint8_t sectorId) const
1412 {
1413 	return m_samSitesAirControl->getControllingSamSiteID(sectorId);
1414 }
1415 
getTowns() const1416 const std::map<int8_t, const TownModel*>& DefaultContentManager::getTowns() const
1417 {
1418 	return m_towns;
1419 }
1420 
getTownName(uint8_t townId) const1421 const ST::string DefaultContentManager::getTownName(uint8_t townId) const
1422 {
1423 	if (townId >= m_townNames.size()) {
1424 		STLOGD("Town name not defined for index {}", townId);
1425 		return ST::null;
1426 	}
1427 	return *m_townNames[townId];
1428 }
1429 
getTownLocative(uint8_t townId) const1430 const ST::string DefaultContentManager::getTownLocative(uint8_t townId) const
1431 {
1432 	if (townId >= m_townNameLocatives.size()) {
1433 		STLOGD("Town name locative not defined for index {}", townId);
1434 		return ST::null;
1435 	}
1436 	return *m_townNameLocatives[townId];
1437 }
1438 
getUndergroundSectors() const1439 const std::vector<const UndergroundSectorModel*>& DefaultContentManager::getUndergroundSectors() const
1440 {
1441 	return m_undergroundSectors;
1442 }
1443 
getMovementCosts() const1444 const MovementCostsModel* DefaultContentManager::getMovementCosts() const
1445 {
1446 	return m_movementCosts;
1447 }
1448 
getSectorLandType(uint8_t const sectorID,uint8_t const sectorLevel) const1449 int16_t DefaultContentManager::getSectorLandType(uint8_t const sectorID, uint8_t const sectorLevel) const
1450 {
1451 	SectorKey key(sectorID, sectorLevel);
1452 	if (m_sectorLandTypes.find(key) == m_sectorLandTypes.end())
1453 	{
1454 		return -1;
1455 	}
1456 	return m_sectorLandTypes.at(key);
1457 }
1458 
getCacheSectors() const1459 const CacheSectorsModel* DefaultContentManager::getCacheSectors() const
1460 {
1461 	return m_cacheSectors;
1462 }
1463 
getMapSecrets() const1464 const std::vector<const StrategicMapSecretModel*>& DefaultContentManager::getMapSecrets() const
1465 {
1466 	return m_mapSecrets;
1467 }
1468 
getNpcPlacement(uint8_t profileId) const1469 const NpcPlacementModel* DefaultContentManager::getNpcPlacement(uint8_t profileId) const
1470 {
1471 	return m_npcPlacements.at(profileId);
1472 }
1473 
listNpcPlacements() const1474 const std::map<uint8_t, const NpcPlacementModel*>& DefaultContentManager::listNpcPlacements() const
1475 {
1476 	return m_npcPlacements;
1477 }
1478 
getRPCSmallFaceOffsets(uint8_t profileID) const1479 const RPCSmallFaceModel* DefaultContentManager::getRPCSmallFaceOffsets(uint8_t profileID) const
1480 {
1481 	if (m_rpcSmallFaces.find(profileID) == m_rpcSmallFaces.end())
1482 	{
1483 		return NULL;
1484 	}
1485 	return m_rpcSmallFaces.at(profileID);
1486 }
1487 
getMERCListings() const1488 const std::vector<const MERCListingModel*>& DefaultContentManager::getMERCListings() const
1489 {
1490 	return m_MERCListings;
1491 }
1492 
getMercProfileInfo(uint8_t const profileID) const1493 const MercProfileInfo* DefaultContentManager::getMercProfileInfo(uint8_t const profileID) const
1494 {
1495 	if (m_mercProfileInfo.find(profileID) != m_mercProfileInfo.end())
1496 	{
1497 		return m_mercProfileInfo.at(profileID);
1498 	}
1499 
1500 	STLOGD("MercProfileInfo is not defined at {}", profileID);
1501 	return &EMPTY_MERC_PROFILE_INFO;
1502 }
1503 
listMercProfiles() const1504 const std::vector<const MercProfile*>& DefaultContentManager::listMercProfiles() const
1505 {
1506 	return m_mercProfiles;
1507 }
1508 
getLoadingScreenForSector(uint8_t sectorId,uint8_t sectorLevel,bool isNight) const1509 const LoadingScreen* DefaultContentManager::getLoadingScreenForSector(uint8_t sectorId, uint8_t sectorLevel, bool isNight) const
1510 {
1511 	return m_loadingScreenModel->getScreenForSector(sectorId, sectorLevel, isNight);
1512 }
1513 
getLoadingScreen(uint8_t index) const1514 const LoadingScreen* DefaultContentManager::getLoadingScreen(uint8_t index) const
1515 {
1516 	return m_loadingScreenModel->getByIndex(index);
1517 }
1518