1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-06-04 23:17:41 +02:00

Implemented parsing of HotA h3m header

This commit is contained in:
Ivan Savenko 2023-04-03 18:56:49 +03:00
parent f93335d678
commit 838d45b32c
4 changed files with 76 additions and 33 deletions

View File

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

View File

@ -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::AB) :
case static_cast<int>(EMapFormat::ROE) : case static_cast<int>(EMapFormat::ROE) :
case static_cast<int>(EMapFormat::SOD) : 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())); return std::unique_ptr<IMapLoader>(new CMapLoaderH3M(mapName, modName, encoding, stream.get()));
default : default :
throw std::runtime_error("Unknown map format"); throw std::runtime_error("Unknown map format");

View File

@ -27,7 +27,9 @@ MapFormatFeaturesH3M MapFormatFeaturesH3M::find(EMapFormat format)
return getFeaturesSOD(); return getFeaturesSOD();
case EMapFormat::WOG: case EMapFormat::WOG:
return getFeaturesWOG(); 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(); return getFeaturesHOTA();
default: default:
throw std::runtime_error("Invalid map format!"); throw std::runtime_error("Invalid map format!");
@ -116,6 +118,14 @@ MapFormatFeaturesH3M MapFormatFeaturesH3M::getFeaturesHOTA()
MapFormatFeaturesH3M result = getFeaturesSOD(); MapFormatFeaturesH3M result = getFeaturesSOD();
result.levelHOTA = true; 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; return result;
} }

View File

@ -154,22 +154,17 @@ void CMapLoaderH3M::readHeader()
features = MapFormatFeaturesH3M::find(mapHeader->version); features = MapFormatFeaturesH3M::find(mapHeader->version);
reader->setFormatLevel(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 uint8_t hotaVersion = reader->readUInt8();
std::vector<uint8_t> bytes;
for(int i = 0; i < 10; ++i) reader->skipZero(5);
bytes.push_back(reader->readInt8()); if (hotaVersion == 3)
assert(bytes[0] == 3); {
assert(bytes[1] == 0); uint8_t unknown = reader->readUInt8();
assert(bytes[2] == 0); logGlobal->error("%s -> header unknown: %d", mapName, int(unknown));
assert(bytes[3] == 0); reader->skipZero(3);
assert(bytes[4] == 0); }
assert(bytes[5] == 0);
assert(bytes[6] == 12);
assert(bytes[7] == 0);
assert(bytes[8] == 0);
assert(bytes[9] == 0);
} }
// Read map name, description, dimensions,... // 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, ARTIFACT = 0,
CAPTURECITY, BEATMONSTER, TAKEDWELLINGS, TAKEMINES, TRANSPORTITEM, WINSTANDARD = 255 }; 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
};
namespace ELossConditionType enum class ELossConditionType : uint8_t
{ {
enum ELossConditionType { LOSSCASTLE, LOSSHERO, TIMEEXPIRES, LOSSSTANDARD = 255 }; LOSSCASTLE,
} LOSSHERO,
TIMEEXPIRES,
LOSSSTANDARD = 255
};
void CMapLoaderH3M::readVictoryLossConditions() void CMapLoaderH3M::readVictoryLossConditions()
{ {
mapHeader->triggeredEvents.clear(); mapHeader->triggeredEvents.clear();
auto vicCondition = static_cast<EVictoryConditionType::EVictoryConditionType>(reader->readUInt8()); auto vicCondition = static_cast<EVictoryConditionType>(reader->readUInt8());
EventCondition victoryCondition(EventCondition::STANDARD_WIN); EventCondition victoryCondition(EventCondition::STANDARD_WIN);
EventCondition defeatCondition(EventCondition::DAYS_WITHOUT_TOWN); EventCondition defeatCondition(EventCondition::DAYS_WITHOUT_TOWN);
@ -464,6 +474,15 @@ void CMapLoaderH3M::readVictoryLossConditions()
specialVictory.trigger = EventExpression(cond); specialVictory.trigger = EventExpression(cond);
break; 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: default:
assert(0); assert(0);
} }
@ -490,7 +509,7 @@ void CMapLoaderH3M::readVictoryLossConditions()
} }
// Read loss conditions // Read loss conditions
auto lossCond = static_cast<ELossConditionType::ELossConditionType>(reader->readUInt8()); auto lossCond = static_cast<ELossConditionType>(reader->readUInt8());
if (lossCond == ELossConditionType::LOSSSTANDARD) if (lossCond == ELossConditionType::LOSSSTANDARD)
{ {
mapHeader->defeatIconIndex = 3; mapHeader->defeatIconIndex = 3;
@ -591,7 +610,16 @@ void CMapLoaderH3M::readAllowedHeroes()
{ {
mapHeader->allowedHeroes = VLC->heroh->getDefaultAllowed(); 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. //TODO: unknown value. Check meaning? Only present in campaign maps.
if(features.levelAB) if(features.levelAB)