The compression codec

Technical appendix · Home · Français

Used by .HUF streams and by G3.CON record 0x27 subs 9 and 10. Those two are the only .CON payloads that pass try_decompress. All of this is implemented by maup_parse.py.

Routine EA Role
Lz_DecompressFar 0x19D92 entry: AX = length low, DX = length high; clears state, sets limit
Lz_Decompress 0x19C6A main loop, owns the ring window
Lz_ReadToken 0x19BD6 tree walk to a symbol
Lz_ReadDistance 0x19C14 distance decode
Lz_ReadByte / Lz_ReadBit 0x19855 / 0x197F8 input
Lz_BitInit 0x198AF tree and bit-reader init
Lz_UpdateTree / Lz_RebuildTree 0x19ACE / 0x19958 adaptive update

Framing

A stream starts with a u32 output length, skipped at 0x19C70. The tree is built at runtime, so there is no static table to find. Decoding stops when the produced count reaches the length.

Bits

MSB-first through a 16-bit register (DS:303E, count at DS:3040).

Tokens and matches (verified)

tree: 314 symbols; token < 0x100 is a literal
match length = token - 0xFD (add ax,0FF03h at 0x19CF6)
c = ReadByte()
high = LzDist_ExtraBits[c] << 6        ; DS:2E3E
n = LzDist_BaseBits[c] - 2             ; DS:2F3E
acc = c; repeat n times: acc = acc*2 + ReadBit()
distance = high | (acc & 0x3F)

Window

A 4096-byte ring at DS:6754, with its first 0xFC4 bytes filled with spaces and writing starting at 0xFC4. Match source = (pos - distance - 1) & 0xFFF.

The 0x400 counter is not a chunk format

0x19D4F-0x19D69 only raises wLzProgressLimit by 0x400 when the output passes it. Nothing reads that for decoding: it doesn't change input position, bit state, tree or window. maup_parse.py decodes every stream correctly without chunk handling.

Verification

selftest decodes all 29 MUS and 28 SND streams; header lengths agree and tail padding is at most 7 bits.

Previous: File formats, scenes and palettes · Next: Video · Technical appendix · Home