Gear CounselCompendium

The ARZ header: a map of the entire file in 24 bytes

The first 24 bytes contain no record payloads for items, skills, or other game entities. They define the current profile and tell us where to find the rest of the file, how much space it occupies, and how many records to read.

Synthetic ARZ-X3 header24 bytes · 7 fields
  • 0x00–0x03 · profile fields
  • 0x04–0x0F · record-table fields
  • 0x10–0x17 · string-table fields

The labels below the color band map exact byte ranges to profile, record-table, and string-table fields. Color is only an additional cue. The boundaries of all seven fields are examined below.

00 · Turning seven numbers into a file map

In the previous article, we read the first four bytes as header_word_0 = 2 and header_word_1 = 3. We can now read the rest of the header and find the bounds of every major ARZ-X3 section. We do not need the contents of any record to do that.

First, we need the term record. In ARZ, this is a compiled DBR record: a distinct set of data that may describe an item, a skill, or another game entity. Every record has a record path: its full name inside the database. ARZ-X3 contains three synthetic records: records/example/dagger.dbr, records/example/shield.dbr, and records/example/strike.dbr. The first record includes the fields enabled = true and weight = 1.5: enabled and weight are field names; true and 1.5 are their values.

How three records are distributed across three parts of the file
fields and valuescompressed data
The data of each record is compressed separately from the other records. The resulting byte sequence for one record is a compressed block. ARZ-X3 has three records, so it also has three blocks: 44, 45, and 44 bytes. They appear one after another in the file and occupy 133 bytes in total.
strings by indexstring table

This is one shared, numbered list of strings. ARZ-X3 has nine entries:

IndexString
0records/example/dagger.dbr
1ExampleClass
2Class
3templateName
4database/templates/example.tpl
5enabled
6weight
7records/example/shield.dbr
8records/example/strike.dbr

The number on the left is an index: the string's number in this list.

links between partsrecord table

Each record has one entry in this table. ARZ-X3 therefore has three entries. The first column gives the index of the path string in the list above:

path string indexrelative block offsetcompressed → decompressed
00 B44 → 48 B
744 B45 → 48 B
889 B44 → 48 B

Number base: every numeric cell in this table is decimal; B means bytes. Each block offset is measured from file+0x18, the beginning of the compressed-data region.

The compressed data holds fields and values. The record table holds the coordinates of all three blocks and each record's path-string index. In the first entry, index 0 resolves to records/example/dagger.dbr. Separately, after that block is decompressed, its decoded bytes form the record body. A numeric reference inside that body can resolve index 5 to the field name enabled.

  1. divide the 24-byte header into seven distinct fields;
  2. keep offsets, sizes, and counts distinct;
  3. calculate the start and end of every section;
  4. detect a gap, overlap, or out-of-bounds region.

01 · Seven fields have different widths and different roles

A field is a defined region of bytes with its own width and role. The first two fields occupy two bytes each. The next five occupy four bytes each. Together, their widths add up as follows:

The first two fields use neutral structural names, header_word_0 and header_word_1. The remaining fields are named by their roles: record_table_offset, record_table_size, record_count, string_table_offset, and string_table_size. The file stores only the values; the names help us distinguish them while reading.

Profile fields

2 fields · 4 bytes
file+0x00
u16 header_word_0
2

value roleFormat compatibility revision

file+0x02
u16 header_word_1
3

value roleHeader feature bits

Record table

3 fields · 12 bytes
file+0x04
u32 record_table_offset
0x9D · 157

value roleStart of the record table

file+0x08
u32 record_table_size
0x78 · 120

value roleSize of the record table in bytes

file+0x0C
u32 record_count
3

value roleNumber of records

String table

2 fields · 8 bytes
file+0x10
u32 string_table_offset
0x115 · 277

value roleStart of the string table

file+0x14
u32 string_table_size
0xBE · 190

value roleSize of the string table in bytes

02 · Two words carry two different contracts

The current official writer emits the pair (2, 3), but the official readers do not consume it as “magic and version.” They operate on two separate 16-bit words.

Four bytes, but two separate fields · HEX · LE
file+0x00
u16 header_word_0
2

value roleFormat compatibility revision

file+0x02
u16 header_word_1
3

value roleHeader feature bits

Readers reject the first word when it is below 2. Because this is a lower-bound check rather than exact equality, the field is not a conventional magic value. The specification uses the behavioral label format compatibility revision. This label is an interpretation of observed behavior, not a recovered private field name.

The writer constructs the second word through bit operations, and readers test those bits separately. The value 3 means 0x0001 | 0x0002, not “version three.”

0x0001layer compatibility
Every later archive layer must match the first layer's bit. What the states 0 and 1 represent remains unknown.
0x0002regional-checksum footer
When set, the reviewed native path processes the final 16-byte footer.
0xFFFCremaining-bit mask
This mask covers the other fourteen bit positions; it is not one feature bit. The current writer leaves those positions clear. Their historical or future meanings are unknown.

Displaying all four bytes as one u32 LE produces 0x00030002, decimal 196610. That packed number is not the semantic unit used by the reviewed native code and is not one numeric version.

03 · Offset, size, and count answer different questions

All five values are physically stored in the same way, as u32 LE. The distinction comes from the field that contains the bytes, not from their appearance. Field names mark these three roles as offset, size, and count.

offset — where?
The distance from the beginning of the file to the first byte of a section. The fields record_table_offset and string_table_offset.
size — how many bytes?
The physical size of a section in bytes. The fields record_table_size and string_table_size. In general prose, this byte size may also be called the section's length; the header field names use the suffix _size.
count — how many entries?
The number of records — and therefore table entries. The record_count field.

Section end formula:end is the first position after the section.

A count does not replace a size. Record-table entries have variable width, so record_count = 3 does not tell us how many bytes the table occupies. That value is stored separately as record_table_size = 120.

04 · The record table stores the bounds of every compressed block

A compressed block does not contain its record path, compressed size, or decompressed size. Those values live in the corresponding record-table entry. We therefore determine the block's bounds from the table, not from the block's contents.

ARZ-X3 has three records, so three compressed blocks lie between the header and the record table. The next article will explain how compression works. Here, we only need the shared bounds of this region.

The compressed data starts immediately after the 24-byte header, at position 0x18. There is no separate field for its size. Its end is the value record_table_offset = 0x9D, which is also the start of the record table.

From the header through the end of the record table
RegionRangeCalculation
compressed data0x18..0x9D0x9D − 0x18 = 133 bytes
record table0x9D..0x115157 + 120 = 277

The three ARZ-X3 records correspond to three 40-byte entries, 120 bytes in total. In this example, their widths match because their embedded class tags have the same length. The format does not guarantee this for an arbitrary file, so the header stores both record_count and record_table_size.

05 · Every string in the string table has a numeric index

The record-table entry stores only the path's number in the string table — 0 — rather than the path itself. Entry 0 contains the full path records/example/dagger.dbr. The same table stores the field name enabled at index 5.

Now find the bounds of this table. Add record_table_offset and record_table_size:

The string_table_offset field also contains 0x115. Both calculations lead to the same boundary, so there is no hidden gap between the sections.

String-table bounds:

07 · The first three checks for the top-level map

For the verified build-24346246 profile with exact leading words (2, 3), the major regions are contiguous. Header values should not be trusted in isolation: they must cross-check one another and the full file size. Every addition below must use checked arithmetic; overflow is an invalid result.

  1. the table does not start inside the header

    record_table_offset ≥ 0x18
    0x9D ≥ 0x18

  2. the two tables meet without a gap

    record_table_offset + record_table_size = string_table_offset
    0x9D + 0x78 = 0x115

  3. the footer ends with the file

    string_table_offset + string_table_size + 16 = file_size
    277 + 190 + 16 = 483

Rejected gap example

string_table_offset = 0x119

The record table would still end at 0x115, but the string table would begin at 0x119. Four unexplained bytes form a gap, so this value must be rejected.

08 · The header really did describe all of ARZ-X3

We have read only the first 24 bytes, yet already know where every major section lies and where the file must end.

483 bytes from start to finish
RegionRangeContents
header0x00..0x1824 B
compressed data0x18..0x9D133 B · 3 blocks
record table0x9D..0x115120 B · 3 entries
string table0x115..0x1D3190 B · 9 strings
checksum footer0x1D3..0x1E316 B · 4 values

The header defines the major section boundaries, but not the coordinates of an individual block. Those come from the corresponding record-table entry. The contents of an LZ4 block also remain compressed. Even so, every next step now has an exact file region to work with.

Five ideas to keep

  1. The verified current ARZ header occupies exactly 24 bytes.
  2. Two leading words carry compatibility and feature behavior; five fields describe sections.
  3. Offset, size, and count are not interchangeable.
  4. A section end is calculated as start + size.
  5. Four footer checksums cover exact regions, while native loading and strict verification remain different contracts.