Sfoglia il codice sorgente

Fixed decompression of slayer.h3c from french version from gog.com

Ivan Savenko 1 anno fa
parent
commit
6d0803dab6

+ 22 - 9
lib/campaign/CampaignHandler.cpp

@@ -66,7 +66,7 @@ std::unique_ptr<Campaign> CampaignHandler::getHeader( const std::string & name)
 	
 	auto ret = std::make_unique<Campaign>();
 	auto fileStream = CResourceHandler::get(modName)->load(resourceID);
-	std::vector<ui8> cmpgn = getFile(std::move(fileStream), true)[0];
+	std::vector<ui8> cmpgn = getFile(std::move(fileStream), name, true)[0];
 
 	readCampaign(ret.get(), cmpgn, resourceID.getName(), modName, encoding);
 
@@ -84,7 +84,7 @@ std::shared_ptr<CampaignState> CampaignHandler::getCampaign( const std::string &
 	
 	auto fileStream = CResourceHandler::get(modName)->load(resourceID);
 
-	std::vector<std::vector<ui8>> files = getFile(std::move(fileStream), false);
+	std::vector<std::vector<ui8>> files = getFile(std::move(fileStream), name, false);
 
 	readCampaign(ret.get(), files[0], resourceID.getName(), modName, encoding);
 
@@ -578,19 +578,32 @@ CampaignTravel CampaignHandler::readScenarioTravelFromMemory(CBinaryReader & rea
 	return ret;
 }
 
-std::vector< std::vector<ui8> > CampaignHandler::getFile(std::unique_ptr<CInputStream> file, bool headerOnly)
+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< std::vector<ui8> > ret;
-	do
+
+	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)
 	{
-		std::vector<ui8> block(stream.getSize());
-		stream.read(block.data(), block.size());
-		ret.push_back(block);
-		ret.back().shrink_to_fit();
+		// 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());
 	}
-	while (!headerOnly && stream.getNextBlock());
 
 	return ret;
 }

+ 1 - 1
lib/campaign/CampaignHandler.h

@@ -31,7 +31,7 @@ class DLL_LINKAGE CampaignHandler
 	static CampaignTravel readScenarioTravelFromMemory(CBinaryReader & reader, CampaignVersion version);
 	/// returns h3c split in parts. 0 = h3c header, 1-end - maps (binary h3m)
 	/// headerOnly - only header will be decompressed, returned vector wont have any maps
-	static std::vector<std::vector<ui8>> getFile(std::unique_ptr<CInputStream> file, bool headerOnly);
+	static std::vector<std::vector<ui8>> getFile(std::unique_ptr<CInputStream> file, const std::string & filename, bool headerOnly);
 
 	static VideoPath prologVideoName(ui8 index);
 	static AudioPath prologMusicName(ui8 index);

+ 2 - 2
lib/filesystem/CCompressedStream.cpp

@@ -162,9 +162,9 @@ si64 CCompressedStream::readMore(ui8 *data, si64 size)
 			break;
 		default:
 			if (inflateState->msg == nullptr)
-				throw std::runtime_error("Decompression error. Return code was " + std::to_string(ret));
+				throw DecompressionException("Error code " + std::to_string(ret));
 			else
-				throw std::runtime_error(std::string("Decompression error: ") + inflateState->msg);
+				throw DecompressionException(inflateState->msg);
 		}
 	}
 	while (!endLoop && inflateState->avail_out != 0 );

+ 6 - 0
lib/filesystem/CCompressedStream.h

@@ -15,6 +15,12 @@ struct z_stream_s;
 
 VCMI_LIB_NAMESPACE_BEGIN
 
+class DecompressionException : public std::runtime_error
+{
+public:
+	using runtime_error::runtime_error;
+};
+
 /// 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