Skip to content

The block container ​

The outer layer of most of POD's binary files. A modder must decode it before anything else, and encode it again after a change, or the game stops with a checksum error.

Reader: decoders/container.py (check DIR, decode FILE OUT). State: decoded and round-trip for all 98 files below (measured, 2026-10-23: every block's checksum matches and encoding the decoded stream gives the original bytes).

Layout ​

A file is a run of blocks, all BLOCK bytes long; the file size is a multiple of BLOCK.

Offset in blockSizeContent
0BLOCK - 4Data: each 32-bit little-endian word XORed with KEY
BLOCK - 44Checksum: the sum, modulo 2³², of the decoded data words of this block

Decoding a file joins the data of its blocks into one stream of (size / BLOCK) × (BLOCK - 4) bytes. Observed: FUN_00468358 and FUN_004694b8 decode a block in place with *p ^= key over BLOCK / 4 - 1 words, add the decoded words, and call the error routine FUN_004912b8 when the sum differs from the last word.

Keys ​

The key and block size are constants at each call to the open routine FUN_004694b8: the key in EDX, the block size in EBX (Watcom register arguments; Ghidra's pseudocode drops EBX, the disassembly shows it). Observed at the call sites; the file kinds are measured by which key decodes which files.

KeyBlockCall site (function)Files
0x0F7E0x40000x46ef96 (FUN_0046ef04)CIRCUITS/*.BL3, *.BL4; POLICES/*.BI2
0x0F2E0x40000x46fbb6 (FUN_0046fb14)VOITURES/*.BV3, *.BV4
0x0DD60x40000x4ad48d (FUN_004ad400)VOITURES/CHROME.CH3, .CH4
0x0FAE0x40000x46e6a2, 0x46e813POLICES/*.BP3
0x0F3A0x20000x469dc0, 0x469f64, 0x46a325VIDEO/*.BBM, CREDITS.BVB
0xEA1E0x2000x474b83 (FUN_00474b4c)DICOS/*/*.BMD
0xEAFA0x10000x4b23b0 (FUN_004b2300)DATA/RECORDS/*.BRT

The .BI2 files decode with the track key, although the fonts' routine loads the other POLICES files: which routine opens them is open.

Not in this container (measured: no key matches): SCRIPTS/*.BIN, MIMG/*.IMG, RESSON/*.MEG, the AVI videos and the text files.

The stream header ​

The open routine reads three things from the start of the stream (observed in FUN_004694b8):

Stream offsetSizeStored atMeaning
04+0x100Equal to the raw file size in every file checked (measured); use unknown
44+0x110Entry count; the low 30 bits are used (& 0x3fffffff)
84 × counttable at +0x114One offset per entry

The first offset equals 8 + 4 × count, the end of the header (measured on all 98 files).

Offsets count raw file bytes, checksum words included. FUN_00468dc4 reads the block at offset - offset % BLOCK and starts at offset % BLOCK inside it (observed). In the decoded stream, an offset o is at (o // BLOCK) × (BLOCK - 4) + o % BLOCK. Measured: no offset in the 98 files falls in a checksum word, and in every track file each offset mapped this way lands on the start of a name, while read as a stream offset it lands inside other data (decoders/offsets_probe.py).

Unused entries hold raw zeros, so they decode to the key itself (for example 0x0F7E as the last track entry, and nine entries of 0x0DD6 in CHROME.CH3). A raw zero word is the only way a decoded word can equal the key, and these words still count in the checksum.

The reader object ​

Fields of the reader, from FUN_004694b8 (open), FUN_00468358 (read) and FUN_00468dc4 (seek). All observed.

OffsetTypeMeaning
+0x000char[0x100]File name
+0x100u32First word of the stream
+0x104u32Block size
+0x108HANDLEFile handle
+0x10cu32Key
+0x110u32Entry count
+0x114u32*Entry offset table
+0x124u8*Block buffer
+0x128i32Bytes left in the current block
+0x12cu8*Read position in the buffer

Leftovers ​

The writer (FUN_00468fa4, FUN_0046859c) encodes each block in place in one buffer. When the data ends inside the last block, the rest of that block keeps what the buffer held: the previous block, already encoded. Encoded again on writing, those bytes decode to the previous block's decoded bytes XOR the key (aligned to the word). Measured on every byte after the pixels in PVRSMOKE.BI2 and PVRSHADO.BI2 (fonts.md). A writer that wants identical files must reproduce this; the .BRT files have zeros there instead (records.md).

Open questions ​

  • What do the two words after CHROME.CH3's header mean, and why does chrome have its own key?

Prose CC0 1.0, code Apache 2.0. POD: Planet of Death is © Ubi Soft; none of its files are on this site.