/* * CMapService.cpp, part of VCMI engine * * Authors: listed in file AUTHORS in main folder * * License: GNU General Public License v2.0 or later * Full text of license available in license.txt file, in main folder * */ #include "StdInc.h" #include "CMapService.h" #include "../filesystem/Filesystem.h" #include "../filesystem/CBinaryReader.h" #include "../filesystem/CCompressedStream.h" #include "../filesystem/CMemoryStream.h" #include "../filesystem/CMemoryBuffer.h" #include "../modding/CModHandler.h" #include "../modding/ModScope.h" #include "../modding/CModInfo.h" #include "../Languages.h" #include "../VCMI_Lib.h" #include "CMap.h" #include "MapFormat.h" #include "MapFormatH3M.h" #include "MapFormatJson.h" VCMI_LIB_NAMESPACE_BEGIN std::unique_ptr CMapService::loadMap(const ResourcePath & name, IGameCallback * cb) const { std::string modName = VLC->modh->findResourceOrigin(name); std::string language = VLC->modh->getModLanguage(modName); std::string encoding = Languages::getLanguageOptions(language).encoding; auto stream = getStreamFromFS(name); return getMapLoader(stream, name.getName(), modName, encoding)->loadMap(cb); } std::unique_ptr CMapService::loadMapHeader(const ResourcePath & name) const { std::string modName = VLC->modh->findResourceOrigin(name); std::string language = VLC->modh->getModLanguage(modName); std::string encoding = Languages::getLanguageOptions(language).encoding; auto stream = getStreamFromFS(name); return getMapLoader(stream, name.getName(), modName, encoding)->loadMapHeader(); } std::unique_ptr CMapService::loadMap(const uint8_t * buffer, int size, const std::string & name, const std::string & modName, const std::string & encoding, IGameCallback * cb) const { auto stream = getStreamFromMem(buffer, size); std::unique_ptr map(getMapLoader(stream, name, modName, encoding)->loadMap(cb)); std::unique_ptr header(map.get()); //might be original campaign and require patch getMapPatcher(name)->patchMapHeader(header); header.release(); return map; } std::unique_ptr CMapService::loadMapHeader(const uint8_t * buffer, int size, const std::string & name, const std::string & modName, const std::string & encoding) const { auto stream = getStreamFromMem(buffer, size); std::unique_ptr header = getMapLoader(stream, name, modName, encoding)->loadMapHeader(); //might be original campaign and require patch getMapPatcher(name)->patchMapHeader(header); return header; } void CMapService::saveMap(const std::unique_ptr & map, boost::filesystem::path fullPath) const { CMemoryBuffer serializeBuffer; { CMapSaverJson saver(&serializeBuffer); saver.saveMap(map); } { boost::filesystem::remove(fullPath); std::ofstream tmp(fullPath.c_str(), std::ofstream::binary); tmp.write(reinterpret_cast(serializeBuffer.getBuffer().data()), serializeBuffer.getSize()); tmp.flush(); tmp.close(); } } ModCompatibilityInfo CMapService::verifyMapHeaderMods(const CMapHeader & map) { const auto & activeMods = VLC->modh->getActiveMods(); ModCompatibilityInfo missingMods; ModCompatibilityInfo missingModsFiltered; for(const auto & mapMod : map.mods) { if(vstd::contains(activeMods, mapMod.first)) { const auto & modInfo = VLC->modh->getModInfo(mapMod.first); if(modInfo.getVerificationInfo().version.compatible(mapMod.second.version)) continue; } missingMods[mapMod.first] = mapMod.second; } //filter child mods for(const auto & mapMod : missingMods) { if(!mapMod.second.parent.empty() && missingMods.count(mapMod.second.parent)) continue; missingModsFiltered.insert(mapMod); } return missingModsFiltered; } std::unique_ptr CMapService::getStreamFromFS(const ResourcePath & name) { return CResourceHandler::get()->load(name); } std::unique_ptr CMapService::getStreamFromMem(const uint8_t * buffer, int size) { return std::unique_ptr(new CMemoryStream(buffer, size)); } std::unique_ptr CMapService::getMapLoader(std::unique_ptr & stream, std::string mapName, std::string modName, std::string encoding) { // Read map header CBinaryReader reader(stream.get()); ui32 header = reader.readUInt32(); reader.getStream()->seek(0); //check for ZIP magic. Zip files are VCMI maps switch(header) { case 0x06054b50: case 0x04034b50: case 0x02014b50: return std::unique_ptr(new CMapLoaderJson(stream.get())); break; default: // Check which map format is used // gzip header is 3 bytes only in size switch(header & 0xffffff) { // gzip header magic number, reversed for LE case 0x00088B1F: stream = std::unique_ptr(new CCompressedStream(std::move(stream), true)); return std::unique_ptr(new CMapLoaderH3M(mapName, modName, encoding, stream.get())); case static_cast(EMapFormat::WOG) : case static_cast(EMapFormat::AB) : case static_cast(EMapFormat::ROE) : case static_cast(EMapFormat::SOD) : case static_cast(EMapFormat::HOTA) : return std::unique_ptr(new CMapLoaderH3M(mapName, modName, encoding, stream.get())); default : throw std::runtime_error("Unknown map format"); } } } static JsonNode loadPatches(const std::string & path) { JsonNode node = JsonUtils::assembleFromFiles(path); for (auto & entry : node.Struct()) JsonUtils::validate(entry.second, "vcmi:mapHeader", "patch for " + entry.first); node.setMeta(ModScope::scopeMap()); return node; } std::unique_ptr CMapService::getMapPatcher(std::string scenarioName) { static const JsonNode node = loadPatches("config/mapOverrides.json"); boost::to_lower(scenarioName); logGlobal->debug("Request to patch map %s", scenarioName); return std::unique_ptr(new CMapPatcher(node[scenarioName])); } VCMI_LIB_NAMESPACE_END