hedz.hdz — full archive (26,529,985 bytes, 227 head records)
+=======================================================================+
| INDEX HEADER (5 bytes) |
| +0x00 u16be record_count = 227 |
| +0x02 [u8;3] padding (always 00 00 00) |
+=======================================================================+
| OFFSET TABLE (record_count x 4 = 908 bytes) |
| u32le[0] = 913 (first record starts right after the table) |
| u32le[1] = 46395 |
| ... |
| u32le[226] = 26525352 |
+=======================================================================+
| RECORD[0] offset=913 size=45,482B Bare_Alien (polygon-only) |
| RECORD[1] offset=46,395 size=103,749B |
| RECORD[2] offset=150,144 size=123,202B |
| ... smallest=4,633B largest=177,098B |
| RECORD[226] offset=26,525,352 size=4,633B |
+=======================================================================+
All offsets within a record are relative to the record start. The header
contains u32le pointers into the record body. For polygon-only heads
(like head 0), voxel/bmp pointers all equal the descriptor offset.
HEAD RECORD (variable size)
+-----------------------------------------------------------------------+
| HeadHeader (0xD1 = 209 bytes, packed C struct) |
| +0x00 u16 header_magic |
| +0x02 u32 data_size_plus_6 (record_size + 12) |
| +0x06 u32 extended_header_offset |
| +0x0A [u8;4] unused |
| +0x0E u32 voxel_direct_pointer_v92 -. |
| +0x12 u32 voxel_table_0_v93 | pointers into |
| +0x16 u32 voxel_table_1_v94 | record body |
| +0x1A u32 voxel_table_2_v95 | |
| +0x1E u32 bmp_pointer_1 | |
| +0x22 u32 bmp_pointer_2 | |
| +0x26 u32 bmp_pointer_3 | |
| +0x2A u32 character_assembly_desc ---' |
| +0x2E u32 unknown_2e |
| +0x32 u32 wav_pointer_1 |
| +0x36 u32 wav_pointer_2 |
| +0x3A u32 wav_pointer_3 |
| +0x3E u8 wav_count |
| +0x3F ... stats, physics, projectile fields ... |
| +0x4A u16 head_id |
| +0x4C [u8;10] language_name_lengths[0..9] |
| +0x56 ... more gameplay fields ... |
| +0xD0 u8 last field |
+-----------------------------------------------------------------------+
| Language names (sum of language_name_lengths[0..9] bytes) |
| 10 consecutive byte strings, lengths from header +0x4C..+0x55 |
+-----------------------------------------------------------------------+
| Record body: sections at offsets given by header pointers |
| Sections appear in pointer order; unused pointers alias another. |
| Typical order for a voxel+polygon head: |
| v92_direct -> v93_table -> v94_table -> v95_table |
| -> bmp_1 -> bmp_2 -> bmp_3 -> descriptor -> wav_data |
+-----------------------------------------------------------------------+
Head 0 is the prototype body source. All voxel/bmp pointers alias the descriptor offset (no voxel data). The body-part meshes for the entire game are embedded inline at the end of this record.
rec+0x000 +---------------------------------------------+
| HeadHeader (209 bytes) |
rec+0x0D1 +---------------------------------------------+
| Language names (82 bytes) |
| "Bare_Alien" x8, "_" x2 |
rec+0x123 +=============================================+ <-- descriptor
| DESCRIPTOR PREFIX (20 bytes) |
| +0x00 u32 stream_word_0 |
| +0x04 [u8;7] category_counts (all 0) |
| +0x0B [u8;3] base_tint_rgb |
| +0x0E u8 voxel_obj_count = 0 |
| +0x0F u8 helper_count = 0 |
| +0x10 u8 poly_node_count_a = 14 |
| +0x11 u8 poly_node_count_b = 1 |
| +0x12 u16 active_blob_id |
rec+0x137 +---------------------------------------------+ <-- node stream
| NODE 0: type 0xFF (compact inline) |
| 1 + 599 bytes: lookup/color/vertex/uv/face |
| 65 vertices, 48 faces (the "head mesh") |
rec+0x38F +---------------------------------------------+
| NODES 1-14: bare type bytes (CachedClone) |
| [10,8,13,4,2,7,14,9,11,12,3,5,6,1] |
| 14 bytes, one per body-part reference |
rec+0x39D +---------------------------------------------+
| Descriptor stream continuation |
| mount/transform tables, LUT-indexed vertex |
| data, and other assembly stream data |
| (~37,701 bytes, partially mapped) |
rec+0x96E2+=============================================+ <-- body mesh section
| BODY MESH OFFSET TABLE (68 bytes) |
| 17 x u32le, one per body-part type 0..16 |
| type[0] = garbage (unused) |
| type[4] = 0x0044 -> first mesh (3 verts) |
| type[10]= 0x0138 (3 verts) |
| ... |
| Offsets relative to table start. |
| Smallest valid offset = 0x44 = 68 |
rec+0x9726+---------------------------------------------+
| BODY MESHES (types 1-16, 6,788 bytes total) |
| 16 submeshes packed back-to-back |
| each: vertex_count(u32) |
| vertex_data(vc x 32B) |
| face_count(u32) |
| face_data(fc x 8B) |
| material_count(u32) |
| face_count_check(u32) |
| material_indices(fc x 4B) |
| material_data(mc x 80B) |
| vertex_count_check(u32) |
| uv_data(vc x 8B) |
| fills exactly to record end |
rec+0xB1AA+=============================================+ <-- record end
A typical head: voxel data for the head/props, polygon body from the prototype cache, bmp textures, and wav sounds.
rec+0x00000 +-------------------------------------------+
| HeadHeader (209 bytes) |
rec+0x000D1 +-------------------------------------------+
| Language names (149 bytes) |
rec+0x00166 +-------------------------------------------+ <-- v92
| V92 DIRECT BLOB (44,270 bytes) |
| legacy voxel format: 16B header + |
| 256-entry palette + color stream + |
| 12B/segment headers + packed moves |
rec+0x0AE54 +-------------------------------------------+ <-- v93
| V93 TABLE (688 bytes) |
| keyed voxel container (blob49) |
rec+0x0B104 +-------------------------------------------+ <-- v94
| V94 TABLE (4,035 bytes) |
| keyed voxel container (blobs 49,50,51) |
rec+0x0C0C7 +-------------------------------------------+ <-- v95
| V95 TABLE (14,737 bytes) |
| keyed voxel container (blobs 49,50,51) |
| each blob: 0x01F2 magic + 22B header + |
| RGB565 palette + color stream + trailer |
rec+0x0FA58 +-------------------------------------------+ <-- bmp_1
| BMP TEXTURES (3 sections, ~5,730 bytes) |
rec+0x110BA +===========================================+ <-- descriptor
| DESCRIPTOR PREFIX (20 bytes) |
| voxel_obj_count = 3 |
| poly_node_count_a = 14, _b = 0 |
+-------------------------------------------+
| Mounted voxel blob ids: [49, 50, 51] |
+-------------------------------------------+
| 14 polygon node type bytes (CachedClone): |
| [10,8,13,4,2,7,14,9,11,12,3,5,6,1] |
| all resolved from head 0's prototype lib |
+-------------------------------------------+
| Descriptor stream: mount/transform tables |
| (~27K bytes, partially mapped) |
rec+0x17C80 +-------------------------------------------+ <-- wav
| WAV DATA (~11,154 bytes) |
rec+0x1A812 +===========================================+ <-- record end
Each body-part mesh within the offset table section:
+0x00 u32 vertex_count (vc)
+0x04 [vc] vertex_data: vc x 32 bytes each
+0x00 f32 position_x
+0x04 f32 position_y
+0x08 f32 position_z
+0x0C [20 bytes] normals / other vertex attributes
u32 face_count (fc)
[fc] face_data: fc x 8 bytes each
+0x00 u16 index_a
+0x02 u16 index_b
+0x04 u16 index_c
+0x06 u16 padding/flags
u32 material_count (mc)
u32 face_count_check (must == fc)
[fc] material_indices: fc x 4 bytes (per-face material assignment)
[mc] material_data: mc x 80 bytes (0x50 per material)
u32 vertex_count_check (must == vc)
[vc] uv_data: vc x 8 bytes each
+0x00 f32 u
+0x04 f32 v
+-----------+
| hedz.hdz |
+-----+-----+
|
parse_record_offsets()
|
+------------+------------+
| |
head 0 record head N record
(template body) (any other head)
| |
find_body_mesh_section() parse descriptor
| |
68-byte offset table read type bytes
+ 16 submeshes (types 1-16) [10,8,13,...,1]
| |
v v
+--------------------+ for each type byte:
| prototype library | cache hit?
| HashMap<u8, Mesh> |<------+ yes -> clone mesh
+--------------------+ | no -> missing_clone_types
|
v
viewer renders all nodes:
CompactInline = unique per-head attachment
CachedClone = shared body part from library