mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	- vcmi can now calculate crc32 checksum of a file
- reorganized internal filesystem structure - all files from one mod are now grouped in same FS node - modhandler will now calculate crc32 checksum for each mod - modhandler now knows validation status of each mod todo - use checksum to determine mods that have not changed since last start and disable validation for them.
This commit is contained in:
		| @@ -204,9 +204,10 @@ CContentHandler::ContentTypeHandler::ContentTypeHandler(IHandlerBase * handler, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void CContentHandler::ContentTypeHandler::preloadModData(std::string modName, std::vector<std::string> fileList) | ||||
| bool CContentHandler::ContentTypeHandler::preloadModData(std::string modName, std::vector<std::string> fileList) | ||||
| { | ||||
| 	JsonNode data = JsonUtils::assembleFromFiles(fileList); | ||||
| 	bool result; | ||||
| 	JsonNode data = JsonUtils::assembleFromFiles(fileList, result); | ||||
| 	data.setMeta(modName); | ||||
|  | ||||
| 	ModInfo & modInfo = modData[modName]; | ||||
| @@ -234,11 +235,13 @@ void CContentHandler::ContentTypeHandler::preloadModData(std::string modName, st | ||||
| 			JsonUtils::merge(remoteConf, entry.second); | ||||
| 		} | ||||
| 	} | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| void CContentHandler::ContentTypeHandler::loadMod(std::string modName) | ||||
| bool CContentHandler::ContentTypeHandler::loadMod(std::string modName) | ||||
| { | ||||
| 	ModInfo & modInfo = modData[modName]; | ||||
| 	bool result = true; | ||||
|  | ||||
| 	// apply patches | ||||
| 	if (!modInfo.patches.isNull()) | ||||
| @@ -257,7 +260,7 @@ void CContentHandler::ContentTypeHandler::loadMod(std::string modName) | ||||
| 			if (originalData.size() > index) | ||||
| 			{ | ||||
| 				JsonUtils::merge(originalData[index], data); | ||||
| 				JsonUtils::validate(originalData[index], "vcmi:" + objectName, name); | ||||
| 				result &= JsonUtils::validate(originalData[index], "vcmi:" + objectName, name); | ||||
| 				handler->loadObject(modName, name, originalData[index], index); | ||||
|  | ||||
| 				originalData[index].clear(); // do not use same data twice (same ID) | ||||
| @@ -266,9 +269,10 @@ void CContentHandler::ContentTypeHandler::loadMod(std::string modName) | ||||
| 			} | ||||
| 		} | ||||
| 		// normal new object or one with index bigger that data size | ||||
| 		JsonUtils::validate(data, "vcmi:" + objectName, name); | ||||
| 		result &= JsonUtils::validate(data, "vcmi:" + objectName, name); | ||||
| 		handler->loadObject(modName, name, data); | ||||
| 	} | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| void CContentHandler::ContentTypeHandler::afterLoadFinalization() | ||||
| @@ -287,20 +291,24 @@ CContentHandler::CContentHandler() | ||||
| 	//TODO: spells, bonuses, something else? | ||||
| } | ||||
|  | ||||
| void CContentHandler::preloadModData(std::string modName, JsonNode modConfig) | ||||
| bool CContentHandler::preloadModData(std::string modName, JsonNode modConfig) | ||||
| { | ||||
| 	bool result = true; | ||||
| 	for(auto & handler : handlers) | ||||
| 	{ | ||||
| 		handler.second.preloadModData(modName, modConfig[handler.first].convertTo<std::vector<std::string> >()); | ||||
| 		result &= handler.second.preloadModData(modName, modConfig[handler.first].convertTo<std::vector<std::string> >()); | ||||
| 	} | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| void CContentHandler::loadMod(std::string modName) | ||||
| bool CContentHandler::loadMod(std::string modName) | ||||
| { | ||||
| 	bool result = true; | ||||
| 	for(auto & handler : handlers) | ||||
| 	{ | ||||
| 		handler.second.loadMod(modName); | ||||
| 		result &= handler.second.loadMod(modName); | ||||
| 	} | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| void CContentHandler::afterLoadFinalization() | ||||
| @@ -520,11 +528,70 @@ void CModHandler::initialize(std::vector<std::string> availableMods) | ||||
|  | ||||
| 	std::ofstream file(*CResourceHandler::get()->getResourceName(ResourceID("config/modSettings.json")), std::ofstream::trunc); | ||||
| 	file << modConfig; | ||||
|  | ||||
| 	loadModFilesystems(); | ||||
| } | ||||
|  | ||||
| std::vector<std::string> CModHandler::getActiveMods() | ||||
| static JsonNode genDefaultFS() | ||||
| { | ||||
| 	return activeMods; | ||||
| 	// default FS config for mods: directory "Content" that acts as H3 root directory | ||||
| 	JsonNode defaultFS; | ||||
| 	defaultFS[""].Vector().resize(2); | ||||
| 	defaultFS[""].Vector()[0]["type"].String() = "zip"; | ||||
| 	defaultFS[""].Vector()[0]["path"].String() = "/Content.zip"; | ||||
| 	defaultFS[""].Vector()[1]["type"].String() = "dir"; | ||||
| 	defaultFS[""].Vector()[1]["path"].String() = "/Content"; | ||||
| 	return defaultFS; | ||||
| } | ||||
|  | ||||
| static ISimpleResourceLoader * genModFilesystem(const std::string & modName, const JsonNode & conf) | ||||
| { | ||||
| 	static const JsonNode defaultFS = genDefaultFS(); | ||||
|  | ||||
| 	if (!conf["filesystem"].isNull()) | ||||
| 		return CResourceHandler::createFileSystem("mods/" + modName, conf["filesystem"]); | ||||
| 	else | ||||
| 		return CResourceHandler::createFileSystem("mods/" + modName, defaultFS); | ||||
| } | ||||
|  | ||||
| static ui32 calculateModChecksum(const std::string modName, ISimpleResourceLoader * filesystem) | ||||
| { | ||||
| 	boost::crc_32_type modChecksum; | ||||
| 	// first - add current VCMI version into checksum to force re-validation on VCMI updates | ||||
| 	modChecksum.process_bytes(reinterpret_cast<const void*>(GameConstants::VCMI_VERSION.data()), GameConstants::VCMI_VERSION.size()); | ||||
|  | ||||
| 	// second - add mod.json into checksum because filesystem does not contains this file | ||||
| 	ResourceID modConfFile("mods/" + modName + "/mod", EResType::TEXT); | ||||
| 	ui32 configChecksum = CResourceHandler::getInitial()->load(modConfFile)->calculateCRC32(); | ||||
| 	modChecksum.process_bytes(reinterpret_cast<const void *>(&configChecksum), sizeof(configChecksum)); | ||||
|  | ||||
| 	// third - add all loaded files from this mod into checksum | ||||
| 	auto files = filesystem->getFilteredFiles([](const ResourceID & resID) | ||||
| 	{ | ||||
| 		return resID.getType() == EResType::TEXT; | ||||
| 	}); | ||||
|  | ||||
| 	for (const ResourceID & file : files) | ||||
| 	{ | ||||
| 		ui32 fileChecksum = filesystem->load(file)->calculateCRC32(); | ||||
| 		modChecksum.process_bytes(reinterpret_cast<const void *>(&fileChecksum), sizeof(fileChecksum)); | ||||
| 	} | ||||
| 	return modChecksum.checksum(); | ||||
| } | ||||
|  | ||||
| void CModHandler::loadModFilesystems() | ||||
| { | ||||
| 	for(std::string & modName : activeMods) | ||||
| 	{ | ||||
| 		ResourceID modConfFile("mods/" + modName + "/mod", EResType::TEXT); | ||||
| 		auto fsConfigData = CResourceHandler::getInitial()->load(modConfFile)->readAll(); | ||||
| 		const JsonNode fsConfig((char*)fsConfigData.first.get(), fsConfigData.second); | ||||
|  | ||||
| 		auto filesystem = genModFilesystem(modName, fsConfig); | ||||
|  | ||||
| 		CResourceHandler::get()->addLoader(filesystem, false); | ||||
| 		allMods[modName].checksum = calculateModChecksum(modName, filesystem); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| CModInfo & CModHandler::getModData(TModID modId) | ||||
| @@ -552,14 +619,16 @@ void CModHandler::loadGameContent() | ||||
|  | ||||
| 	for(const TModID & modName : activeMods) | ||||
| 	{ | ||||
| 		logGlobal->infoStream() << "\t\t" << allMods[modName].name; | ||||
| 		// print message in format [<8-symbols checksum>] <modname> | ||||
| 		logGlobal->infoStream() << "\t\t[" << std::noshowbase << std::hex << std::setw(8) << std::setfill('0') | ||||
| 								<< allMods[modName].checksum << "] " << allMods[modName].name; | ||||
|  | ||||
| 		std::string modFileName = "mods/" + modName + "/mod.json"; | ||||
|  | ||||
| 		const JsonNode config = JsonNode(ResourceID(modFileName)); | ||||
| 		JsonUtils::validate(config, "vcmi:mod", modName); | ||||
| 		allMods[modName].validated = JsonUtils::validate(config, "vcmi:mod", modName); | ||||
|  | ||||
| 		content.preloadModData(modName, config); | ||||
| 		allMods[modName].validated &= content.preloadModData(modName, config); | ||||
| 	} | ||||
| 	logGlobal->infoStream() << "\tParsing mod data: " << timer.getDiff() << " ms"; | ||||
|  | ||||
| @@ -568,8 +637,11 @@ void CModHandler::loadGameContent() | ||||
|  | ||||
| 	for(const TModID & modName : activeMods) | ||||
| 	{ | ||||
| 		content.loadMod(modName); | ||||
| 		logGlobal->infoStream() << "\t\t" << allMods[modName].name; | ||||
| 		allMods[modName].validated &= content.loadMod(modName); | ||||
| 		if (allMods[modName].validated) | ||||
| 			logGlobal->infoStream()  << "\t\t[DONE] " << allMods[modName].name; | ||||
| 		else | ||||
| 			logGlobal->errorStream() << "\t\t[FAIL] " << allMods[modName].name; | ||||
| 	} | ||||
| 	logGlobal->infoStream() << "\tLoading mod data: " << timer.getDiff() << "ms"; | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user