Browse Source

zip format VCMP

Laserlicht 1 year ago
parent
commit
d97fdfdefa
2 changed files with 52 additions and 17 deletions
  1. 51 17
      lib/campaign/CampaignHandler.cpp
  2. 1 0
      lib/campaign/CampaignHandler.h

+ 51 - 17
lib/campaign/CampaignHandler.cpp

@@ -16,6 +16,7 @@
 #include "../filesystem/CCompressedStream.h"
 #include "../filesystem/CMemoryStream.h"
 #include "../filesystem/CBinaryReader.h"
+#include "../filesystem/CZipLoader.h"
 #include "../VCMI_Lib.h"
 #include "../constants/StringConstants.h"
 #include "../mapping/CMapHeader.h"
@@ -588,32 +589,65 @@ CampaignTravel CampaignHandler::readScenarioTravelFromMemory(CBinaryReader & rea
 
 std::vector< std::vector<ui8> > CampaignHandler::getFile(std::unique_ptr<CInputStream> file, const std::string & filename, bool headerOnly)
 {
-	CCompressedStream stream(std::move(file), true);
+	std::vector<ui8> magic(2);
+	file->read(magic.data(), magic.size());
+	file->seek(0);
 
 	std::vector< std::vector<ui8> > ret;
 
-	try
+	if (magic == std::vector<ui8>{0x50, 0x4B}) // VCMP (ZIP)
 	{
-		do
+		CInputStream * buffer(file.get());
+		std::shared_ptr<CIOApi> ioApi(new CProxyROIOApi(buffer));
+		CZipLoader loader("", "_", ioApi);
+
+		// load header
+		JsonPath resource = JsonPath::builtin(VCMP_HEADER_FILE_NAME);
+		if(!loader.existsResource(resource))
+			throw std::runtime_error(resource.getName() + " not found in " + filename);
+		auto data = loader.load(resource)->readAll();
+		ret.push_back(std::vector<ui8>(data.first.get(), data.first.get() + data.second));
+
+		// load scenarios
+		JsonNode header(reinterpret_cast<const std::byte*>(data.first.get()), data.second, VCMP_HEADER_FILE_NAME);
+		for(auto scenario : header["scenarios"].Vector())
 		{
-			std::vector<ui8> block(stream.getSize());
-			stream.read(block.data(), block.size());
-			ret.push_back(block);
-			ret.back().shrink_to_fit();
+			ResourcePath resource = ResourcePath(scenario["map"].String(), EResType::MAP);
+			if(!loader.existsResource(resource))
+				throw std::runtime_error(resource.getName() + " not found in " + filename);
+			auto data = loader.load(resource)->readAll();
+			ret.push_back(std::vector<ui8>(data.first.get(), data.first.get() + data.second));
 		}
-		while (!headerOnly && stream.getNextBlock());
+
+		return ret;
 	}
-	catch (const DecompressionException & e)
+	else // H3M
 	{
-		// Some campaigns in French version from gog.com have trailing garbage bytes
-		// For example, slayer.h3c consist from 5 parts: header + 4 maps
-		// However file also contains ~100 "extra" bytes after those 5 parts are decompressed that do not represent gzip stream
-		// leading to exception "Incorrect header check"
-		// Since H3 handles these files correctly, simply log this as warning and proceed
-		logGlobal->warn("Failed to read file %s. Encountered error during decompression: %s", filename, e.what());
-	}
+		CCompressedStream stream(std::move(file), true);
 
-	return ret;
+		try
+		{
+			do
+			{
+				std::vector<ui8> block(stream.getSize());
+				stream.read(block.data(), block.size());
+				ret.push_back(block);
+				ret.back().shrink_to_fit();
+			}
+			while (!headerOnly && stream.getNextBlock());
+		}
+		catch (const DecompressionException & e)
+		{
+			// Some campaigns in French version from gog.com have trailing garbage bytes
+			// For example, slayer.h3c consist from 5 parts: header + 4 maps
+			// However file also contains ~100 "extra" bytes after those 5 parts are decompressed that do not represent gzip stream
+			// leading to exception "Incorrect header check"
+			// Since H3 handles these files correctly, simply log this as warning and proceed
+			logGlobal->warn("Failed to read file %s. Encountered error during decompression: %s", filename, e.what());
+		}
+
+		return ret;
+	}
 }
 
 VideoPath CampaignHandler::prologVideoName(ui8 index)

+ 1 - 0
lib/campaign/CampaignHandler.h

@@ -37,6 +37,7 @@ class DLL_LINKAGE CampaignHandler
 	static AudioPath prologMusicName(ui8 index);
 	static AudioPath prologVoiceName(ui8 index);
 
+	static constexpr auto VCMP_HEADER_FILE_NAME = "header.json";
 public:
 	static std::unique_ptr<Campaign> getHeader( const std::string & name); //name - name of appropriate file