heroes3-mapreader/h3m.py

803 lines
32 KiB
Python
Raw Permalink Normal View History

2014-06-26 07:29:46 +00:00
# -*- coding: utf-8 -*-
"""
copyright 2008 - Johannes 'josch' Schauer <j.schauer@email.de>
copyright 2010 - Remigiusz 'Enleth' Marcinkiewicz <enleth@enleth.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
import gzip
import struct
import chardet
import binascii
#from guess_language import guessLanguage
from lxml import etree
from lxml import objectify
from bf import bf
from h3m_ids import *
def edet(string):
e = chardet.detect(string)
if e["encoding"] in h3m_encoding_quirks:
e["encoding"] = h3m_encoding_quirks[e["encoding"]]
return e
def extract(filename):
h3m_data = gzip.open(filename)
r = lambda s: struct.unpack(s,h3m_data.read(struct.calcsize(s)))
E = objectify.ElementMaker(annotate=False)
try:
vver = h3m_versions[r("<I")[0]]
except KeyError:
raise ValueError("Invalid game version or not a HoMM 3 map")
if vver == "WoG":
raise NotImplementedError("WoG maps not supported yet")
(vhas_players, vsize, vunderground) = r("<BIB");
if vunderground not in [0,1]:
raise ValueError("Underground specifier not in [0,1]")
vunderground = str(bool(vunderground))
m = E.map(version=vver,size=str(vsize),underground=vunderground)
m.append(E.conversion_metadata())
(vlength, ) = r("<I")
if vlength>30:
raise ValueError("Map name longer than 30 characters, possibly an invalid file")
vname = h3m_data.read(vlength)
enc = edet(vname)
vname = vname.decode(enc["encoding"])
m.append(E.name(vname))
m.conversion_metadata.append(E.encoding(enc["encoding"],entity="/map/name[0]",confidence=str(enc["confidence"])))
(vlength, ) = r("<I")
vdesc = h3m_data.read(vlength)
enc = edet(vdesc)
vdesc = vdesc.decode(enc["encoding"])
m.append(E.description(vdesc))
m.conversion_metadata.append(E.encoding(enc["encoding"],entity="/map/description[0]",confidence=str(enc["confidence"])))
try:
vdifficulty = h3m_diffictulties[r("<B")[0]]
except KeyError:
raise ValueError("Invalid map difficulty value")
m.append(E.difficulty(vdifficulty))
if m.xpath("/map/@version")[0] in ["AB", "SoD", "WoG"]:
(vmax_level,) = r("<B")
if vmax_level==0:
vmax_level = "Unspecified"
else:
vmax_level = "Unspecified"
m.append(E.max_level(vmax_level))
m.append(E.players())
#player info
for pnum in h3m_player_colors:
(vis_human, vis_computer) = r("<BB")
if vis_human not in [0,1] or vis_computer not in [0,1]:
raise ValueError("Invalid player type specifiers %#04x and %#04x" % (vis_human, vis_computer))
if vis_human == 0 and vis_computer == 0:
#player is disabled, skipping garbage
if m.xpath("/map/@version")[0] == "RoE":
h3m_data.read(6)
elif m.xpath("/map/@version")[0] == "AB":
h3m_data.read(12)
elif m.xpath("/map/@version")[0] in ["SoD", "WoG"]:
h3m_data.read(13)
m.players.append(E.player(color=h3m_player_colors[pnum],type="Disabled"))
continue
if vis_human == 1 and vis_computer == 1:
player_type = "Both"
elif vis_human == 1:
player_type = "Human"
else:
player_type = "Computer"
player = E.player(color=h3m_player_colors[pnum],type=player_type)
try:
vai_type = h3m_ai_types[r("<B")[0]]
except KeyError:
raise ValueError("Invalid AI type")
player.append(E.ai(vai_type))
if m.xpath("/map/@version")[0] in ["SoD", "WoG"]:
(venforce_alignment,) = r("<B")
if venforce_alignment not in [0,1]:
raise ValueError("Invalid alignment enforcement specifier %#04x" % venforce_alignment)
venforce_alignment = str(bool(venforce_alignment))
else:
venforce_alignment = "Unspecified"
valignments = bf(r("<B")[0])
if m.xpath("/map/@version")[0] in ["AB", "SoD", "WoG"]:
(vconflux,) = r("<B")
if vconflux not in [0,1]:
raise ValueError("Invalid Conflux specifier %#04x" % vconflux)
if vconflux == 1:
valignments[8] = 1
(vrandom_alignment,) = r("<B")
if vrandom_alignment not in [0,1]:
raise ValueError("Invalid random alignment specifier %#04x" % vrandom_alignment)
if vrandom_alignment == 1:
valignments[9] = 1
alignments = set()
for i in range(0,10):
if valignments[i] == 1:
alignments.add(h3m_alignments[i+1])
player.append(E.alignment(', '.join(alignments),enforced=venforce_alignment))
(vmain_town,) = r("<B")
if vmain_town not in [0,1]:
raise ValueError("Invalid main town specifier %#04x" % vmain_town)
if vmain_town:
if m.xpath("/map/@version")[0] in ["AB", "SoD", "WoG"]:
(vmain_town_generate_hero, vmain_town_unknown1) = r("<BB")
vmain_town_unknown1 = ("%#04x" % vmain_town_unknown1)
m.conversion_metadata.append(E.unknown1(vmain_town_unknown1,player_color=h3m_player_colors[pnum]))
if vmain_town_generate_hero not in [0,1]:
raise ValueError("Invalid main town hero generation specifier %#04x" % vmain_town_generate_hero)
vmain_town_generate_hero = str(bool(vmain_town_generate_hero))
else:
vmain_town_generate_hero = "Unspecified"
vmain_town_unknown1 = "Unspecified"
vcoords = r("<BBB")
player.append(E.main_town("%d,%d,%d" % vcoords, generate_hero=vmain_town_generate_hero, unknown1=vmain_town_unknown1))
(vgenerate_random_hero, vmain_hero) = r("<BB");
if vgenerate_random_hero not in [0,1]:
raise ValueError("Invalid random hero generation specifier %#04x" % vgenerate_random_hero)
vgenerate_random_hero = str(bool(vgenerate_random_hero))
player.append(E.generate_random_hero(vgenerate_random_hero))
if vmain_hero != 0xFF:
(vmain_hero_portrait, ) = r("<B")
if vmain_hero_portrait == 0xFF:
vmain_hero_portrait = "Standard"
else:
vmain_hero_portrait = str(vmain_hero_portrait)
(vlength, ) = r("<I")
vmain_hero_name = h3m_data.read(vlength)
player.append(E.main_hero(str(vmain_hero),portrait=vmain_hero_portrait,name=vmain_hero_name))
if m.xpath("/map/@version")[0] in ["AB", "SoD", "WoG"]:
(vhero_placeholders,vheroes_count) = r("<BI")
player.append(E.hero_placeholders(vhero_placeholders));
if vheroes_count > 0:
player.append(E.heroes())
for i in range(vheroes_count):
(vhero_portrait, ) = r("<B")
if vhero_portrait == 0xFF:
vhero_portrait = "Standard"
else:
vhero_portrait = str(vhero_portrait)
(vlength, ) = r("<I")
vhero_name = h3m_data.read(vlength)
player.heroes.append(E.hero(portrait=vhero_portrait,name=vhero_name))
m.players.append(player)
#special victory condition
(vvictory_condition,) = r("<B")
try:
vvictory_condition = h3m_victory_conditions[vvictory_condition]
except KeyError:
raise ValueError("Invalid special victory condition %#04x" % vvictory_condition)
if vvictory_condition != "None":
(vvictory_allow_normal, vvictory_apply_to_computer) = r("<BB")
if vvictory_allow_normal not in [0,1]:
raise ValueError("Invalid normal victory specifier %#04x" % vvictory_allow_normal)
if vvictory_apply_to_computer not in [0,1]:
raise ValueError("Invalid special victory for computer specifier %#04x" % vvictory_apply_to_computer)
vvictory_allow_normal = str(bool(vvictory_allow_normal))
vvictory_apply_to_computer = str(bool(vvictory_apply_to_computer))
m.append(E.special_victory_condition(type=vvictory_condition, allow_normal=vvictory_allow_normal, apply_to_computer=vvictory_apply_to_computer))
if vvictory_condition == "Acquire artifact":
(vvictory_artifact_id,) = r("<B")
try:
vvictory_artifact_id = h3m_artifact_types[vvictory_artifact_id]
except KeyError:
raise ValueError("Invalid special victory artifact id %#04x" % vvictory_artifact_id)
m.special_victory_condition.append(E.artifact(vvictory_artifact_id))
if m.xpath("/map/@version")[0] in ["AB", "SoD", "WoG"]:
(vvictory_artifact_unknown2,) = r("<B")
vvictory_artifact_unknown2 = ("%#04x" % vvictory_artifact_unknown2)
m.special_victory_condition.append(E.unknown2(vvictory_artifact_unknown2))
m.conversion_metadata.append(E.unknown2(vvictory_artifact_unknown2))
elif vvictory_condition == "Accumulate creatures":
(vvictory_creature_id,vvictory_creature_quantity) = r("<BI")
try:
vvictory_creature_id = h3m_creature_types[vvictory_creature_id]
except KeyError:
raise ValueError("Invalid special victory creature id %#04x" % vvictory_creature_id)
m.special_victory_condition.append(E.creature(vvictory_creature_id,quantity=str(vvictory_creature_quantity)))
elif vvictory_condition == "Accumulate resources":
(vvictory_resource_type, vvictory_resource_quantity) = r("<BI")
try:
vvictory_resource_type = h3m_resource_types[vvictory_resource_type]
except KeyError:
raise ValueError("Invalid special victory resource type %#04x" % vvictory_resource_type)
m.special_victory_condition.append(E.resource(vvictory_resource_type,quantity=str(vvictory_resource_quantity)))
elif vvictory_condition == "Upgrade town":
raise NotImplementedError
elif vvictory_condition in ("Build Grail", "Defeat hero", "Capture town", "Defeat monster"):
vcoords = r("<BBB")
m.special_victory_condition.append(E.location("%d,%d,%d" % vcoords))
elif vvictory_condition in ("Flag all dwellings", "Flag all mines"):
pass
elif vvictory_condition == "Transport artifact":
raise NotImplementedError
else:
raise NotImplementedError
#special loss condition
(vloss_condition,) = r("<B")
try:
vloss_condition = h3m_loss_conditions[vloss_condition]
except KeyError:
raise ValueError("Invalid special loss condition %#04x" % vloss_condition)
if vloss_condition != "None":
m.append(E.special_loss_condition(type=vloss_condition))
if vloss_condition in ("Lose town", "Lose hero"):
vcoords = r("<BBB")
m.special_loss_condition.append(E.location("%d,%d,%d" % vcoords))
elif vloss_condition == "Time limit":
(vloss_days,) = r("<H")
m.special_loss_condition.append(E.days(str(vloss_days)))
else:
raise NotImplementedError
#Teams
(vteam_count, ) = r("<B")
if vteam_count > 0:
#print dict(zip(range(0,8),r("<8B")))
vteams = r("<8B")
for pnum in range(0,8):
if m.xpath("/map/players/player[@color=$color]/@type",color=h3m_player_colors[pnum])[0] != "Disabled":
m.xpath("/map/players/player[@color=$color]",color=h3m_player_colors[pnum])[0].append(E.team(str(vteams[pnum])))
#guess_str = m.xpath("/map/name")[0].text + ' ' + m.xpath("/map/description")[0].text
#print guessLanguage(guess_str)
if m.xpath("/map/@version")[0] == "RoE":
vavailable_heroes = r("<16s")[0]
else:
vavailable_heroes = r("<20s")[0]
vunknown3 = "0x"+binascii.hexlify(r("<4s")[0])
m.conversion_metadata.append(E.unknown3(vunknown3))
if m.xpath("/map/@version")[0] in ["SoD", "WoG"]:
(vdisposed_heroes,) = r("<B")
if vdisposed_heroes != 0:
raise NotImplementedError("Disposed heroes not implemented")
vunknown4 = "0x"+binascii.hexlify(r("<31s")[0])
m.conversion_metadata.append(E.unknown4(vunknown4))
if m.xpath("/map/@version")[0] == "AB":
vavailable_artifacts = r("<17s")[0]
elif m.xpath("/map/@version")[0] in ["SoD", "WoG"]:
vavailable_artifacts = r("<18s")[0]
if m.xpath("/map/@version")[0] in ["SoD", "WoG"]:
vavailable_spells = r("<9s")[0]
if m.xpath("/map/@version")[0] in ["SoD", "WoG"]:
vavailable_skills = r("<4s")[0]
(vrumors_count,) = r("<B");
vunknown5 = "0x"+binascii.hexlify(r("<3s")[0])
m.conversion_metadata.append(E.unknown5(vunknown5))
for rnum in range(vrumors_count):
(vlength, ) = r("<I")
vrumor_name = h3m_data.read(vlength)
enc = edet(vrumor_name)
vrumor_name = vrumor_name.decode(enc["encoding"])
#m.conversion_metadata.append(E.encoding(enc["encoding"],entity="/map/rumor[@name='']",confidence=str(enc["confidence"])))
(vlength, ) = r("<I")
vrumor_text = h3m_data.read(vlength)
enc = edet(vrumor_text)
vrumor_text = vrumor_text.decode(enc["encoding"])
m.append(E.rumor(vrumor_text,name=vrumor_name))
#m.conversion_metadata.append(E.encoding(enc["encoding"],entity="/map/description[0]",confidence=str(enc["confidence"])))
#(map_data["heroes_count"], ) = struct.unpack("<B", h3m_data.read(1))
#if map_data["heroes_count"] > 0:
#map_data["free_heroes"] = []
#for i in range(map_data["heroes_count"]):
#(hero_id, ) = struct.unpack("<B", h3m_data.read(1))
#(hero_portrait, ) = struct.unpack("<B", h3m_data.read(1))
#(name_length, ) = struct.unpack("<I", h3m_data.read(4))
#hero_name = h3m_data.read(name_length)
#(hero_players, ) = struct.unpack("<B", h3m_data.read(1))
#map_data["free_heroes"].append({"id": hero_id, "portrait":hero_portrait, "name":hero_name, "players":hero_players})
#hero options
#for i in range(156):
#(hero_enable, ) = struct.unpack("<B", h3m_data.read(1))
#if hero_enable == 1:
#(isExp, ) = struct.unpack("<B", h3m_data.read(1))
#if isExp == 0x01:
#(exp, ) = struct.unpack("<I", h3m_data.read(4))
#(isSecSkill, ) = struct.unpack("<B", h3m_data.read(1))
#if isSecSkill == 0x01:
#(skills_count, ) = struct.unpack("<I", h3m_data.read(4))
#for i in range(skills_count):
#(skill_id, ) = struct.unpack("<B", h3m_data.read(1))
#(skill_lvl, ) = struct.unpack("<B", h3m_data.read(1))
#(isArtifact, ) = struct.unpack("<B", h3m_data.read(1))
#if isArtifact == 0x01:
#raise NotImplementedError
#(isBiography, ) = struct.unpack("<B", h3m_data.read(1))
#if isBiography == 0x01:
#(length, ) = struct.unpack("<I", h3m_data.read(4))
#biography = h3m_data.read(length)
#(gender, ) = struct.unpack("<B", h3m_data.read(1))
#(isSpells, ) = struct.unpack("<B", h3m_data.read(1))
#if isSpells == 0x01:
#spells = struct.unpack("<9B", h3m_data.read(9))
#(isPrimarySkills, ) = struct.unpack("<B", h3m_data.read(1))
#if isPrimarySkills == 0x01:
#(attack, defense, power, knowledge) = struct.unpack("<4B", h3m_data.read(4))
if m.xpath("/map/@version")[0] in ["SoD", "WoG"]:
vunknown6 = "0x"+binascii.hexlify(r("<156s")[0])
m.conversion_metadata.append(E.unknown6(vunknown6))
map_size = int(m.xpath("/map/@size")[0])
upper_terrain = [[] for i in range(map_size)]
#read upper world
for i in range(map_size**2):
x = i%map_size
y = (i-x)/map_size
upper_terrain[y].append(r("<7B"))
#read underworld
if m.xpath("/map/@underground")[0] == "True":
lower_terrain = [[] for i in range(map_size)]
for i in range(map_size**2):
x = i%map_size
y = (i-x)/map_size
lower_terrain[y].append(r("<7B"))
(vdef_count, ) = r("<I")
print vdef_count
defs = []
for i in range(vdef_count):
(vlength, ) = r("<I")
vdef_filename = h3m_data.read(vlength)
#the biggest object is 8x6 tiles big - this is an array that maps
#passability to each of this tiles with bits 1 passable, 0 not passable
#we store a set with all not passable tiles
print vdef_filename
vdef_passability = r("<6B") #passability
vdef_passability = frozenset([(8-x,6-y) for x in range(8)
for y, p in enumerate(vdef_passability)
if not (p >> x) & 1])
vdef_accessibility = r("<6B") #passability
vdef_accessibility = frozenset([(8-x,6-y) for x in range(8)
for y, p in enumerate(vdef_accessibility)
if not (p >> x) & 1])
h3m_data.read(2) #landscape
h3m_data.read(2) #land_edit_groups
(vdef_type,vdef_subtype,vdef_groups,vdef_underlay ) = r("<IIBB")
h3m_data.read(16) #junk
return m
(vobj_count, ) = r("<I")
map_data["tunedobj"] = []
for i in range(map_data["tunedobj_count"]):
(x, y, z) = struct.unpack("<3B", h3m_data.read(3))
(object_id, ) = struct.unpack("<I", h3m_data.read(4))
#print x,y,z,object_id,
junk = h3m_data.read(5) #junk
#if junk != "\x00\x00\x00\x00\x00":
# for c in junk:
# print("%02d"%ord(c), end=' ')
# break
#print i, map_data["objects"][object_id]["filename"],
#print map_data["objects"][object_id]["class"]
map_data["tunedobj"].append({"id":object_id, "x":x, "y":y, "z":z})
if object_id in (0,1):
pass
elif map_data["objects"][object_id]["class"] == 53:
if map_data["objects"][object_id]["number"] == 7:
h3m_data.read(4)
else:
h3m_data.read(4)
elif map_data["objects"][object_id]["class"] in (76, 79):
(isText, ) = struct.unpack("<B", h3m_data.read(1))
if isText == 0x01:
(length, ) = struct.unpack("<I", h3m_data.read(4))
text = h3m_data.read(length)
(isGuards, ) = struct.unpack("<B", h3m_data.read(1))
if isGuards == 0x01:
for i in range(7):
(guard_id, guard_count) = struct.unpack("<HH", h3m_data.read(4))
h3m_data.read(4) #junk
(quantity, ) = struct.unpack("<I", h3m_data.read(4))
h3m_data.read(4) #junk
elif map_data["objects"][object_id]["class"] in (34, 70, 62):
(hero_id, color, hero) = struct.unpack("<IBB", h3m_data.read(6))
(isName, ) = struct.unpack("<B", h3m_data.read(1))
if isName == 0x01:
(length, ) = struct.unpack("<I", h3m_data.read(4))
name = h3m_data.read(length)
(isExp, ) = struct.unpack("<B", h3m_data.read(1))
if isExp == 0x01:
(exp, ) = struct.unpack("<I", h3m_data.read(4))
(isPortrait, ) = struct.unpack("<B", h3m_data.read(1))
if isPortrait == 0x01:
(portrait, ) = struct.unpack("<B", h3m_data.read(1))
(isSecSkill, ) = struct.unpack("<B", h3m_data.read(1))
if isSecSkill == 0x01:
(skills_count, ) = struct.unpack("<I", h3m_data.read(4))
for i in range(skills_count):
(skill_id, ) = struct.unpack("<B", h3m_data.read(1))
(skill_lvl, ) = struct.unpack("<B", h3m_data.read(1))
(isCreature, ) = struct.unpack("<B", h3m_data.read(1))
if isCreature == 0x01:
for i in range(7):
(guard_id, ) = struct.unpack("<H", h3m_data.read(2))
(guard_count, ) = struct.unpack("<H", h3m_data.read(2))
(creaturesFormation, ) = struct.unpack("<B", h3m_data.read(1))
(isArtifact, ) = struct.unpack("<B", h3m_data.read(1))
if isArtifact == 0x01:
(headID, shouldersID, neckID, rightHandID, leftHandID,
trunkID, rightRingID, leftRingID, legsID, misc1ID, misc2ID,
misc3ID, misc4ID, machine1ID, machine2ID, machine3ID,
machine4ID, magicbook, misc5ID) \
= struct.unpack("<19H", h3m_data.read(38))
(knapsack_count, ) = struct.unpack("<H", h3m_data.read(2))
if knapsack_count > 0:
for i in range(knapsack_count):
(knapsackID, ) = struct.unpack("<H", h3m_data.read(2))
(zoneRadius, ) = struct.unpack("<B", h3m_data.read(1))
(isBiography, ) = struct.unpack("<B", h3m_data.read(1))
if isBiography == 0x01:
(length, ) = struct.unpack("<I", h3m_data.read(4))
biography = h3m_data.read(length)
(gender, ) = struct.unpack("<B", h3m_data.read(1))
(isSpells, ) = struct.unpack("<B", h3m_data.read(1))
if isSpells == 0x01:
spells = struct.unpack("<9B", h3m_data.read(9))
(isPrimarySkills, ) = struct.unpack("<B", h3m_data.read(1))
if isPrimarySkills == 0x01:
(attack, defense, power, knowledge) = struct.unpack("<4B", h3m_data.read(4))
h3m_data.read(16) #unknown
elif map_data["objects"][object_id]["class"] in (17, 20, 42):
(owner, ) = struct.unpack("<I", h3m_data.read(4))
elif map_data["objects"][object_id]["class"] == 93:
(isText, ) = struct.unpack("<B", h3m_data.read(1))
if isText == 0x01:
(length, ) = struct.unpack("<I", h3m_data.read(4))
text = h3m_data.read(length)
(isGuards, ) = struct.unpack("<B", h3m_data.read(1))
if isGuards == 0x01:
for i in range(7):
(guard_id, guard_count) = struct.unpack("<HH", h3m_data.read(4))
h3m_data.read(4) #junk
(spell_id, ) = struct.unpack("<I", h3m_data.read(4))
elif map_data["objects"][object_id]["class"] == 216:
(owner, ) = struct.unpack("<I", h3m_data.read(4))
(junk, ) = struct.unpack("<I", h3m_data.read(4))
if junk == 0x00:
(towns, ) = struct.unpack("<H", h3m_data.read(2))
(minlevel, maxlevel, ) = struct.unpack("<2B", h3m_data.read(2))
elif map_data["objects"][object_id]["class"] in (54, 71, 72, 73, 74, 75, 162, 163, 164):
(monster_id, ) = struct.unpack("<I", h3m_data.read(4))
(monster_count, ) = struct.unpack("<H", h3m_data.read(2))
(mood, ) = struct.unpack("<B", h3m_data.read(1))
(isTreasureOrText, ) = struct.unpack("<B", h3m_data.read(1))
if isTreasureOrText == 0x01:
(length, ) = struct.unpack("<I", h3m_data.read(4))
text = h3m_data.read(length)
(wood, mercury, ore, sulfur, crystal, gem, gold, artefactID) \
= struct.unpack("<7IH", h3m_data.read(30))
(mosterNeverRunAway, ) = struct.unpack("<B", h3m_data.read(1))
(monsterDontGrowUp, ) = struct.unpack("<B", h3m_data.read(1))
h3m_data.read(2) #junk
elif map_data["objects"][object_id]["class"] in (98, 77):
h3m_data.read(4) #junk
(owner, ) = struct.unpack("<B", h3m_data.read(1))
(isName, ) = struct.unpack("<B", h3m_data.read(1))
if isName == 0x01:
(length, ) = struct.unpack("<I", h3m_data.read(4))
name = h3m_data.read(length)
(isGuard, ) = struct.unpack("<B", h3m_data.read(1))
if isGuard == 0x01:
for i in range(7):
(guard_id, guard_count) = struct.unpack("<HH", h3m_data.read(4))
(formation, ) = struct.unpack("<B", h3m_data.read(1))
(isBuildings, ) = struct.unpack("<B", h3m_data.read(1))
if isBuildings == 0x01:
build = struct.unpack("6B", h3m_data.read(6))
active = struct.unpack("6B", h3m_data.read(6))
else:
(isFort, ) = struct.unpack("<B", h3m_data.read(1))
mustSpells = struct.unpack("<9B", h3m_data.read(9))
canSpells = struct.unpack("<9B", h3m_data.read(9))
(eventQuantity, ) = struct.unpack("<I", h3m_data.read(4))
if eventQuantity > 0:
for i in range(eventQuantity):
(length, ) = struct.unpack("<I", h3m_data.read(4))
event_name = h3m_data.read(length)
(length, ) = struct.unpack("<I", h3m_data.read(4))
event_text = h3m_data.read(length)
(wood, mercury, ore, sulfur, crystal, gem, gold,
players_affected, human_affected, ai_affected,
day_of_first_event, event_iteration) \
= struct.unpack("<7I3B2H", h3m_data.read(35))
h3m_data.read(16) #junk
buildings = struct.unpack("<6B", h3m_data.read(6))
creatures = struct.unpack("<7H", h3m_data.read(14))
h3m_data.read(4) #junk
h3m_data.read(4) #junk
elif map_data["objects"][object_id]["class"] in (5, 65, 66, 67, 68, 69):
(isText, ) = struct.unpack("<B", h3m_data.read(1))
if isText == 0x01:
(length, ) = struct.unpack("<I", h3m_data.read(4))
text = h3m_data.read(length)
(isGuards, ) = struct.unpack("<B", h3m_data.read(1))
if isGuards == 0x01:
for i in range(7):
(guard_id, guard_count) = struct.unpack("<HH", h3m_data.read(4))
h3m_data.read(4) #junk
elif map_data["objects"][object_id]["class"] in (33, 219):
(color, ) = struct.unpack("<I", h3m_data.read(4))
for i in range(7):
(guard_id, guard_count) = struct.unpack("<HH", h3m_data.read(4))
(undeleteSoldiers, ) = struct.unpack("<B", h3m_data.read(1))
h3m_data.read(8)
elif map_data["objects"][object_id]["class"] == 87:
(owner, ) = struct.unpack("<I", h3m_data.read(4))
elif map_data["objects"][object_id]["class"] == 83:
(quest, ) = struct.unpack("<B", h3m_data.read(1))
if quest == 0x00:
pass
elif quest == 0x01:
(level, ) = struct.unpack("<I", h3m_data.read(4))
elif quest == 0x02:
(offence, defence, power, knowledge) = struct.unpack("4B", h3m_data.read(4))
elif quest == 0x03:
(hero_id, ) = struct.unpack("<I", h3m_data.read(4))
elif quest == 0x04:
(monster_id, ) = struct.unpack("<I", h3m_data.read(4))
elif quest == 0x05:
(art_quantity, ) = struct.unpack("<B", h3m_data.read(1))
for i in range(art_quantity):
(art, ) = struct.unpack("<H", h3m_data.read(2))
elif quest == 0x06:
(creatures_quantity, ) = struct.unpack("<B", h3m_data.read(1))
for i in range(creatures_quantity):
(guard_id, ) = struct.unpack("<H", h3m_data.read(2))
(guard_count, ) = struct.unpack("<H", h3m_data.read(2))
elif quest == 0x07:
resources = struct.unpack("7I", h3m_data.read(28))
elif quest == 0x08:
(hero_id, ) = struct.unpack("<B", h3m_data.read(1))
elif quest == 0x09:
(player, ) = struct.unpack("<B", h3m_data.read(1))
else:
raise NotImplementedError
if quest:
(time_limit, ) = struct.unpack("<I", h3m_data.read(4))
(length, ) = struct.unpack("<I", h3m_data.read(4))
quest_begin = h3m_data.read(length)
(length, ) = struct.unpack("<I", h3m_data.read(4))
quest_running = h3m_data.read(length)
(length, ) = struct.unpack("<I", h3m_data.read(4))
quest_end = h3m_data.read(length)
(reward, ) = struct.unpack("<B", h3m_data.read(1))
if reward == 0x00:
pass
elif reward == 0x01:
(exp, ) = struct.unpack("<I", h3m_data.read(4))
elif reward == 0x02:
(spell_points, ) = struct.unpack("<I", h3m_data.read(4))
elif reward == 0x03:
(morale, ) = struct.unpack("<B", h3m_data.read(1))
elif reward == 0x04:
(lucky, ) = struct.unpack("<B", h3m_data.read(1))
elif reward == 0x05:
(resID, ) = struct.unpack("<B", h3m_data.read(1))
(res_quantity, ) = struct.unpack("<I", h3m_data.read(4))
elif reward == 0x06:
(priSkillID, ) = struct.unpack("<B", h3m_data.read(1))
(priSkillBonus, ) = struct.unpack("<B", h3m_data.read(1))
elif reward == 0x07:
(secSkillID, ) = struct.unpack("<B", h3m_data.read(1))
(secSkillBonus, ) = struct.unpack("<B", h3m_data.read(1))
elif reward == 0x08:
(artID, ) = struct.unpack("<H", h3m_data.read(2))
elif reward == 0x09:
(spellID, ) = struct.unpack("<B", h3m_data.read(1))
elif reward == 0x0A:
(creatureID, ) = struct.unpack("<H", h3m_data.read(2))
(creatureQuantity, ) = struct.unpack("<H", h3m_data.read(2))
else:
raise NotImplementedError
h3m_data.read(2) #junk
elif map_data["objects"][object_id]["class"] in (91, 59):
(length, ) = struct.unpack("<I", h3m_data.read(4))
text = h3m_data.read(length)
h3m_data.read(4) #junk
elif map_data["objects"][object_id]["class"] == 113:
(secSkills, ) = struct.unpack("<I", h3m_data.read(4))
elif map_data["objects"][object_id]["class"] in (88, 89, 90):
(spellID, ) = struct.unpack("<I", h3m_data.read(4))
elif map_data["objects"][object_id]["class"] == 215:
(quest, ) = struct.unpack("<B", h3m_data.read(1))
if quest == 0x00:
pass
elif quest == 0x01:
(level, ) = struct.unpack("<I", h3m_data.read(4))
elif quest == 0x02:
(offence, defence, power, knowledge) = struct.unpack("4B", h3m_data.read(4))
elif quest == 0x03:
(hero_id, ) = struct.unpack("<I", h3m_data.read(4))
elif quest == 0x04:
(monster_id, ) = struct.unpack("<I", h3m_data.read(4))
elif quest == 0x05:
(art_quantity, ) = struct.unpack("<B", h3m_data.read(1))
for i in range(art_quantity):
(art, ) = struct.unpack("<H", h3m_data.read(2))
elif quest == 0x06:
(creatures_quantity, ) = struct.unpack("<B", h3m_data.read(1))
for i in range(creatures_quantity):
(guard_id, ) = struct.unpack("<H", h3m_data.read(2))
(guard_count, ) = struct.unpack("<H", h3m_data.read(2))
elif quest == 0x07:
resources = struct.unpack("7I", h3m_data.read(28))
elif quest == 0x08:
(hero_id, ) = struct.unpack("<B", h3m_data.read(1))
elif quest == 0x09:
(player, ) = struct.unpack("<B", h3m_data.read(1))
else:
raise NotImplementedError
if quest:
(time_limit, ) = struct.unpack("<I", h3m_data.read(4))
(length, ) = struct.unpack("<I", h3m_data.read(4))
quest_begin = h3m_data.read(length)
(length, ) = struct.unpack("<I", h3m_data.read(4))
quest_running = h3m_data.read(length)
(length, ) = struct.unpack("<I", h3m_data.read(4))
quest_end = h3m_data.read(length)
elif map_data["objects"][object_id]["class"] == 36:
(radius, ) = struct.unpack("<B", h3m_data.read(1))
h3m_data.read(3) #junk
elif map_data["objects"][object_id]["class"] == 220:
(resources, ) = struct.unpack("<B", h3m_data.read(1))
h3m_data.read(3) #junk
elif map_data["objects"][object_id]["class"] == 217:
(owner, towns) = struct.unpack("<II", h3m_data.read(8))
if towns==0x00:
(towns,) = struct.unpack("<H", h3m_data.read(2))
elif map_data["objects"][object_id]["class"] == 218:
(owner, minlvl, maxlvl) = struct.unpack("<IBB", h3m_data.read(6))
elif map_data["objects"][object_id]["class"] == 81:
(bonus_type, primaryID) = struct.unpack("<BI", h3m_data.read(5))
h3m_data.read(3) #junk
elif map_data["objects"][object_id]["class"] == 6:
(isText, ) = struct.unpack("<B", h3m_data.read(1))
if isText == 0x01:
(length, ) = struct.unpack("<I", h3m_data.read(4))
text = h3m_data.read(length)
(isGuards, ) = struct.unpack("<B", h3m_data.read(1))
if isGuards == 0x01:
for i in range(7):
(guard_id, guard_count) = struct.unpack("<HH", h3m_data.read(4))
h3m_data.read(4) #junk
(exp, spell_points, morals, luck, wood, mercury, ore, sulfur,
crystal, gem, gold, offence, defence, power, knowledge) = \
struct.unpack("<IIBBIIIIIIIBBBB", h3m_data.read(42))
(secSkills, ) = struct.unpack("<B", h3m_data.read(1))
if secSkills > 0:
for i in range(secSkills):
(skill_id, skill_lvl) = struct.unpack("<BB", h3m_data.read(2))
(artefacts, ) = struct.unpack("<B", h3m_data.read(1))
if artefacts > 0:
for i in range(artefacts):
(artID, ) = struct.unpack("<H", h3m_data.read(2))
(spells, ) = struct.unpack("<B", h3m_data.read(1))
if spells > 0:
for i in range(spells):
(spellID, ) = struct.unpack("<B", h3m_data.read(1))
(monsters, ) = struct.unpack("<B", h3m_data.read(1))
if monsters > 0:
for i in range(monsters):
(guard_id, guard_count) = struct.unpack("<HH", h3m_data.read(4))
h3m_data.read(8) #junk
elif map_data["objects"][object_id]["class"] == 26:
(isText, ) = struct.unpack("<B", h3m_data.read(1))
if isText == 0x01:
(length, ) = struct.unpack("<I", h3m_data.read(4))
text = h3m_data.read(length)
(isGuards, ) = struct.unpack("<B", h3m_data.read(1))
if isGuards == 0x01:
for i in range(7):
(guard_id, guard_count) = struct.unpack("<HH", h3m_data.read(4))
h3m_data.read(4) #junk
(exp, spell_points, morals, luck, wood, mercury, ore, sulfur,
crystal, gem, gold, offence, defence, power, knowledge) = \
struct.unpack("<IIBBIIIIIIIBBBB", h3m_data.read(42))
(secSkills, ) = struct.unpack("<B", h3m_data.read(1))
if secSkills > 0:
for i in range(secSkills):
(skill_id, skill_lvl) = struct.unpack("<BB", h3m_data.read(2))
(artefacts, ) = struct.unpack("<B", h3m_data.read(1))
if artefacts > 0:
for i in range(artefacts):
(artID, ) = struct.unpack("<H", h3m_data.read(2))
(spells, ) = struct.unpack("<B", h3m_data.read(1))
if spells > 0:
for i in range(spells):
(spellID, ) = struct.unpack("<B", h3m_data.read(1))
(monsters, ) = struct.unpack("<B", h3m_data.read(1))
if monsters > 0:
for i in range(monsters):
(guard_id, guard_count) = struct.unpack("<HH", h3m_data.read(4))
h3m_data.read(8) #junk
(players, isAICan, disableAfterFirstDay) = struct.unpack("<BBB", h3m_data.read(3))
h3m_data.read(4) #junk
try:
(gevents_count, ) = struct.unpack("<I", h3m_data.read(4))
for i in range(gevents_count):
(length, ) = struct.unpack("<I", h3m_data.read(4))
name = h3m_data.read(length)
(length, ) = struct.unpack("<I", h3m_data.read(4))
text = h3m_data.read(length)
h3m_data.read(7*4) #resources
(players_affected, ) = struct.unpack("<B", h3m_data.read(1))
(human_affected, ) = struct.unpack("<B", h3m_data.read(1))
(ai_affected, ) = struct.unpack("<B", h3m_data.read(1))
(day_of_first_event,) = struct.unpack("<H", h3m_data.read(2))
(event_iteration,) = struct.unpack("<H", h3m_data.read(2))
h3m_data.read(16) #junk
except:
print("d'ough...'")
h3m_data.read(124) #junk
return map_data