Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

GXA bitmap archives

GXA files store indexed bitmap frames for UI and sky textures.

system/pointers.bmp also uses the GXA container and contains 30 cursor frames. Its first chunk is BMHD, despite the filename’s .bmp extension.

The cursor sprite sheet system/pointers.bmp also uses this chunked format. Its .bmp extension does not describe a Windows bitmap.

Overall Structure

[Section header — 4-byte tag + BE u32 payload length]
[Section payload — tag-specific]
...
[END section — tag `END ` with no payload length field]

Sections are chunked and use this order in the shipped files:

[BMHD — bitmap header (title + frame count)]
[BPAL — 256-color RGB palette]
[BBMP — bitmap frame records]
[END  — terminator]

Section Header

OffsetSizeTypeNameDescription
0x004[u8; 4]tagChunk name (BMHD, BPAL, BBMP, END ).
0x044u32data_length_beBig-endian payload size. Not present for END .

BMHD Section

The shipped .gxa files use the title GXlib image conversion. The cursor archive pointers.bmp has an empty title.

OffsetSizeTypeNameDescription
0x0022[u8; 22]titleNull-terminated title/label.
0x1610[u8; 10]unknown_16Unknown header bytes.
0x202i16num_imagesNumber of bitmap frames in BBMP.

BPAL Section

OffsetSizeTypeNameDescription
0x00768[u8; 768]colors256 palette entries, each RGB triplet (r,g,b). Values are 6-bit VGA (0–63); shift left by 2 for 8-bit RGB.

BBMP Section

BBMP stores num_images frame records back-to-back. The GOG release contains 65 GXA files with 450 total frames: 320 raw, 128 RLE-compressed, 2 LZHUF-compressed.

Frame Record Header

OffsetSizeTypeNameDescription
0x002i16unknown_00Unknown frame field.
0x022i16widthFrame width in pixels.
0x042i16heightFrame height in pixels.
0x062i16offset_xDraw offset added to the anchor X when the frame is drawn as a HUD bitmap.
0x082i16offset_yDraw offset added to the anchor Y when the frame is drawn as a HUD bitmap.
0x0A2i16compressionCompression type (0 = raw, 1 = RLE, 2 = LZHUF).
0x0C6[i16; 3]unknown_0cThree unknown frame fields.

offset_x and offset_y are zero in shipped files. The candle, active-item and compass HUD elements add them to their anchor; the power-up slots and the logbook icon ignore them. See In-game HUD.

Pixel Decode Rules

  • Palette index 0 is transparent (RGBA = 0,0,0,0).
  • Non-zero indices map to BPAL RGB (after 6-bit → 8-bit expansion) and use alpha 255.
  • Rows are stored top-to-bottom (no Y-flip needed).

Compression Type 0 - Raw

Pixel data is width * height uncompressed bytes starting at offset 0x12.

Compression Type 1 - RLE

OffsetSizeTypeNameDescription
0x124u32compressed_sizeSize of RLE-compressed pixel data.
0x16compressed_size[u8; N]compressed_dataRLE-encoded pixel stream.

RLE control byte behavior:

  • 0x80..=0xFF: repeat the next byte (ctrl & 0x7F) + 1 times.
  • 0x00..=0x7F: copy the next ctrl + 1 literal bytes.

Compression Type 2 - LZHUF

OffsetSizeTypeNameDescription
0x124u32compressed_sizeSize of LZHUF-compressed pixel data.
0x164u32uncompressed_sizeExpected decompressed size (width * height).
0x1Acompressed_size[u8; N]compressed_dataLZHUF bitstream (Okumura LZSS + adaptive Huffman, 4 KB window).

LZHUF uses a 4096-byte sliding window initialized to 0x20, with adaptive Huffman coding for both character and position codes. This matches the classic Okumura lzhuf.c algorithm, equivalent to LHA method lh1.

Section Length Quirk

Some files store BBMP data_length_be as file_size - 4 instead of the actual BBMP payload size. Parsers should clamp the BBMP section length to available data.

External References