Przeglądaj źródła

Fixed parsing of HotA H3M format, including Seer Huts

Ivan Savenko 2 lat temu
rodzic
commit
88096de7e4

+ 6 - 5
lib/mapping/MapFeaturesH3M.cpp

@@ -15,7 +15,7 @@
 
 VCMI_LIB_NAMESPACE_BEGIN
 
-MapFormatFeaturesH3M MapFormatFeaturesH3M::find(EMapFormat format, uint8_t hotaVersion)
+MapFormatFeaturesH3M MapFormatFeaturesH3M::find(EMapFormat format, uint32_t hotaVersion)
 {
 	switch(format)
 	{
@@ -113,13 +113,14 @@ MapFormatFeaturesH3M MapFormatFeaturesH3M::getFeaturesWOG()
 	return result;
 }
 
-MapFormatFeaturesH3M MapFormatFeaturesH3M::getFeaturesHOTA(uint8_t hotaVersion)
+MapFormatFeaturesH3M MapFormatFeaturesH3M::getFeaturesHOTA(uint32_t hotaVersion)
 {
-	assert(hotaVersion == 1 || hotaVersion == 3);
+	assert(hotaVersion == 1 || hotaVersion == 2 || hotaVersion == 3);
 
 	MapFormatFeaturesH3M result = getFeaturesSOD();
 	result.levelHOTA = true;
-	result.levelHOTA3 = hotaVersion == 3;
+	//result.levelHOTA2 = hotaVersion > 1; // HOTA2 seems to be identical to HOTA1 so far
+	result.levelHOTA3 = hotaVersion > 2;
 
 	result.artifactsBytes = 21;
 	result.heroesBytes = 23;
@@ -129,7 +130,7 @@ MapFormatFeaturesH3M MapFormatFeaturesH3M::getFeaturesHOTA(uint8_t hotaVersion)
 	result.factionsCount = 10; // + Cove
 	result.creaturesCount = 171; // + Cove + neutrals
 
-	if(hotaVersion == 1)
+	if(hotaVersion == 1 || hotaVersion == 2)
 	{
 		result.artifactsCount = 163; // + HotA artifacts
 		result.heroesCount = 178; // + Cove

+ 2 - 2
lib/mapping/MapFeaturesH3M.h

@@ -17,12 +17,12 @@ enum class EMapFormat : uint8_t;
 struct MapFormatFeaturesH3M
 {
 public:
-	static MapFormatFeaturesH3M find(EMapFormat format, uint8_t hotaVersion);
+	static MapFormatFeaturesH3M find(EMapFormat format, uint32_t hotaVersion);
 	static MapFormatFeaturesH3M getFeaturesROE();
 	static MapFormatFeaturesH3M getFeaturesAB();
 	static MapFormatFeaturesH3M getFeaturesSOD();
 	static MapFormatFeaturesH3M getFeaturesWOG();
-	static MapFormatFeaturesH3M getFeaturesHOTA(uint8_t hotaVersion);
+	static MapFormatFeaturesH3M getFeaturesHOTA(uint32_t hotaVersion);
 
 	MapFormatFeaturesH3M() = default;
 

+ 45 - 32
lib/mapping/MapFormatH3M.cpp

@@ -155,16 +155,16 @@ void CMapLoaderH3M::readHeader()
 
 	if(mapHeader->version == EMapFormat::HOTA1 || mapHeader->version == EMapFormat::HOTA2 || mapHeader->version == EMapFormat::HOTA3)
 	{
-		uint8_t hotaVersion = reader->readUInt8();
+		uint32_t hotaVersion = reader->readUInt32();
 		features = MapFormatFeaturesH3M::find(mapHeader->version, hotaVersion);
 		reader->setFormatLevel(mapHeader->version, hotaVersion);
 
-		reader->skipZero(5);
-		if (hotaVersion == 3)
+		reader->skipZero(2);
+		if (hotaVersion > 1)
 		{
-			uint8_t unknown = reader->readUInt8();
-			logGlobal->error("%s -> header unknown: %d", mapName, int(unknown));
-			reader->skipZero(3);
+			uint8_t unknown = reader->readUInt32();
+			assert(unknown == 12);
+			MAYBE_UNUSED(unknown);
 		}
 	}
 	else
@@ -481,16 +481,18 @@ void CMapLoaderH3M::readVictoryLossConditions()
 				break;
 			}
 		case EVictoryConditionType::HOTA_ELIMINATE_ALL_MONSTERS:
-				logGlobal->error("Map %s - victory condition 'Eliminate all monsters' is not supported!", mapName);
 				//TODO: HOTA
+				logGlobal->error("Map %s - victory condition 'Eliminate all monsters' is not supported!", mapName);
 				break;
 		case EVictoryConditionType::HOTA_SURVIVE_FOR_DAYS:
-				logGlobal->error("Map %s - victory condition 'Survive for certain time' is not supported!", mapName);
+			{
 				//TODO: HOTA
-				reader->readUInt32(); // Number of days
+				uint32_t daysToSurvive = reader->readUInt32(); // Number of days
+				logGlobal->error("Map %s - victory condition 'Survive for %d days' is not supported!", mapName, daysToSurvive);
 				break;
+			}
 		default:
-			assert(0);
+				assert(0);
 		}
 
 		// if condition is human-only turn it into following construction: AllOf(human, condition)
@@ -661,27 +663,24 @@ void CMapLoaderH3M::readMapOptions()
 
 	if (features.levelHOTA)
 	{
-		std::vector<uint8_t> unknown(10);
-		for (size_t i = 0; i < 10; ++i)
-			unknown[i] = reader->readUInt8();
+		//TODO: HotA
+		bool allowSpecialMonths = reader->readBool();
+		if (!allowSpecialMonths)
+			logGlobal->warn("Map '%s': Option 'allow special months' is not implemented!", mapName);
 
-		assert(unknown[0] == 0 || unknown[0] == 1); // allowSpecialWeeks?
-		assert(unknown[1] == 0);
-		assert(unknown[2] == 0);
-		assert(unknown[3] == 0);
-		//assert(unknown[4] == 0 || unknown[4] == 16);
-		assert(unknown[4] == 16);
-		assert(unknown[5] == 0);
-		assert(unknown[6] == 0);
-		assert(unknown[7] == 0);
-		assert(unknown[8] == 0);
-		assert(unknown[9] == 0);
+		reader->skipZero(3);
+		uint8_t unknownConstant = reader->readUInt8();
+		assert(unknownConstant == 16);
+		MAYBE_UNUSED(unknownConstant);
+		reader->skipZero(5);
 	}
 
 	if (features.levelHOTA3)
 	{
-		uint32_t roundLimit = reader->readUInt32();
-		logGlobal->error("%s -> roundLimit of %d is not implemented!", mapName, roundLimit);
+		//TODO: HotA
+		int32_t roundLimit = reader->readInt32();
+		if (roundLimit != -1)
+			logGlobal->warn("Map '%s': roundLimit of %d is not implemented!", mapName, roundLimit);
 	}
 }
 
@@ -1036,13 +1035,14 @@ CGObjectInstance * CMapLoaderH3M::readMonster(const int3 & objPos, const ObjectI
 
 	if (features.levelHOTA3)
 	{
+		//TODO: HotA
 		uint32_t agressionExact = reader->readUInt32();
 		bool joinOnlyForMoney = reader->readBool();
 		uint32_t joinPercent = reader->readUInt32();
 		uint32_t upgradedStack = reader->readUInt32();
 		uint32_t splitStack = reader->readUInt32();
 
-		logGlobal->error("%s -> creature settings %d %d %d %d %d not implemeted!", agressionExact, int(joinOnlyForMoney), joinPercent, upgradedStack, splitStack);
+		logGlobal->warn("Map '%s': creature %s settings %d %d %d %d %d are not implemeted!", mapName, objPos.toString(), agressionExact, int(joinOnlyForMoney), joinPercent, upgradedStack, splitStack);
 	}
 
 	return object;
@@ -1312,6 +1312,7 @@ CGObjectInstance * CMapLoaderH3M::readBank(const int3 & position, std::shared_pt
 {
 	if (features.levelHOTA3)
 	{
+		//TODO: HotA
 		uint32_t field1 = reader->readUInt32();
 		uint8_t field2 = reader->readUInt8();
 
@@ -1321,7 +1322,7 @@ CGObjectInstance * CMapLoaderH3M::readBank(const int3 & position, std::shared_pt
 		{
 			artifacts.push_back(reader->readArtifact());
 		}
-		logGlobal->error("%s -> creature banks settings %d %d %d not implemeted!", field1, int(field2), artifacts.size());
+		logGlobal->warn("Map '%s: creature banks settings %d %d %d are not implemented!", field1, int(field2), artifacts.size());
 	}
 
 	return readBlank(position, objTempl);
@@ -1690,7 +1691,8 @@ CGObjectInstance * CMapLoaderH3M::readSeerHut(const int3 & position)
 		{
 			uint32_t questsCount = reader->readUInt32();
 			assert(questsCount == 1);
-			logGlobal->error("%s -> multiple quests: %d not implemented!", mapName, int(questsCount));
+			if (questsCount != 1)
+				logGlobal->error("%s -> multiple quests: %d not implemented!", mapName, int(questsCount));
 		}
 
 		readQuest(hut, position);
@@ -1796,6 +1798,15 @@ CGObjectInstance * CMapLoaderH3M::readSeerHut(const int3 & position)
 		reader->skipZero(3);
 	}
 
+	if (features.levelHOTA3)
+	{
+		uint32_t questsCount = reader->readUInt32();
+		assert(questsCount == 0);
+
+		if (questsCount != 0)
+			logGlobal->error("%s -> multiple quests: %d not implemented!", mapName, int(questsCount));
+	}
+
 	return hut;
 }
 
@@ -1939,8 +1950,9 @@ CGObjectInstance * CMapLoaderH3M::readTown(const int3 & position, std::shared_pt
 
 	if (features.levelHOTA)
 	{
-		bool spellResearch = reader->readBool();
-		logGlobal->error("%s -> spell research: %d not implemented!", mapName, int(spellResearch));
+		// TODO: HOTA support
+		bool spellResearchAvailable = reader->readBool();
+		MAYBE_UNUSED(spellResearchAvailable);
 	}
 
 	// Read castle events
@@ -1983,11 +1995,12 @@ CGObjectInstance * CMapLoaderH3M::readTown(const int3 & position, std::shared_pt
 
 	if(features.levelHOTA)
 	{
+		// TODO: HOTA support
 		uint8_t alignment = reader->readUInt8();
 		if (alignment < PlayerColor::PLAYER_LIMIT.getNum() || alignment == PlayerColor::NEUTRAL.getNum())
 			nt->alignmentToPlayer = PlayerColor(alignment);
 		else
-			logGlobal->error("%s - Aligment of town at %s 'not as player %d' is not implemented!", mapName, position.toString(), alignment - PlayerColor::PLAYER_LIMIT.getNum());
+			logGlobal->warn("%s - Aligment of town at %s 'not as player %d' is not implemented!", mapName, position.toString(), alignment - PlayerColor::PLAYER_LIMIT.getNum());
 	}
 	else if(features.levelSOD)
 	{