mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	- ZipArchive namespace for operations with zip archives, located in CZipLoader.h/cpp.
- new fields in mod format, for use with mod manager (check config/shemas/mod.json for details) - removed some 0.92 compatibility from mods loading - several compile fixes
This commit is contained in:
		| @@ -1612,7 +1612,7 @@ void VCAI::reserveObject(HeroPtr h, const CGObjectInstance *obj) | ||||
| { | ||||
| 	reservedObjs.push_back(obj); | ||||
| 	reservedHeroesMap[h].push_back(obj); | ||||
| 	logAi->debugStream() << "reserved object id=" << obj->id << "; address=" << (int)obj << "; name=" << obj->getHoverText(); | ||||
| 	logAi->debugStream() << "reserved object id=" << obj->id << "; address=" << (intptr_t)obj << "; name=" << obj->getHoverText(); | ||||
| } | ||||
|  | ||||
| void VCAI::validateVisitableObjs() | ||||
|   | ||||
| @@ -1,5 +1,8 @@ | ||||
| project(vcmi) | ||||
| cmake_minimum_required(VERSION 2.6) | ||||
| # TODO: | ||||
| # 1) Detection of system version of minizip and use it instead of local | ||||
| # 2) Detection of Qt5 and compilation of launcher, unless explicitly disabled | ||||
|  | ||||
| # where to look for cmake modules | ||||
| set(CMAKE_MODULE_PATH ${CMAKE_HOME_DIRECTORY}/cmake_modules) | ||||
| @@ -16,6 +19,7 @@ set(VCMI_VERSION_PATCH 0) | ||||
|  | ||||
| option(ENABLE_ERM "Enable compilation of ERM scripting module" OFF) | ||||
| option(ENABLE_EDITOR "Enable compilation of map editor" OFF) | ||||
| option(ENABLE_LAUNCHER "Enable compilation of launcher" OFF) | ||||
| option(ENABLE_TEST "Enable compilation of unit tests" OFF) | ||||
|  | ||||
| ############################################ | ||||
| @@ -52,11 +56,15 @@ find_package(SDL_mixer REQUIRED) | ||||
| find_package(SDL_ttf REQUIRED) | ||||
| find_package(ZLIB REQUIRED) | ||||
|  | ||||
| if (ENABLE_EDITOR) | ||||
| if (ENABLE_EDITOR OR ENABLE_LAUNCHER) | ||||
| 	# Widgets finds its own dependencies (QtGui and QtCore). | ||||
| 	find_package(Qt5Widgets REQUIRED) | ||||
| endif() | ||||
|  | ||||
| if (ENABLE_LAUNCHER) | ||||
| 	find_package(Qt5Network REQUIRED) | ||||
| endif() | ||||
|  | ||||
| if(ENABLE_TEST) | ||||
| 	# find_package overwrites BOOST_* variables which are already set, so all components have to be | ||||
| 	# included again | ||||
| @@ -133,6 +141,9 @@ endif() | ||||
| if (ENABLE_EDITOR) | ||||
| 	add_subdirectory(editor) | ||||
| endif() | ||||
| if (ENABLE_LAUNCHER) | ||||
| 	add_subdirectory(launcher) | ||||
| endif() | ||||
| if(ENABLE_TEST) | ||||
| 	add_subdirectory(test) | ||||
| endif() | ||||
|   | ||||
| @@ -1,4 +1,25 @@ | ||||
| { | ||||
| 	"name" : "In The Wake of Gods", | ||||
| 	"description" : "Unnofficial addon for Heroes of Might and Magic III", | ||||
|  | ||||
| 	"version" : "3.58.0", | ||||
| 	"author" : "WoG Team", | ||||
|  | ||||
| 	"artifacts" :  | ||||
| 	[ | ||||
| 		"config/wog/artifacts.json" | ||||
| 	], | ||||
|  | ||||
| 	"creatures" :  | ||||
| 	[ | ||||
| 		"config/wog/creatures.json" | ||||
| 	], | ||||
|  | ||||
| 	"factions" :  | ||||
| 	[ | ||||
| 		"config/wog/factions.json" | ||||
| 	], | ||||
|  | ||||
| 	"filesystem": | ||||
| 	{ | ||||
| 		"" : | ||||
| @@ -39,23 +60,5 @@ | ||||
| 		[ | ||||
| 			{"type" : "dir", "path" : "/Maps"} | ||||
| 		] | ||||
| 	}, | ||||
|  | ||||
| 	"name" : "In The Wake of Gods", | ||||
| 	"description" : "Unnofficial addon for Heroes of Might and Magic III", | ||||
|  | ||||
| 	"artifacts" :  | ||||
| 	[ | ||||
| 		"config/wog/artifacts.json" | ||||
| 	], | ||||
|  | ||||
| 	"creatures" :  | ||||
| 	[ | ||||
| 		"config/wog/creatures.json" | ||||
| 	], | ||||
|  | ||||
| 	"factions" :  | ||||
| 	[ | ||||
| 		"config/wog/factions.json" | ||||
| 	] | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -1,4 +1,10 @@ | ||||
| { | ||||
| 	"name" : "VCMI essential files", | ||||
| 	"description" : "Essential files required for VCMI to run correctly", | ||||
|  | ||||
| 	"version" : "0.0", | ||||
| 	"author" : "VCMI Team", | ||||
|  | ||||
| 	"filesystem": | ||||
| 	{ | ||||
| 		"DATA/" : | ||||
| @@ -13,8 +19,5 @@ | ||||
| 		[ | ||||
| 			{"type" : "dir",  "path" : "/Maps"} | ||||
| 		] | ||||
| 	}, | ||||
|  | ||||
| 	"name" : "VCMI essential files", | ||||
| 	"description" : "Essential files required for VCMI to run correctly" | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -3,7 +3,7 @@ | ||||
| 	"$schema": "http://json-schema.org/draft-04/schema", | ||||
| 	"title" : "VCMI mod file format", | ||||
| 	"description" : "Format used to define main mod file (mod.json) in VCMI", | ||||
| 	"required" : [ "name", "description" ], | ||||
| 	"required" : [ "name", "description", "version", "author" ], | ||||
|  | ||||
| 	"additionalProperties" : false, | ||||
| 	"properties":{ | ||||
| @@ -16,6 +16,26 @@ | ||||
| 			"description": "More lengthy description of mod. No hard limit" | ||||
| 		}, | ||||
|  | ||||
| 		"modType" : { | ||||
| 			"type":"string", | ||||
| 			"description": "Type of mod, e.g. Town, Artifacts, Graphical." | ||||
| 		}, | ||||
|  | ||||
| 		"version" : { | ||||
| 			"type":"string", | ||||
| 			"description": "Current mod version, up to 3 numbers, dot-separated. Format: A.B.C" | ||||
| 		}, | ||||
|  | ||||
| 		"author" : { | ||||
| 			"type":"string", | ||||
| 			"description": "Author of the mod. Can be nickname, real name or name of team" | ||||
| 		}, | ||||
|  | ||||
| 		"weblink" : { | ||||
| 			"type":"string", | ||||
| 			"description": "Home page of mod or link to forum thread" | ||||
| 		}, | ||||
|  | ||||
| 		"depends": { | ||||
| 			"type":"array", | ||||
| 			"description": "List of mods that are required to run this one", | ||||
| @@ -69,7 +89,7 @@ | ||||
| 						}, | ||||
| 						"type": { | ||||
| 							"type" : "string", | ||||
| 							"enum" : [ "dir", "lod", "snd", "vid", "map" ], | ||||
| 							"enum" : [ "dir", "lod", "snd", "vid", "map", "zip" ], | ||||
| 							"description" : "Type of data source" | ||||
| 						} | ||||
| 					} | ||||
|   | ||||
| @@ -3,7 +3,7 @@ | ||||
| { | ||||
| 	"type" : "object", | ||||
| 	"$schema": "http://json-schema.org/draft-04/schema", | ||||
| 	"required" : [ "general", "video", "adventure", "battle", "server", "logging" ], | ||||
| 	"required" : [ "general", "video", "adventure", "battle", "server", "logging", "launcher" ], | ||||
| 	"definitions" : { | ||||
| 		"logLevelEnum" : {  | ||||
| 			"type" : "string",  | ||||
| @@ -224,6 +224,21 @@ | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		}, | ||||
| 		"launcher" : { | ||||
| 			"type" : "object", | ||||
| 			"default": {}, | ||||
| 			"additionalProperties" : false, | ||||
| 			"required" : [ "repositoryURL" ], | ||||
| 			"properties" : { | ||||
| 				"repositoryURL" : { | ||||
| 					"type" : "array", | ||||
| 					"default" : [ ], | ||||
| 					"items" : { | ||||
| 						"type" : "string" | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -2184,7 +2184,7 @@ SpellID CBattleInfoCallback::getRandomBeneficialSpell(const CStack * subject) co | ||||
| 				break; | ||||
| 			case SpellID::SLAYER://only if monsters are present | ||||
| 				{ | ||||
| 					auto kingMonster = getStackIf([&](const CStack *stack) //look for enemy, non-shooting stack | ||||
| 					auto kingMonster = getStackIf([&](const CStack *stack) -> bool //look for enemy, non-shooting stack | ||||
| 					{ | ||||
| 						const auto isKing = Selector::type(Bonus::KING1) | ||||
| 							.Or(Selector::type(Bonus::KING2)) | ||||
|   | ||||
| @@ -294,18 +294,11 @@ void CHeroHandler::loadHeroSkills(CHero * hero, const JsonNode & node) | ||||
|  | ||||
| 	for(const JsonNode & spell : node["spellbook"].Vector()) | ||||
| 	{ | ||||
| 		if (spell.getType() == JsonNode::DATA_FLOAT) // for compatibility | ||||
| 		VLC->modh->identifiers.requestIdentifier("spell", spell, | ||||
| 		[=](si32 spellID) | ||||
| 		{ | ||||
| 			hero->spells.insert(SpellID(spell.Float())); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			VLC->modh->identifiers.requestIdentifier("spell", spell, | ||||
| 			[=](si32 spellID) | ||||
| 			{ | ||||
| 				hero->spells.insert(SpellID(spellID)); | ||||
| 			}); | ||||
| 		} | ||||
| 			hero->spells.insert(SpellID(spellID)); | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -156,10 +156,6 @@ bool CIdentifierStorage::resolveIdentifier(const ObjectCallback & request) | ||||
| 		{ | ||||
| 			logGlobal->errorStream() << "\tID is available in mod " << it->second.scope; | ||||
| 		} | ||||
|  | ||||
| 		// temporary code to smooth 0.92->0.93 transition | ||||
| 		request.callback(entries.first->second.id); | ||||
| 		return true; | ||||
| 	} | ||||
| 	logGlobal->errorStream() << "Unknown identifier " << request.type << "." << request.name << " from mod " << request.localScope; | ||||
| 	return false; | ||||
|   | ||||
| @@ -339,7 +339,7 @@ void JsonWriter::writeEntry(JsonVector::const_iterator entry) | ||||
|  | ||||
| void JsonWriter::writeString(const std::string &string) | ||||
| { | ||||
| 	static const std::string escaped = "\"\\/\b\f\n\r\t"; | ||||
| 	static const std::string escaped = "\"\\\b\f\n\r\t"; | ||||
|  | ||||
| 	out <<'\"'; | ||||
| 	size_t pos=0, start=0; | ||||
| @@ -506,7 +506,6 @@ bool JsonParser::extractEscaping(std::string &str) | ||||
| 	{ | ||||
| 		break; case '\"': str += '\"'; | ||||
| 		break; case '\\': str += '\\'; | ||||
| 		break; case  '/': str += '/'; | ||||
| 		break; case 'b': str += '\b'; | ||||
| 		break; case 'f': str += '\f'; | ||||
| 		break; case 'n': str += '\n'; | ||||
|   | ||||
| @@ -302,7 +302,7 @@ DLL_LINKAGE void RemoveObject::applyGs( CGameState *gs ) | ||||
| { | ||||
|  | ||||
| 	CGObjectInstance *obj = gs->getObjInstance(id); | ||||
| 	logGlobal->debugStream() << "removing object id=" << id << "; address=" << (int)obj << "; name=" << obj->getHoverText(); | ||||
| 	logGlobal->debugStream() << "removing object id=" << id << "; address=" << (intptr_t)obj << "; name=" << obj->getHoverText(); | ||||
| 	//unblock tiles | ||||
| 	if(obj->defInfo) | ||||
| 	{ | ||||
| @@ -595,7 +595,7 @@ DLL_LINKAGE void NewObject::applyGs( CGameState *gs ) | ||||
| 	o->initObj(); | ||||
| 	assert(o->defInfo); | ||||
|  | ||||
| 	logGlobal->debugStream() << "added object id=" << id << "; address=" << (int)o << "; name=" << o->getHoverText(); | ||||
| 	logGlobal->debugStream() << "added object id=" << id << "; address=" << (intptr_t)o << "; name=" << o->getHoverText(); | ||||
| } | ||||
|  | ||||
| DLL_LINKAGE void NewArtifact::applyGs( CGameState *gs ) | ||||
|   | ||||
| @@ -14,6 +14,8 @@ | ||||
|  | ||||
| struct z_stream_s; | ||||
|  | ||||
| /// Abstract class that provides buffer for one-directional input streams (e.g. compressed data) | ||||
| /// Used for zip archives support and in .lod deflate compression | ||||
| class CBufferedStream : public CInputStream | ||||
| { | ||||
| public: | ||||
| @@ -86,7 +88,7 @@ private: | ||||
|  | ||||
| /** | ||||
|  * A class which provides method definitions for reading a gzip-compressed file | ||||
|  * This class implements lazy loading - data will be decompressed (and cached by this class) only by request | ||||
|  * This class implements lazy loading - data will be decompressed (and cached) only by request | ||||
|  */ | ||||
| class DLL_LINKAGE CCompressedStream : public CBufferedStream | ||||
| { | ||||
| @@ -103,7 +105,7 @@ public: | ||||
| 	~CCompressedStream(); | ||||
|  | ||||
| 	/** | ||||
| 	 * Prepare stream for decompression of next block (e.g. nect part of h3c) | ||||
| 	 * Prepare stream for decompression of next block (e.g. next part of h3c) | ||||
| 	 * Applicable only for streams that contain multiple concatenated compressed data | ||||
| 	 * | ||||
| 	 * @return false if next block was not found, true othervice | ||||
|   | ||||
| @@ -2,6 +2,8 @@ | ||||
| #include "../../Global.h" | ||||
| #include "CZipLoader.h" | ||||
|  | ||||
| #include "../ScopeGuard.h" | ||||
|  | ||||
| /* | ||||
|  * CZipLoader.cpp, part of VCMI engine | ||||
|  * | ||||
| @@ -99,4 +101,102 @@ std::unordered_set<ResourceID> CZipLoader::getFilteredFiles(std::function<bool(c | ||||
| 			foundID.insert(file.first); | ||||
| 	} | ||||
| 	return foundID; | ||||
| } | ||||
| } | ||||
|  | ||||
| /// extracts currently selected file from zip into stream "where" | ||||
| static bool extractCurrent(unzFile file, std::ostream & where) | ||||
| { | ||||
| 	std::array<char, 8 * 1024> buffer; | ||||
|  | ||||
| 	unzOpenCurrentFile(file); | ||||
|  | ||||
| 	while (1) | ||||
| 	{ | ||||
| 		int readSize = unzReadCurrentFile(file, buffer.data(), buffer.size()); | ||||
|  | ||||
| 		if (readSize < 0) // error | ||||
| 			break; | ||||
|  | ||||
| 		if (readSize == 0) // end-of-file. Also performs CRC check | ||||
| 			return unzCloseCurrentFile(file) == UNZ_OK; | ||||
|  | ||||
| 		if (readSize > 0) // successfull read | ||||
| 		{ | ||||
| 			where.write(buffer.data(), readSize); | ||||
| 			if (!where.good()) | ||||
| 				break; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// extraction failed. Close file and exit | ||||
| 	unzCloseCurrentFile(file); | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| std::vector<std::string> ZipArchive::listFiles(std::string filename) | ||||
| { | ||||
| 	std::vector<std::string> ret; | ||||
|  | ||||
| 	unzFile file = unzOpen(filename.c_str()); | ||||
|  | ||||
| 	if (unzGoToFirstFile(file) == UNZ_OK) | ||||
| 	{ | ||||
| 		do | ||||
| 		{ | ||||
| 			unz_file_info info; | ||||
| 			std::vector<char> filename; | ||||
|  | ||||
| 			unzGetCurrentFileInfo (file, &info, nullptr, 0, nullptr, 0, nullptr, 0); | ||||
|  | ||||
| 			filename.resize(info.size_filename); | ||||
| 			// Get name of current file. Contrary to docs "info" parameter can't be null | ||||
| 			unzGetCurrentFileInfo (file, &info, filename.data(), filename.size(), nullptr, 0, nullptr, 0); | ||||
|  | ||||
| 			ret.push_back(std::string(filename.data(), filename.size())); | ||||
| 		} | ||||
| 		while (unzGoToNextFile(file) == UNZ_OK); | ||||
| 	} | ||||
| 	unzClose(file); | ||||
|  | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| bool ZipArchive::extract(std::string from, std::string where) | ||||
| { | ||||
| 	// Note: may not be fast enough for large archives (should NOT happen with mods) | ||||
| 	// because locating each file by name may be slow. Unlikely slower than decompression though | ||||
| 	return extract(from, where, listFiles(from)); | ||||
| } | ||||
|  | ||||
| bool ZipArchive::extract(std::string from, std::string where, std::vector<std::string> what) | ||||
| { | ||||
| 	unzFile archive = unzOpen(from.c_str()); | ||||
|  | ||||
| 	auto onExit = vstd::makeScopeGuard([&]() | ||||
| 	{ | ||||
| 		unzClose(archive); | ||||
| 	}); | ||||
|  | ||||
| 	for (std::string & file : what) | ||||
| 	{ | ||||
| 		if (unzLocateFile(archive, file.c_str(), 1) != UNZ_OK) | ||||
| 			return false; | ||||
|  | ||||
| 		std::string fullName = where + '/' + file; | ||||
| 		std::string fullPath = fullName.substr(0, fullName.find_last_of("/")); | ||||
|  | ||||
| 		boost::filesystem::create_directories(fullPath); | ||||
| 		// directory. No file to extract | ||||
| 		// TODO: better way to detect directory? Probably check return value of unzOpenCurrentFile? | ||||
| 		if (boost::algorithm::ends_with(file, "/")) | ||||
| 			continue; | ||||
|  | ||||
| 		std::ofstream destFile(fullName); | ||||
| 		if (!destFile.good()) | ||||
| 			return false; | ||||
|  | ||||
| 		if (!extractCurrent(archive, destFile)) | ||||
| 			return false; | ||||
| 	} | ||||
| 	return true; | ||||
| } | ||||
|   | ||||
| @@ -54,4 +54,17 @@ public: | ||||
| 	bool existsResource(const ResourceID & resourceName) const override; | ||||
| 	std::string getMountPoint() const override; | ||||
| 	std::unordered_set<ResourceID> getFilteredFiles(std::function<bool(const ResourceID &)> filter) const override; | ||||
| }; | ||||
| }; | ||||
|  | ||||
|  | ||||
| namespace ZipArchive | ||||
| { | ||||
| 	/// List all files present in archive | ||||
| 	std::vector<std::string> DLL_LINKAGE listFiles(std::string filename); | ||||
|  | ||||
| 	/// extracts all files from archive "from" into destination directory "where". Directory must exist | ||||
| 	bool DLL_LINKAGE extract(std::string from, std::string where); | ||||
|  | ||||
| 	///same as above, but extracts only files mentioned in "what" list | ||||
| 	bool DLL_LINKAGE extract(std::string from, std::string where, std::vector<std::string> what); | ||||
| } | ||||
| @@ -22,6 +22,7 @@ | ||||
| #include "../lib/VCMI_Lib.h" | ||||
| #include "../lib/mapping/CMap.h" | ||||
| #include "../lib/VCMIDirs.h" | ||||
| #include "../lib/ScopeGuard.h" | ||||
| #include "../client/CSoundBase.h" | ||||
| #include "CGameHandler.h" | ||||
| #include "CVCMIServer.h" | ||||
| @@ -3536,7 +3537,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba ) | ||||
|  | ||||
| 			StartAction start_action(ba); | ||||
| 			sendAndApply(&start_action); | ||||
| 			auto makeScopeGuard([&]{ sendAndApply(&end_action); }); //if we started than we have to finish | ||||
| 			auto onExit = vstd::makeScopeGuard([&]{ sendAndApply(&end_action); }); //if we started than we have to finish | ||||
|  | ||||
| 			const CGHeroInstance * attackingHero = gs->curB->battleGetFightingHero(ba.side); | ||||
| 			CHeroHandler::SBallisticsLevelInfo sbi = VLC->heroh->ballistics[attackingHero->getSecSkillLevel(SecondarySkill::BALLISTICS)]; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user