Championship saves (Data\ChShip\POD<n>.<kind>)
POD saves a championship under way into one of sixteen slots. The game makes these files; the disc has none. Everything here is observed in POD 2.8's WINPOD.EXE unless marked; no file made by the original has been checked yet (unknown: whether a real save matches byte for byte; the layout below is read from the code that writes and reads it).
Reader and writer: crates/pod-formats/src/chship.rs (round-trip tests in the module). The menus that save and load them: docs/systems/front-end.md, "Championship saves (POD)".
Where and what
- Folder: folder 9 of
open_data_file(0x4765f8):folder_path(0x475a10) givesDirectory(0x58e1b0) plusData\ChShip\(0x4e5f64). As for the other save folders, no copy on the CD is looked for (docs/modding.md). - Name (
0x46b598):PODand the slot ("%s%d", the slot 0 to 15 as a signed 16-bit number), then an extension chosen from the mode bits (0x633320), tested in this order:
| Mode bits | Extension | Championship |
|---|---|---|
| 2, 0x800 and 0x1000 | .RS | random (action 21, mode 0x180a) |
| 2 and 0x80 | .CS | custom (action 20, mode 0x8a) |
| 2 and 0x1000 | .VS | unknown |
| 2 and 0x800 | .TS | unknown |
| 2 | .NS | standard (action 19, mode 10) |
Without bit 2 the name gets whatever the stack buffer held (not a championship; never called so).
- Written by
0x46b70c(slot inAX):CreateFileAfor writing, then oneWriteFileper block below, each checked for its full length; a short write closes the file and returns 0. - Read by
0x46bc60(slot inAX, keep inDL): nothing when the slot's menu text is empty; else0x46b200keeps the globals, each block is read and stored back where it came from, and any short read restores them (0x46b3cc) and returns 0. WithDL= 0 (a probe) the state is restored after a good read too. - Names only (
0x46c2d8): for each of the 16 slots, the first 32 bytes are read into the current menu's item text (AL= 1) or field (AL= 0); a slot with no file gets an empty field.
Layout
All values little-endian; i32 unless said. Offsets for one player and eight racers with no driver entries; the racers' part grows with their lists.
| Offset | Size | Content | Memory (POD) |
|---|---|---|---|
0x0000 | 32 | The slot's name: the menu item's field (+0x88) as typed, its zero, then whatever the field held | item +0x88 |
0x0020 | 8 | POD Cshp (0x4e4e74, strncpy of 8: no zero). Not checked by the reader | — |
0x0028 | 4 | Tracks in the championship | 0x633340 |
0x002c | 4 | The race being run, from 0 | 0x633344 |
0x0030 | 4 | Human racers | 0x633324 |
0x0034 | 4 | Computer racers | 0x633328 |
0x0038 | 4 | Mode bits | 0x633320 |
0x003c | 4 | Laps per race | 0x633330 |
0x0040 | 4 | Difficulty (1, 2, 4) | 0x633334 |
0x0044 | 4 | Damage (0, 1, 2) | 0x63334c |
0x0048 | 16 × 4 | The tracks by circuit number; action 19 writes -1 past the last | 0x634594 |
0x0088 | 0xd40 | Player 1's garage set-ups: eight car slots of 0x1a8 (layout not traced) | 0x4fe2e8 |
0x0dc8 | 8 | Player 1's kept flag per car slot | 0x4ffd68 |
0x0dd0 | 0xd40 | Player 2's set-ups | 0x4ff028 |
0x1b10 | 8 | Player 2's kept flags | 0x4ffd70 |
0x1b18 | 1 | Racer 0's gearbox (4 automatic) | 0x6339b8 (racer entry +0xc4) |
0x1b19 | 1 | Racer 1's gearbox | 0x633a94 |
0x1b1a | per racer | The racers, below, as many as the low words of 0x633324 and 0x633328 added (signed 16 bits) | |
| end − 2 | 2 | Mirror mask: bit n set, race n is mirrored (0x4284a8 sets the race's flag 0x633350 from it) | 0x4fdf16 |
The reader reads nothing after the mask.
A racer (0x18c bytes, then two lists)
Built on the stack by 0x46b70c from several tables, racer r:
| Offset | Size | Content | Memory (POD) |
|---|---|---|---|
+0x000 | 0x40 | Name | 0x6338f4 + r × 0xdc |
+0x040 | 0x40 | The racer entry's text at +0x80 (start-up, 0x4986a4, copies a string from 0x6341cc into it; use unknown) | 0x633974 + r × 0xdc |
+0x080 | 1 | A byte 0x473534 subtracts from the racer count to put a race record's place back (inferred: the count less the last race's place) | 0x58b6d0 + r |
+0x081 | 3 | Never set: stale stack bytes | — |
+0x084 | 4 | The racer's car (entry +0xc0) | 0x6339b4 + r × 0xdc |
+0x088 | 4 | Never set: stale stack bytes | — |
+0x08c | 4 | Entry +0xc8 (start-up sets the racer's number from 1; use unknown) | 0x6339bc + r × 0xdc |
+0x090 | 6 × 4 | Computer driver's state after the last race (0x456408, 0x4564b0 copy word +0x3c of six objects the AI record at 0x5a8e34 + r × 0xb8 points to) | 0x5a9828 + r × 0x7c |
+0x0a8 | 6 × 4 | The same, kept from the race before (0x4565a4 copies the table) | 0x5a9448 + r × 0x7c |
+0x0c0 | 4 | Championship points | 0x58b6b0 + 4r |
+0x0c4 | 4 | Race times added up | 0x58b690 + 4r |
+0x0c8 | 4 | Entries in the second list | 0x5a9824 + r × 0x7c |
+0x0cc | 4 | Entries in the first list | 0x5a9444 + r × 0x7c |
+0x0d0 | 0xbc | The race record (crates/pod-race/src/record.rs) | 0x4f8d84 + r × 0xbc |
+0x18c | 8 × count +0xcc | First list (none when the count is 0 or less) | 0x5a93f4 + r × 0x7c |
| then | 8 × count +0xc8 | Second list | 0x5a97d4 + r × 0x7c |
The two 0x7c-byte tables per racer (0x5a93f4, 0x5a97d4) hold up to ten 8-byte entries, the count at +0x50 and the six words at +0x54; they are copied from the computer drivers' records after a race (observed in 0x456408, 0x4564b0; what the entries mean is unknown).
After a good read, 0x46bc60 also: rebuilds the order table (0x4f9360, by the place in the high nibble of each race record's byte +4); checks the mirror mask: a non-zero mask needs the difficulty's flag (0x6345d4, 0x6345d5, 0x6345d6 for 1, 2, 4), else the load fails and the mask is cleared; loads the best times of the current track (load_records, 0x4ad118, with the bit 1 << track number of the mask: by the track's number, where 0x4284a8 uses the race's, observed); then calls 0x421bb0 (unknown).
Size
0x1b1c + 0x18c × racers + 8 × (all list entries): 0x277c (10,108) bytes for one player and seven computers with empty lists.