浏览代码

Implemented parsing of HotA h3m header

Ivan Savenko 2 年之前
父节点
当前提交
838d45b32c
共有 4 个文件被更改,包括 77 次插入34 次删除
  1. 9 7
      lib/mapping/CMap.h
  2. 3 0
      lib/mapping/CMapService.cpp
  3. 11 1
      lib/mapping/MapFeaturesH3M.cpp
  4. 54 26
      lib/mapping/MapFormatH3M.cpp

+ 9 - 7
lib/mapping/CMap.h

@@ -253,13 +253,15 @@ struct DLL_LINKAGE DisposedHero
 enum class EMapFormat: uint8_t
 {
 	INVALID = 0,
-	//    HEX     DEC
-	ROE  = 0x0e, // 14
-	AB   = 0x15, // 21
-	SOD  = 0x1c, // 28
-	HOTA = 0x1e, // 30
-	WOG  = 0x33, // 51
-	VCMI = 0xF0
+	//       HEX    DEC
+	ROE   = 0x0e, // 14
+	AB    = 0x15, // 21
+	SOD   = 0x1c, // 28
+	HOTA1 = 0x1e, // 30
+	HOTA2 = 0x1f, // 31
+	HOTA3 = 0x20, // 32
+	WOG   = 0x33, // 51
+	VCMI  = 0xF0
 };
 
 /// The map header holds information about loss/victory condition,map format, version, players, height, width,...

+ 3 - 0
lib/mapping/CMapService.cpp

@@ -124,6 +124,9 @@ std::unique_ptr<IMapLoader> CMapService::getMapLoader(std::unique_ptr<CInputStre
 			case static_cast<int>(EMapFormat::AB)  :
 			case static_cast<int>(EMapFormat::ROE) :
 			case static_cast<int>(EMapFormat::SOD) :
+			case static_cast<int>(EMapFormat::HOTA1) :
+			case static_cast<int>(EMapFormat::HOTA2) :
+			case static_cast<int>(EMapFormat::HOTA3) :
 				return std::unique_ptr<IMapLoader>(new CMapLoaderH3M(mapName, modName, encoding, stream.get()));
 			default :
 				throw std::runtime_error("Unknown map format");

+ 11 - 1
lib/mapping/MapFeaturesH3M.cpp

@@ -27,7 +27,9 @@ MapFormatFeaturesH3M MapFormatFeaturesH3M::find(EMapFormat format)
 			return getFeaturesSOD();
 		case EMapFormat::WOG:
 			return getFeaturesWOG();
-		case EMapFormat::HOTA:
+		//case EMapFormat::HOTA1: //TODO: find such maps? Not present in current HotA release (1.6)
+		//case EMapFormat::HOTA2:
+		case EMapFormat::HOTA3:
 			return getFeaturesHOTA();
 		default:
 			throw std::runtime_error("Invalid map format!");
@@ -116,6 +118,14 @@ MapFormatFeaturesH3M MapFormatFeaturesH3M::getFeaturesHOTA()
 	MapFormatFeaturesH3M result = getFeaturesSOD();
 	result.levelHOTA = true;
 
+	result.factionsCount = 10; // + Cove
+	result.creaturesCount = 162; // + Cove + neutrals
+	result.artifactsCount = 161; // + HotA artifacts
+
+	result.heroesBytes = 24;
+	result.heroesCount = 179; // + Cove
+	result.heroesPortraitsCount = 186; // + Cove
+
 	return result;
 }
 

+ 54 - 26
lib/mapping/MapFormatH3M.cpp

@@ -154,22 +154,17 @@ void CMapLoaderH3M::readHeader()
 	features = MapFormatFeaturesH3M::find(mapHeader->version);
 	reader->setFormatLevel(mapHeader->version);
 
-	if (mapHeader->version == EMapFormat::HOTA)
+	if(mapHeader->version == EMapFormat::HOTA1 || mapHeader->version == EMapFormat::HOTA2 || mapHeader->version == EMapFormat::HOTA3)
 	{
-		//unknown values
-		std::vector<uint8_t> bytes;
-		for(int i = 0; i < 10; ++i)
-			bytes.push_back(reader->readInt8());
-		assert(bytes[0] == 3);
-		assert(bytes[1] == 0);
-		assert(bytes[2] == 0);
-		assert(bytes[3] == 0);
-		assert(bytes[4] == 0);
-		assert(bytes[5] == 0);
-		assert(bytes[6] == 12);
-		assert(bytes[7] == 0);
-		assert(bytes[8] == 0);
-		assert(bytes[9] == 0);
+		uint8_t hotaVersion = reader->readUInt8();
+
+		reader->skipZero(5);
+		if (hotaVersion == 3)
+		{
+			uint8_t unknown = reader->readUInt8();
+			logGlobal->error("%s -> header unknown: %d", mapName, int(unknown));
+			reader->skipZero(3);
+		}
 	}
 
 	// Read map name, description, dimensions,...
@@ -268,22 +263,37 @@ void CMapLoaderH3M::readPlayerInfo()
 	}
 }
 
-namespace EVictoryConditionType
+enum class EVictoryConditionType : uint8_t
 {
-	enum EVictoryConditionType { ARTIFACT, GATHERTROOP, GATHERRESOURCE, BUILDCITY, BUILDGRAIL, BEATHERO,
-		CAPTURECITY, BEATMONSTER, TAKEDWELLINGS, TAKEMINES, TRANSPORTITEM, WINSTANDARD = 255 };
-}
-
-namespace ELossConditionType
+	ARTIFACT = 0,
+	GATHERTROOP = 1,
+	GATHERRESOURCE = 2,
+	BUILDCITY = 3,
+	BUILDGRAIL = 4,
+	BEATHERO = 5,
+	CAPTURECITY = 6,
+	BEATMONSTER = 7,
+	TAKEDWELLINGS = 8,
+	TAKEMINES = 9,
+	TRANSPORTITEM = 10,
+	HOTA_ELIMINATE_ALL_MONSTERS = 11,
+	HOTA_SURVIVE_FOR_DAYS = 12,
+	WINSTANDARD = 255
+};
+
+enum class ELossConditionType : uint8_t
 {
-	enum ELossConditionType { LOSSCASTLE, LOSSHERO, TIMEEXPIRES, LOSSSTANDARD = 255 };
-}
+	LOSSCASTLE,
+	LOSSHERO,
+	TIMEEXPIRES,
+	LOSSSTANDARD = 255
+};
 
 void CMapLoaderH3M::readVictoryLossConditions()
 {
 	mapHeader->triggeredEvents.clear();
 
-	auto vicCondition = static_cast<EVictoryConditionType::EVictoryConditionType>(reader->readUInt8());
+	auto vicCondition = static_cast<EVictoryConditionType>(reader->readUInt8());
 
 	EventCondition victoryCondition(EventCondition::STANDARD_WIN);
 	EventCondition defeatCondition(EventCondition::DAYS_WITHOUT_TOWN);
@@ -464,6 +474,15 @@ void CMapLoaderH3M::readVictoryLossConditions()
 				specialVictory.trigger = EventExpression(cond);
 				break;
 			}
+		case EVictoryConditionType::HOTA_ELIMINATE_ALL_MONSTERS:
+				logGlobal->error("Map %s - victory condition 'Eliminate all monsters' is not supported!", mapName);
+				//TODO: HOTA
+				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
+				break;
 		default:
 			assert(0);
 		}
@@ -490,7 +509,7 @@ void CMapLoaderH3M::readVictoryLossConditions()
 	}
 
 	// Read loss conditions
-	auto lossCond = static_cast<ELossConditionType::ELossConditionType>(reader->readUInt8());
+	auto lossCond = static_cast<ELossConditionType>(reader->readUInt8());
 	if (lossCond == ELossConditionType::LOSSSTANDARD)
 	{
 		mapHeader->defeatIconIndex = 3;
@@ -591,7 +610,16 @@ void CMapLoaderH3M::readAllowedHeroes()
 {
 	mapHeader->allowedHeroes = VLC->heroh->getDefaultAllowed();
 
-	reader->readBitmask(mapHeader->allowedHeroes, features.heroesBytes, features.heroesCount, false);
+	uint32_t heroesCount = features.heroesCount;
+
+	if (features.levelHOTA)
+	{
+		heroesCount = reader->readUInt32();
+	}
+
+	assert(heroesCount <= features.heroesCount);
+
+	reader->readBitmask(mapHeader->allowedHeroes, features.heroesBytes, heroesCount, false);
 
 	//TODO: unknown value. Check meaning? Only present in campaign maps.
 	if(features.levelAB)