Skip to content

Un-POD: the road to 1:1 ​

Un-POD is the reimplementation of POD's WINPOD.EXE in Rust (the last version released, the reference since 2026-10-24: docs/versions.md), in the way of Un-Banished (GameFrame's docs/case-studies/banished.md). This page says what "1:1" means here, lists every subsystem with how it is checked and where it stands, and sets the order. docs/CHECKLIST.md keeps the item-by-item state; each subsystem's document in docs/systems/ keeps its evidence.

What 1:1 means ​

Given the player's own disc and the same inputs, Un-POD does what WINPOD.EXE does:

  • The same race: the same physics, opponents, laps, order and times, step for step. Fixed point and integer arithmetic match bit for bit, measured under the emulator (re-emulate), and every random draw comes in the same order.
  • The same game: the same menus, screens, text, options, car and track choices, best times and saved settings, so files written by one load in the other.
  • The same picture and sound, at 640 × 480 first: frames compared with the original's (the emulated PC records them), then any resolution as an addition.
  • Checked, not assumed: every subsystem has a replay test against recordings of the original (derived/race/, run/vm/racerec.py), and every intentional difference is a Deviation: in the code (docs/DEVIATIONS.md).

Additions (upscaling, editors, mods, gamepads) stay behind build options, and --vanilla turns them all off (AGENTS.md, working rule 5).

POD is the only current reference ​

Since 2026-10-24 (the user's decision), the latest POD release is the sole target for code, tests, measurements and recordings. The March 1997 first edition is historical context only. The address map and version comparison remain in labels/pod/ and docs/versions.md under their established names; do not use them to add first-edition support or start new first-edition measurements. The game files remain at target/pod/ until the user changes that data layout.

Reference setup:

  • [x] POD's WINPOD.EXE, PODMMX.EXE and the 3D builds exported (analysis/export/pod/); WINPOD.EXE's gaps filled (run/ghidra).
  • [x] Historical address matching (labels/pod/, run/versions) and the version record (docs/versions.md); use POD addresses for ongoing work.
  • [x] POD runs in the emulated PC, in English, with its own patch list and readers (run/vm/pod.sh, run/vm/podaddr.py, docs/versions.md, "Running POD").
  • [x] Reference races on POD (docs/systems/race-recording.md, "Recordings").
  • [x] Readers and disc tests on POD's files (target/pod).
  • [ ] Every ported subsystem re-checked against POD (physics and controllers done; see the checklist).

Subsystems ​

SubsystemCrate or appOriginalChecked byState (2026-10-25)
Formatspod-formats, pod-containerevery readerevery file decodes and writes back byte-identicalDone
Race rulespod-raceFUN_0049e63c and the track macros (docs/systems/race.md)replays POD's six reference races to the same records and order; the order between gates against the game's own path stateDone on POD, the order between gates included; the human's per-frame tracker FUN_004c688c not ported
Car physicspod-physicsFUN_0041b66c and its callees: engine, gears, brakes, tyres, suspension, collisionseach function emulated on recorded car states, bit for bit; a recorded race replayed from its inputsOn POD: the step and its callees ported (docs/systems/physics.md); every function matches POD's calls under Unicorn, every car step of two step-logged POD races matches (Beltane 8,328, Canyon 18,600 with jumps), and the port follows both whole races on its own with car contacts; damage and the track's props ported and measured under Unicorn
Opponentspod-aithe controller at driver +0xc (0x6345d8 + n × 0xb0), the path graph, the rule drawscontroller outputs emulated on recorded states; draws in orderPorted (docs/systems/controllers.md): matches POD's WINPOD.EXE under Unicorn on random states and eight-car races, and on EASY and MEDIUM races read from POD in the emulated PC (4,200 steps), and POD's own step logs (19,194 controller calls)
Player inputpod-inputDirectInput keyboard and joystick reads, the controller for the playerrecorded key presses give the same controller outputsPorted in pod-ai (human): matches POD's FUN_00415f9c under Unicorn for the keyboard and the three joystick kinds; not yet from keys recorded in the game
Game appapps/unpod, pod-gamethe main loop, frame clock, live race loading (docs/systems/main-loop.md)headless one-lap live race reaches results; effects and CD streams captured; race HUD inspectedLive races use POD physics, controllers, effects and CD audio. The lower-left damage bars and lower-right radar now draw; their values/markers are approximate, and the upper-right inset remains blank. Menu-to-live-race integration and full-frame renderer parity remain open.
Front endpod-uithe menu tree, its screens and text, options, car and track choice, the records screenscreen-by-screen comparison with POD captures; same Ubi.ini and .BRT filesMain menu, one player, options, track pages, car, pause, quit warning and records match captures pixel for pixel. Championship and time-attack flows are implemented; some pages still need capture comparison, and .prb writer parity remains open.
Rendererpod-renderPOD's software renderer (docs/systems/render.md)compare complete 640 × 480 frames with the originalMeasured ports now cover tables, shade and coverage, textured spans, polygon fillers, clipping, transform/projection, horizon lists/spans and the cloud span kernel. Un-POD draws the track horizon behind the scene with its stand-in camera. The rest of the cloud pass is not measured or ported. Sun flare measurement/port, triangle faces, some affine drawers, and the complete frame path remain open.
Soundpod-soundthe waveOut mixer, engine sound, effects and CD musicmixer calls measured sample for sample; local CUE output captured during a live raceThe race loop was traced to POD's car slots 8/10 pointing at missing events 21/22, which resolve to the looped tyre-squeal sample; Un-POD now skips missing car events. CD routing and the effects path run. A fresh POD race mix still needs sample-level comparison.
Savespod-formats (records)load_records, save_records, Ubi.inifiles written by Un-POD load in the originalReaders and writers done

The end-to-end test (2026-10-24, evening) ​

The user's goal: a fully running game, tested from start to finish with video and every renderer.

  • Un-POD: start-up videos (logo, intro) → main menu → track and car → race with live physics, opponents, sound and music → results → championship-end videos → back to the menus. Run with each renderer: the stand-in (apps/unpod raster) and pod-render (POD's software renderer), at 640 × 480 and upscaled; natively and in the browser. One scripted run per renderer (apps/unpod --headless --probe), screenshots at fixed points, a pass/fail per stage.
  • The original: all 14 POD builds through GameFrame's wrappers with the clean-room Indeo codec installed and the intro on: videos → menus → a full race → the results screen.
  • Every stage that fails is a task for its owner; the table below says who.

Next tasks (2026-10-25, afternoon) ​

One line per session, from its latest report. Each session keeps its own line current.

SessionNext task
App (apps/unpod)Live race from POD DATA reaches results with physics, opponents, collisions, damage, keyboard input, sound effects and CD audio. One-lap autopilot and captured HUD frames verified. The lower-left bars and lower-right radar draw; their exact gauges and markers remain open. Next: trace the upper-right inset, compare the radar and damage display against POD captures, and measure the chase camera FUN_0042b6c0.
Renderer (pod-render)Measured: spans, tables, shade bands, coverage mask, .BV3 car palette, polygon fillers, clipping, transform/projection, horizon lists and spans, and the cloud span kernel; Un-POD draws a race with the measured lower layers and track horizon (UNPOD_RENDERER=pod-render). Next measure and port the cloud layer projection and fillers, then measure and port the instruction-traced sun flare, triangle face loop, lit vertex sources and affine fan drawers. Wire the measured transform into Un-POD's scene path, then compare complete frames.
Physics (pod-physics)Done on POD: the step, collisions with cars and props, damage and its record for the race display (docs/systems/physics.md). Open: zones (FUN_00456dd4), a car put back after 2.5 s upside down, and a point with no sector (FUN_0045ccd8); none in any capture yet
Front end (pod-ui)Done 2026-10-24: 19 POD captures match pixel for pixel; the end-to-end path in pod-ui: start-up videos (Ui::start), main menu, track, car, race, both race-end pages, back to the menus; the standard championship to its end with finpod/credits or finpod2 (Effect::Video) and back (tests/flow.rs). 2026-10-25: championship saves (Data\ChShip\POD<n>.<kind>, menus 40, 45), the race end's championship pages (menu 60) and next track (menu 20), the custom and random championships (menu 19, Watcom rand), one championship model (laps 0x633330, mirror mask 0x4fdf16 on Game). time attack (menu 18, its three types and laps, race ends 7/3, 6, 5 and their pages; the Top 5 ghost panel, .ght ghost bundles, the racer table; the track screen's mirror and menu_back); best times read, inserted and written back as POD does (crate::best_times, measured under Unicorn), ghost import and export, the mirrored map and the typed mirror code, menu 21 and menu 60's team pages. Next: the championship and time-attack screens against captures (asked of race-loop); the .prb writer against a game-written file
Sound (pod-sound)Capture a fresh POD race with the CD stream and effects separated, then compare the decoded samples with Un-POD's live-race output; list the car-code sounds each race fires
Race rules (pod-race)Done on POD (all checks on the three POD races; the mid-lap order check on the recorded path state). Going the wrong way, the place and fold searches, and the human's per-frame follower FUN_004c688c with its wrong-way bit (in pod-ai, pod_ai::human_path) ported and measured. Next: the order update's branch for a human in a view; the results text once a POD recording reaches the results screen (asked of race-loop); a replay of the follower needs every frame's position
Controllers (pod-ai)Done against POD's measured states, EASY and MEDIUM races, and step logs; no new recording needed
Race loop (VM owner)Fix the VM after its restart (the CD drive's audio, the disc's new drive), then the queue: pod-sound's Beltane take cut and copied and the FACTORY race; POD's 3dfx and D3D through the wrappers; the extras in the 3dfx build; the garage SAVE for pod-ui; step logs for controllers; a damage-on race for physics. Between requests, the main loop in pod-game
ExtrasDone on POD's software build and in Un-POD (docs/extras.md); the fan tracks' grids checked: Tron and Arcade race since the .BL4 face-word fix, Beach waits on the game's chase camera (FUN_0042b6c0, app or renderer). Next: Tron and CanyOEM in POD's 3dfx build (in race-loop's queue)
Wrapper (now GameFrame's wrappers/)Run POD's 3dfx and Direct3D builds through GameFrame's wrappers (race-loop's queue, staged with run/wrappers/stage.sh); fix what they show in a GameFrame worktree
PowerVR (GameFrame's wrappers/sgl)POD's PowerVR build races at 1280 × 960; fog, shading, projection and light-volume placement follow PowerVR's own driver (SGLMID4.dll, docs/hardware/3d-cards.md). Menus are 640 × 480 by the game's own choice; only the race takes 800 × 600. Next: where the driver places an unplaced light, a light volume's strength, a night race to see the headlight beams, a race at 800 × 600
Track editorDone on POD (2026-10-24): all 66 track files save byte for byte; edited tracks install into a game folder (--install, --as for a new track) and race with unpod --content; props added and removed, beacons and the racing line moved, each raced in Un-POD (docs/modding.md). Next: the animation library's instances, saving in the browser, props for tracks without an environment, the reverse race's beacons and paths
Mod layerVehicle creator done on the command line (docs/vehicle-creator.md): parameters, settings, curve, tint, scale, both files byte for byte, raced in Un-POD. A new car beside the sixteen (--new, installed by pod-extras), single vertices and faces, pages from PNG files done. The window (a panel in the viewer, create) done. Next: picking vertices and faces with the mouse, saving from the browser

Order ​

  1. Physics, opponents and input in parallel, each against recordings of the original. With them the race runs on its own, and pod-race checks the result.
  2. The game app: a window, the loop and the clock, the race drawn with the viewer's renderer, a HUD. A whole race playable in Un-POD.
  3. Front end and sound: menus to results, with sound and music.
  4. Renderer to 1:1: the software renderer, frame for frame.
  5. The comparison suite: the same recorded inputs played into the original (in the emulated PC) and into Un-POD, for every track, with races, menus and saves compared.

How the work is done ​

Each subsystem is one session in its own worktree. It follows GameFrame's loop: trace, emulate, port, verify. It writes a diary entry as it goes, with screenshots, and adds what it learns to docs/knowhow/ and docs/gameframe-lessons.md. It merges into main early.

Rust first, one owner for the emulated PC (the user's decision, 2026-10-24). One session, the race-loop session (Un-POD core), owns the emulated PC (run/vm): it runs POD there and records races, step logs and captures only when another session asks for one. Every other session writes Rust in the game, against the POD recordings already on main (derived/race/pod-*, derived/ui/pod/), and asks the owner for a new recording instead of starting the VM itself.

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