Skip to content

Lists, menus and the smaller files ​

The files that hold lists rather than pictures or sound: the circuit list, the menu tree, the race display layout and the music tables, plus the files the game writes into DATA\Params\ and DATA\Com\, and the two kinds of file in the data folders that are not POD formats (the AVI videos and LOADER.EXE). The car tables VOITURES.BIN, VOITURE2.BIN and VOIRESET.BIN are in cars.md.

FileReader (Python, Rust)State
SCRIPTS/CIRCUITS.BINdecoders/circuits.py, circuits.rsdecoded, round-trip
SCRIPTS/BETAMNU.BINdecoders/betamnu.py, menus.rsdecoded, round-trip; many item fields unknown
SCRIPTS/HABI.BINdecoders/habi.py, dressing.rsdecoded, round-trip; fields traced (docs/systems/hud.md)
RESSON/CDAREF.TXT, MIDIREF.TXT, SCRIPT1.TXT, SCRIPT2.TXTdecoders/restxt.py, music.rsdecoded, round-trip
VIDEO/*.AVIdecoders/riff.pychunk tree walked (standard RIFF AVI)
LOADER.EXEdecoders/pe.pylayout walked (a Windows program)
Params\<car>.prb, Com\modem.annnonetraced in the code; not on the disc

Measured for every file on the disc, 2026-10-23: every byte read, and writing the decoded form gives the original bytes (the AVI and EXE walkers only read).

CIRCUITS.BIN: the circuit list ​

Observed in FUN_0047737c: it opens the file in folder 7 (Data\Binary\Scripts\), reads a u32 count into 0x6174C4, then 0x1180 bytes into 0x61D5EC, and XORs each of those bytes with 0x50. The 0x1180 bytes are 32 rows of 0x8C bytes; the first count (17) are circuits, the other 15 rows are zero (measured). POD's file has 35 rows: 32 circuits, then three rows that each hold a circuit named "Night" (measured). POD's reader (FUN_00471f24) reads the count, then that many rows of 0x8c (up to 96, each in its own allocation), so the three "Night" rows are never read (observed, 2026-10-24). The readers keep them so that the file writes back the same.

OffsetSizeContentEvidence
0x0020File name stem: CIRCUITS/<stem>.BL3, RECORDS/<stem>.BRTobserved: copied to 0x620048 by FUN_0043572c
0x1464The name shown in the menusobserved: read at 0x61D600 by FUN_004a8b80 and FUN_004cbcec
0x5413Small map picture, sm_*.tgainferred from the names
0x6113Sky picture, sk_*.tga; may fill all 13 bytes with no zeroinferred
0x6E14Second sky picture, sk_*_2.tga; may be cut short (sk_Beltane_2.t)inferred
0x7C1Flags: bit 1 = may be raced mirrored; bit 0 set on every row, meaning unknownobserved: FUN_0043db04 draws a mirror bit only for rows with bit 1; FUN_004a8fd0
0x7D1Group: 2 for the 16 circuits, 1 for Specialobserved: FUN_0044cb68 finds a row by (group, number)
0x7E1Number, 0 to 16as above
0x7F1Zeromeasured
0x804Length, metres (Beltane 7,717)inferred: FUN_0044d98c prints it with %f divided by a number read from the text tables after INFOS/KM
0x841Laps of a race (3 to 8)observed: FUN_0043572c copies it to 0x61D5C8 outside time attack
0x851Timing sectors per lap (2 to 4)observed: FUN_0043572c copies it to 0x61D5D4, the sector count of records.md
0x862Zeromeasured
0x884Zero in the file; the game stores the slot of the track's sound bank RESSON/<stem>.MEG hereobserved: FUN_0045ede0 calls the bank loader FUN_004c1e48 with the stem

Text fields are zero after their first zero byte (measured); the readers refuse other bytes there so that a written file matches the game's.

BETAMNU.BIN: the menu tree ​

Observed in FUN_00435d74 (start-up): three u32 words (0x502ED0 = 32, 0x502ED4 = 1, then the size of the rest), then the rest read whole into one allocation and walked as menus.

A menu is 0x1BC bytes, followed by its items of 0xFC bytes each. In POD a menu is 0x374 bytes: 200 item slots instead of 90 (observed in POD's load_menus 0x428af8, which steps 0x374 a menu at 0x428e09 and 0xFC an item; measured: the whole file walks with it). The walk ends at the end of the buffer: 33 menus, 503 items (measured).

Menu offsetSizeContentEvidence
0x000x44The editor's name for the menu, in French ("Menu racine", "Ecran des Records")measured
0x442Menu number: the game stores the menu's address at 0x502DCC + 4 × numberobserved
0x462Handler: index into the function table FUN_0043be00 returns; -1 none. The game puts the function's address in the whole word +0x44observed
0x48, 0x4C4, 4Unknown
0x504Item countobserved
0x5490 × 4Item addresses as the menu editor held them in its memory (0x6D902C …); the game overwrites the first count with its own pointers; the rest are zeroobserved, measured
Item offsetSizeContentEvidence
0x004 × 4Top, bottom, left, right on a 640 × 480 screeninferred from the values ("One Player": 153, 185, 195, 304)
0x100x40The editor's label ("One Player"); empty for most items: the shown text comes from the DICOS tablesmeasured
0x684Action: index into the same function tableobserved
0x744Type; types 6 and 10 carry a listobserved
0x784List entries (types 6 and 10)observed
0x7C4Index into the table at 0x506EDC (types 6 and 10)observed
0xD0 / 0xD44 × entriesOne action per list entry (type 6 / type 10)observed

The other item words are kept as they are; the words at 0x5C look like the indices of the neighbouring items (inferred).

HABI.BIN: the race display layout ​

Observed in FUN_0047ce34: it reads up to 0x6400 bytes into 0x5393F0 and XORs every byte with 0x80. The text is a run of zero-terminated lines; empty lines are extra zero bytes; the last line is Fin Habillage ("end of dressing"). An element starts with a line that FUN_0047ce34 reads with sscanf("%ld %s %ld %s %ld %ld %ld %ld %ld %ld"): ten fields, the fourth a font or picture name (typo021 … typo040, nulle). The lines after it, up to the next such line, are the element's options: characters to draw (! ! ! ! ! !), lists after /l, /p, /s, /t, /c, and a last line of /N KEY pairs naming the view keys (F3, F4, PgUp). 219 elements (measured). The ten fields (slot, kind, the element it hangs off, font, a word, the start state, display mode, views, x, y) and what each kind draws are in docs/systems/hud.md.

The music tables (RESSON/*.TXT) ​

Plain text, CR LF lines. The game reads a line at a time with FUN_004c21e8 (at most 0x50 characters) and skips lines that start with ; or a space, and empty lines (observed in FUN_004c49e0).

FileReaderKeys (sscanf)
CDAREF.TXTFUN_004c4170NB_CD_TRACKS:%hd; per track %hd:, TRACK:%hd (the CD audio track), START:%hd, TIME:%hd; at most 20 tracks of 6 bytes at 0x697D18
MIDIREF.TXTFUN_004c45d8NB_MIDI_TRACKS:%d; per track %hd:, MIDIFILE:%s, TIME:%hd
SCRIPT1.TXT, SCRIPT2.TXTFUN_004c49e0NB_LOOPS:%hd, NB_ELEMENTS:%hd; per element TYPE:%s (CD → 3, else 2: MIDI), ID:%hd, VOLUME:%hd, FADEIN:%s, FADEOUT:%s, CROSSFADE:%s (OUI = yes)

CDAREF.TXT lists CD tracks 2 (30) and 3 (20): the disc's music is audio tracks 2 and 3. MIDIREF.TXT lists no MIDI tracks; its commented-out entries name resson\gmarag.mid and resson\canon.mid. SCRIPT1.TXT plays CD element 0 at volume 50 for 9,999 loops; SCRIPT2.TXT is a MIDI test sequence ("sequence scriptee de test des pistes CD"). The units of START, TIME and VOLUME are open. The callers of FUN_004c49e0 (FUN_004c0f3c) choose the script file; how is open.

Files the game writes: Data\Params\ and Data\Com\ ​

Both folders are empty on the disc. The folder numbers are observed in FUN_0047aeb4, which turns a folder number into a path: 10 = Data\Params\, 12 = Data\Com\ (11 = Scope\).

Params\<car>.prb: car set-ups ​

The garage's five saved set-ups for one car (docs/systems/front-end.md, "The garage"). Observed in POD: load_car_setups (0x472138) reads them, 0x472668 writes them; the reader and writer are crates/pod-formats/src/prb.rs. The name is the car's own name with .prb. Five rows of 0x28 bytes in memory (name at +0, flag at +0x10, the five values at +0x14); in the file, per row r (0 to 4):

SizeContent
4Name length n, the zero included; at most 15
nThe name, encoded: the first byte XOR 0xad + 0x2d × r (a byte), each next byte XOR the encoded byte before it plus 0xfd
1Flag: 1 for an unused row
4Value 1 XOR 0x1b37efe3
4 × 4Values 2 to 5: ((previous ^ K(i-1)) − value) ^ K(i), with K = 0x1b37efe3, 0x345cbe08, 0xfc405a6c, 0xadffddb3, 0xda27fe18

then a u32 check word: the sum of all 25 values XOR 0xda27fe18.

What the reader keeps: without the file, five rows named ... with flag 1. A row whose name length is over 15 becomes ... unused, and the reading goes on just after the length. Each value is added to the sum before a negative one is set to 0. A wrong check word makes every row ... unused (the values stay). In time attack (0x63331c & 4) a used row with a value outside 0 to 100, or five values adding up to more than 300, becomes unused. The values are acceleration, brakes, grip, steering and speed, sharing 300 points.

The first edition's reader (FUN_0047751c) and writer (FUN_00477894) store the values as differences without the keys and the names without encoding (history, docs/versions.md). No game-written file has been compared yet: tests in prb.rs check the writer against the reader.

Com\modem.ann: the modem phone book ​

Observed in FUN_00454bb4: three pairs of (u32 length, text) — a name and a phone number for each entry — read into the multi-player menu's text fields. Without the file the game shows ... in each. FUN_00454e24 writes it (opened for writing in folder 12).

Other files ​

  • VIDEO/*.AVI (6): standard RIFF AVI (Microsoft Video for Windows). riff.py walks the chunk tree to the last byte: each has one Indeo 4.1 (IV41) video stream (CREDITS.AVI: 280 × 180, 131 frames at 33,367 µs). The pictures, the sound and how the game plays them: video.md.
  • LOADER.EXE: a 32-bit Windows program linked 1997-02-11, identical to the installed LOADER.EXE (docs/FINDINGS.md). pe.py accounts for its bytes by headers and sections.

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