Skip to content

Instantly share code, notes, and snippets.

@BenChampion
Last active August 14, 2020 11:10
Show Gist options
  • Save BenChampion/96d6b18fc60c88a5b513c7be483de6d9 to your computer and use it in GitHub Desktop.
Save BenChampion/96d6b18fc60c88a5b513c7be483de6d9 to your computer and use it in GitHub Desktop.
Incomplete parser for R3D HOB file
from construct import \
Int8ul, Int16ul, Int16ub, Int16sl, Int32ul, \
Float32l, \
Struct, AlignedStruct, Array, Aligned, Sequence, \
RepeatUntil, GreedyRange, \
this, len_, obj_,\
Byte, Bytes, Const, \
Bitwise, BitStruct, Flag, BitsInteger, \
If, IfThenElse, Select,\
Tell, Computed, Check, \
Pointer, \
Probe, RawCopy, HexDump # can debug by putting a "Probe()" as a parser field
Object = \
Struct(
"name" / Bytes(16),
"object_parts_offset" / Int32ul,
"object_parts_header_offset" / Int32ul,
"object_subparts_header_offset" / Int32ul,
"UNKNOWN_header_offset" / HexDump(RawCopy(Int32ul)), # in e.g. imp_stuff/tieinter_HOB, not zero;
"UNKNOWN_header_2_offset" / HexDump(RawCopy(Int32ul)), # instead, offsets to another object header
# just after the 0xFFFFFFFF end of first one (?)
Const([0]*4, Int8ul[4]),
"object_parts_names_offset" / Int32ul,
"object_effects_offset" / Int32ul,
"UNKNOWN_offset_1" / HexDump(RawCopy(Int32ul)),
"UNKNOWN_float" / Float32l,
Const([0]*12, Int8ul[12]),
"UNKNOWN_floats" / Array(5, Float32l),
"offset_to_8_bytes_before_bounding_box_before_end_of_header_marker" / Int32ul,
"bounding_box" / Array(6, Float32l)
)
ObjectDefinition = \
Struct(
"object_parts_header" /
Struct(
"number_of_object_parts" / Int16ul,
"number_of_object_subparts" / Int16ul,
"object_parts_offsets" /
Array(this.number_of_object_parts,
Struct(
"UNKNOWN_maybe_2_Int16ul" / HexDump(RawCopy(Int16ul[2])),
"object_part_meshdef0_offset" / Int32ul
)
),
),
"object_subparts_header" /
Struct(
"number_of_object_parts" / Int16ul,
"number_of_object_subparts" / Int16ul,
"object_subparts_definition" /
Array(this.number_of_object_parts,
Struct(
"object_subparts_meshdef1_offsets" / RepeatUntil(obj_ == 0, Int32ul),
"number_of_subparts_this_part" / Computed(len_(this.object_subparts_meshdef1_offsets) - 1)
)
),
),
"named_object_parts" /
Aligned(4,
RepeatUntil(obj_ == 0,
Select(
Const(0,Int32ul),
Struct(
"name" / Bytes(8),
"object_index" / Int16ub
)
)
)
),
"object_effects" /
RepeatUntil(obj_ == 0,
Select(
Const(0,Int32ul),
Struct(
"meshdef1_offset_plus_4" / Int32ul,
"name" / Bytes(6),
"object_index" / Int16ub
)
)
),
"UNKNOWN_matrices" /
RepeatUntil(obj_ == 0,
Select(
Const(0, Int32ul),
Struct(
"name" / Bytes(6),
"maybe_object_index" / HexDump(RawCopy(Int8ul)),
"number_of_matrices" / Int8ul,
"matrices" / Float32l[9][this.number_of_matrices]
)
)
),
"bounding_boxes" /
RepeatUntil(obj_ == b"\xff\xff\xff\xff",
Select(
Const(b"\xff\xff\xff\xff",Bytes(4)),
Struct(
"unknown_maybe_0_or_1" / Int32ul,
"bounding_box" / Float32l[6]
)
)
),
)
Meshdef0 = \
Struct(
"mostly_offset_to_next" / Int32ul,
"maybe_offset_to_prev" / Int32ul,
"maybe_mostly_offset_to_beginning" / Int32ul,
"offset_to_end_if_no_next" / Int32ul,
"offset_to_meshdef1_plus_4" / Int32ul,
Const([0]*8, Int8ul[8]),
Array(3,
Struct(
Float32l,
Const([0]*12, Int8ul[12])
)
),
"UNKNOWN_int" / HexDump(RawCopy(Int32ul)),
"UNKNOWN_floats_1" / Float32l[3],
"UNKNOWN_floats_2" / Float32l[3],
"UNKNOWN_floats_3" / Float32l[4],
"maybe_relative_translation" / Float32l[3],
)
Meshdef1 = \
Struct(
"facedef_end_offset" / Int32ul,
"UNKNOWN_maybe_offset_to_next" / Int32ul,
"UNKNOWN_maybe_offset_to_prev" / Int32ul,
Const([0]*12, Int8ul[12]),
"vertex_count" / Int32ul,
"UNKNOWN_int" / Int32ul,
Const(0, Int32ul),
"face_block_offset" / Int32ul,
"vertex_block_offset" / Int32ul,
"UNKNOWN_offset" / Int32ul,
Const([0]*48, Int8ul[48]),
)
Color = \
Struct(
"R" / Int8ul,
"G" / Int8ul,
"B" / Int8ul,
"A" / Int8ul
)
Face = \
Struct(
"flags" /
BitStruct(
"bit07_UNKNOWN" / Flag,
"bit06_has_extra_8_bytes" / Flag,
"bit05_has_color" / Flag,
"bit04_has_vertex_colors" / Flag,
"bit03_is_quad" / Flag,
"bit02_has_texture_coords" / Flag,
"bit01_UNKNOWN" / Flag,
"bit00_UNKNOWN" / Flag,
"bit15_UNKNOWN_seems_unset" / Flag,
"bit14_UNKNOWN_seems_unset" / Flag,
"bit13_UNKNOWN_seems_unset" / Flag,
"bit12_UNKNOWN_seems_unset" / Flag,
"bit11_UNKNOWN_seems_unset" / Flag,
"bit10_UNKNOWN" / Flag, # may be associated with an extra 8 bytes
"bit09_UNKNOWN" / Flag, # may be associated with an extra 8 bytes
"bit08_UNKNOWN" / Flag,
),
"UNKNOWN_bits_seem_unset" / Const(0,Int16ul),
"UNKNOWN_byte_1" / Int8ul,
"UNKNOWN_byte_2" / Int8ul,
"UNKNOWN_byte_3" / Int8ul,
"face_struct_size_in_4_byte_words" / Int8ul,
Const(0, Int16ul),
"material_index" / Int16ul,
"vertex_indices" / Int16ul[4],
# all DEBUG sizes in 4 byte words
"DEBUG_FIXED_PARTS_SIZE" / Computed(5),
"DEBUG_EXTRA_8_BYTES_SIZE" / Computed(lambda this: 2 if this.flags.bit06_has_extra_8_bytes else 0),
"DEBUG_COLOR_SIZE" /
Computed(
lambda this:
(
(4 if this.flags.bit03_is_quad else 3) if this.flags.bit04_has_vertex_colors else 1
)
if this.flags.bit05_has_color
else 0
),
"DEBUG_TEXTURE_COORDS_SIZE" /
Computed(
lambda this:
(4 if this.flags.bit03_is_quad else 3)
if this.flags.bit02_has_texture_coords
else 0
),
"DEBUG_COMPUTED_TOTAL_SIZE" /
Computed(
lambda this:
this.DEBUG_FIXED_PARTS_SIZE +
this.DEBUG_EXTRA_8_BYTES_SIZE +
this.DEBUG_COLOR_SIZE +
this.DEBUG_TEXTURE_COORDS_SIZE
),
Check(this.DEBUG_COMPUTED_TOTAL_SIZE <= this.face_struct_size_in_4_byte_words),
"face_data" /
IfThenElse(this.DEBUG_COMPUTED_TOTAL_SIZE == this.face_struct_size_in_4_byte_words,
Struct(
"UNKNOWN_extra_8_bytes" /
If(this._.flags.bit06_has_extra_8_bytes, Byte[8]),
"color" /
If(this._.flags.bit05_has_color,
IfThenElse(this._.flags.bit04_has_vertex_colors,
Color[lambda this: 4 if this._.flags.bit03_is_quad else 3],
Color
),
),
"texture_coords" /
If(this._.flags.bit02_has_texture_coords,
Array(lambda this: 4 if this._.flags.bit03_is_quad else 3,
Struct(
"u" / Int16sl,
"v" / Int16sl
)
)
)
),
Struct(
"size_mismatch_unknown_flags_raw_bytes" /
HexDump(RawCopy(Byte[lambda this: 4*(this._.face_struct_size_in_4_byte_words - this._.DEBUG_FIXED_PARTS_SIZE) ]))
)
)
)
FaceBlock = \
Struct(
Const([0]*8, Int8ul[8]),
"current_file_pos" / Tell,
"current_file_pos_plus_4" / Int32ul,
"face_count" / Int32ul,
"faces" / Face[this.face_count]
)
Vertex = \
Struct(
"x" / Int16sl,
"y" / Int16sl,
"z" / Int16sl,
"maybe_padding" / Const(0,Int16sl)
)
HOBFile = \
Struct(
"object_count" / Int32ul,
"offset_to_faces_and_vertices" / Int32ul,
"object_data" /
Array(
this.object_count,
Struct(
"object_header" / Object,
"object_definition" / Pointer(this.object_header.object_parts_header_offset, ObjectDefinition),
"object_composition" /
Array(
this.object_definition.object_parts_header.number_of_object_parts,
Struct(
"meshdef0" /
Pointer(lambda this:
this._.object_definition.object_parts_header.
object_parts_offsets[this._index].object_part_meshdef0_offset,
Meshdef0
),
"subparts" /
Array(lambda this:
this._.object_definition.object_subparts_header.
object_subparts_definition[this._index].number_of_subparts_this_part,
Struct(
"meshdef1" /
Pointer(lambda this:
this._._.object_definition.object_subparts_header.
object_subparts_definition[this._._._index].
object_subparts_meshdef1_offsets[this._._index] - 4,
Meshdef1
),
"face_block" /
Pointer(
this.meshdef1.face_block_offset,
FaceBlock
),
"vertex_block" /
Pointer(
this.meshdef1.vertex_block_offset,
Struct(
Array(
this._.meshdef1.vertex_count,
Struct(
"vertex" / Vertex
)
),
)
),
)
)
)
)
)
)
)
# Y level/lv_X/opkg_HOB", X = 1..9 a...i (j has empty opkg)
# Y box_HOB
# Y dbg/dbg_HOB
# Y pl_crafts/xwing_HOB
# Y pl_crafts/falcon_HOB
# (Y all pl_crafts tried, including wm crafts and cockpits)
# (Y imp_stuff/tieinter_HOB)
# (Y ../data2/koelsch_HOB)
# (Y weapons_HOB)
with open("../../unpacked_DATA/data/dbg/dbg_HOB","rb+") as f:
result = HOBFile.parse_stream(f)
print(result)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment