Last active
August 29, 2015 14:01
-
-
Save ChadSki/f4b72be6c7d3d6236d85 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# ensure plugins are loaded | |
if len(plugin_classes) == 0: | |
load_plugins() | |
def load_map(map_path=None): | |
"""Loads a map from Halo.exe's memory, or from disk if given a filepath. Loading a | |
map requires that plugins have already been loaded. | |
""" | |
# end result, constructed now (because we need the reference) but init'd at the end | |
halomap = HaloMap() | |
if map_path == None: | |
location='mem' | |
halomap.ba_builder = WinMemoryByteArrayBuilder('halo') # error if halo.exe is not running | |
else: | |
location = 'file' | |
halomap.ba_builder = FileByteArrayBuilder(map_path) | |
# class ByteArray: | |
# -- Denotes a region of bytes. | |
# -- Provides methods for reading and writing datatypes such as byte[], int, float, and string. | |
# -- Uses relative internal offsets, so two ByteArrays which wrap the same data at different | |
# locations will always appear identical. | |
# | |
# Use ByteArray(offset, size) as a constructor: | |
# offset: location within the source medium | |
# size: number of bytes enclosed | |
# | |
ByteArray = halomap.ba_builder.constructor | |
if location == 'mem': | |
# Force Halo to render video even when window is deselected | |
exe_offset = 0x400000 | |
wmkillHandler_offset = exe_offset + 0x142538 | |
ByteArray(wmkillHandler_offset, 4).WriteBytes(0, Array[Byte]((0xe9, 0x91, 0x00, 0x00))) | |
# fetch the necessary struct layouts | |
MapHeader = plugin_classes['map_header'] | |
IndexHeader = plugin_classes['index_header'] | |
TagHeader = plugin_classes['tag_header'] | |
# load the map header | |
map_header_offset = {'file': 0, 'mem': 0x6A8154}[location] | |
map_header = MapHeader( | |
ByteArray( | |
map_header_offset, | |
MapHeader.struct_size), | |
halomap) | |
# load the index header | |
index_header_offset = {'file': map_header.index_offset, 'mem': 0x40440000}[location] | |
index_header = IndexHeader( | |
ByteArray( | |
index_header_offset, | |
IndexHeader.struct_size), | |
halomap) | |
if location == 'file': | |
# Usually the tag index directly follows the index header. However, some forms of | |
# map protection move the tag index to other locations. | |
index_offset = map_header.index_offset + index_header.primary_magic - 0x40440000 | |
# On disk, we need to use a magic value to convert raw pointers into file offsets. | |
# This magic value is based on the tag index's location within the file, since the | |
# tag index always appears at the same place in memory. | |
halomap.magic = index_header.primary_magic - index_offset | |
elif location == 'mem': | |
# Almost always 0x40440028, unless the map has been protected in a specific way. | |
index_offset = index_header.primary_magic | |
# In memory, offsets are just raw pointers and require no adjustment. | |
halomap.magic = 0 | |
# load all tag headers from the index | |
tag_headers = [TagHeader( | |
ByteArray( | |
TagHeader.struct_size * i + index_offset, | |
TagHeader.struct_size), | |
halomap) for i in range(index_header.tag_count)] | |
# tag metadata can recurse to unknown places, but at least we know where they start | |
meta_offsets = sorted(tag_header.meta_offset_raw for tag_header in tag_headers) | |
if location == 'file': # the BSP's meta has an offset of 0, skip it | |
bsp_offset = meta_offsets.pop(0) | |
elif location == 'mem': # the BSP's meta has a very large, distant offset, skip it | |
bsp_offset = meta_offsets.pop() | |
# to calculate sizes, we need the offset to the end (does not point to a tag) | |
meta_offsets.append(meta_offsets[0] + map_header.metadata_size) | |
# [0, 10, 40, 60] from location offsets... | |
# [10, 30, 20] we can calculate sizes... | |
# but instead of an ordered list, key based on the start offset | |
meta_sizes = {start: (end - start) for start, end in zip(meta_offsets[:-1], meta_offsets[1:])} | |
# just give BSP's meta a size of zero for now | |
meta_sizes.update({bsp_offset: 0}) | |
name_maxlen = 256 # not sure what the actual limit is; just picking some value | |
# HaloTags can load their own name and metadata, so just give them the starting ByteArrayes | |
tags = [HaloTag( | |
tag_header, | |
ByteArray( # name | |
tag_header.name_offset_raw - halomap.magic, | |
name_maxlen), | |
ByteArray( # meta | |
tag_header.meta_offset_raw - halomap.magic, | |
meta_sizes[tag_header.meta_offset_raw]), | |
halomap) for tag_header in tag_headers] | |
halomap.init(map_header, index_header, tags) | |
return halomap |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8" ?> | |
<plugin name="index_header" struct_size="40"> | |
<uint32 name="primary_magic" offset="0" /> | |
<uint32 name="base_tag_ident" offset="4" /> | |
<uint32 name="map_id" offset="8" /> | |
<uint32 name="tag_count" offset="12" /> | |
<uint32 name="verticie_count" offset="16" /> | |
<uint32 name="verticie_offset" offset="20" /> | |
<uint32 name="indicie_count" offset="24" /> | |
<uint32 name="indicie_offset" offset="28" /> | |
<uint32 name="model_data_length" offset="32" /> | |
<ascii name="integrity" offset="36" length="4" reverse="true" /> | |
</plugin> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8" ?> | |
<plugin name="map_header" struct_size="132"> | |
<ascii name="integrity" offset="0" length="4" reverse="true" /> | |
<uint32 name="game_version" offset="4" /> | |
<uint32 name="map_size" offset="8" /> | |
<!-- 4 bytes padding --> | |
<uint32 name="index_offset" offset="16" /> | |
<uint32 name="metadata_size" offset="20" /> | |
<!-- 8 bytes padding --> | |
<asciiz name="map_name" offset="32" maxlength="32" /> | |
<asciiz name="map_build" offset="64" maxlength="64" /> | |
<uint32 name="map_type" offset="128" /> | |
</plugin> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8" ?> | |
<plugin name="tag_header" struct_size="32"> | |
<ascii name="first_class" offset="0" length="4" reverse="true" /> | |
<ascii name="second_class" offset="4" length="4" reverse="true" /> | |
<ascii name="third_class" offset="8" length="4" reverse="true" /> | |
<uint32 name="ident" offset="12" /> | |
<uint32 name="name_offset_raw" offset="16" /> | |
<uint32 name="meta_offset_raw" offset="20" /> | |
<uint32 name="indexed" offset="24" /> | |
<!-- 4 bytes padding --> | |
</plugin> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment