Sets up the foundation for reimplementing the PS2 game in Rust rather than emulating it. Nothing is implemented yet; this is the scaffold, the verified target and the working agreements that keep a long project coherent. Target disc, verified against a real image: SCUS_972.68 v1.01, USA Black Label, NTSC 60 Hz boot ELF 2,618,684 bytes, SHA-1 bacda860...d098a77, entry 0x00131AE8 The entry point independently matches the value recorded by the rac2-gc-decomp community reference, which cross-confirms the identification. This is the exact build the upstream research pins; Greatest Hits v2.00 is a different binary and is not the target. NTSC matters beyond label compatibility: PAL discs run 50 Hz, which would make chargeboots and other speed-sensitive movement run at 5/6 speed. Verifying the disc before writing movement code avoids discovering that in Phase 5. Contents: - Five-crate workspace (formats, extract, game, video, engine) on Bevy 0.19.1, matching the version ReRAC uses so its Graphics Synthesizer and VU work stays portable. `cargo check --workspace` is clean. - tools/identify-disc.py, which reproduces the identification against any disc image and distinguishes a truncated dump from a non-PS2 image. - .githooks/pre-commit, refusing game data from entering git history: disc image extensions, asset directories, container signatures at offset 0, and files over 50 MiB. Verified against seven cases including a disc image renamed to look like source. Now that this pushes to a remote, this matters more than it would have locally. - AGENTS.md, the operating agreements: no game data ever, reproduce the PS2 float model rather than approximating it, bound every read, verify against the real disc. - PROVENANCE.md, recording that this follows ReRAC (ISC) and learns from rac2-gc-decomp (MIT). Wrench is used as a written specification only and no GPLv3 code is copied, since that would relicense everything derived from it. RAC2Decomp is explicitly not used: its provenance is disputed in the upstream community reference, and it targets the wrong region. - docs/roadmap.md, phased so the riskiest unknowns are retired early. The two that matter: overlay relocation transfers from ReRAC and makes 27 level overlays tractable, while VU1 microcode is binary and is the hardest single item. Phase 5 is a deliberately scoped Veldin vertical slice as the go/no-go.
194 lines
7.1 KiB
Markdown
194 lines
7.1 KiB
Markdown
# Roadmap
|
|
|
|
Status as of **2026-10-09**.
|
|
|
|
A from-scratch native Rust reimplementation of Ratchet & Clank: Going Commando
|
|
(PS2). Each phase ends in a **gate** — something checkable, not a feeling that
|
|
it is going well. Phases are ordered so that the riskiest unknowns are retired
|
|
early, while they are still cheap to retire.
|
|
|
|
## Where we are
|
|
|
|
- [x] Target disc identified and verified (`SCUS_972.68` v1.01, NTSC 60 Hz)
|
|
- [x] Workspace builds (`cargo check --workspace`)
|
|
- [x] Repository hygiene in place (pre-commit guard, `.gitignore`, licence
|
|
record)
|
|
- [ ] Nothing else implemented yet
|
|
|
|
The disc being the exact build the reference projects pin is the single
|
|
biggest favourable fact so far: no disc conversion, no regional timing work,
|
|
and no re-derivation of the reference baseline.
|
|
|
|
---
|
|
|
|
## Phase 0 — Foundation ✅
|
|
|
|
**Done.**
|
|
|
|
- Disc identified: `SCUS_972.68` v1.01, USA Black Label, NTSC 60 Hz. Boot ELF
|
|
SHA-1 `bacda860cbd6723142e86db56f197e47e728970f`, entry `0x00131AE8` — the
|
|
entry point independently matches the value recorded by `rac2-gc-decomp`,
|
|
which cross-confirms the identification.
|
|
- `tools/identify-disc.py` reproduces the identification against any disc image
|
|
and distinguishes a truncated dump from a non-PS2 image.
|
|
- Five-crate workspace scaffolding, Bevy 0.19.1 pinned, builds clean.
|
|
- `.githooks/pre-commit` blocks game data. Verified against seven cases:
|
|
`.iso`, renamed disc image, WAD container, ADX container, `extracted/`,
|
|
a 60 MiB file, and legitimate source.
|
|
- `AGENTS.md` and `PROVENANCE.md` record conventions and licences.
|
|
|
|
### Open from this phase
|
|
|
|
- `alsa-lib-devel` is not installed, so Bevy's host audio backend is disabled in
|
|
`Cargo.toml`. Audio is the last phase; revisit then.
|
|
- Bevy feature set is deliberately minimal. Re-add features as needed rather
|
|
than switching `default-features` back on wholesale.
|
|
|
|
---
|
|
|
|
## Phase 1 — Disc pipeline
|
|
|
|
**Goal:** turn the disc image into verified, unpacked asset files.
|
|
|
|
- [ ] `iso9660` reader (R&C2 lists assets as real files, unlike R&C1)
|
|
- [ ] `RC2.HDR` table of contents parser (408,444 bytes at disc root)
|
|
- [ ] WAD LZ77 decompression
|
|
- [ ] Unpack global WADs (`G/*.WAD`) and per-level WADs
|
|
- [ ] Commit a size+SHA-1 table per target file (hashes only, never content)
|
|
- [ ] `verify` stage
|
|
|
|
**Gate:** every extracted file matches the committed hash table, and the
|
|
decompressed size of every WAD stream matches its header.
|
|
|
|
**Note:** the three MPEG WADs total roughly 2.4 GB of the 3.8 GB disc. Video is
|
|
a data problem before it is a decoding problem — plan to stream.
|
|
|
|
---
|
|
|
|
## Phase 2 — Format parsers
|
|
|
|
**Goal:** every asset type parses into plain Rust values, tested against real
|
|
disc data.
|
|
|
|
- [ ] `tfrag` — terrain: LOD chains, strips, morph targets
|
|
- [ ] `tie` — instanced environment geometry, fat vertices
|
|
- [ ] `shrub` — foliage and decorative meshes
|
|
- [ ] `moby` — dynamic actors; `moby_anim` — skeletal animation
|
|
- [ ] `collision` — cells, packed vertices, faces
|
|
- [ ] `occlusion` — the PVS grid
|
|
- [ ] `sky`, `volumes`, `textures` (CSM1 CLUT + PS2 alpha)
|
|
- [ ] `level_overlay` — the seven-section parser and relocation from Phase 3
|
|
|
|
**Gate:** every parser has a golden test against bytes from the real disc, and
|
|
reports mismatched header counts as failures rather than warnings.
|
|
|
|
**Discipline:** bound every read. Typed errors, no panics, no reads past the
|
|
buffer.
|
|
|
|
---
|
|
|
|
## Phase 3 — Overlay system
|
|
|
|
**Goal:** make 27 level overlays tractable.
|
|
|
|
Every overlay carries the same seven sections (`.lit`, `.bss`, `.data`,
|
|
`lvl.vtbl`, `lvl.camvtbl`, `lvl.sndvtbl`, `.text`) and the same engine code,
|
|
linked at different addresses. ReRAC solves this by masking relocations — `j`/
|
|
`jal` targets, `lui` immediates of main-RAM addresses, the `%lo` that completes
|
|
them, and `$gp`-relative operands — then matching the masked words to find a
|
|
reference function's copy on another overlay.
|
|
|
|
- [ ] Port the masking and code-identity logic
|
|
- [ ] `Relocation::func` and `Relocation::data`
|
|
- [ ] `$gp` for this game (`0x001AEFF0`, against R&C1's `0x166c00`)
|
|
- [ ] Extend the level table from 19 to 27
|
|
|
|
**Gate:** every level-01 function's relocated copy is found on all 27 overlays;
|
|
cross-checked against an independent cluster hash.
|
|
|
|
**Why this is high-value:** it turns "27 overlays of unknown code" into a table
|
|
lookup, and it is the main thing inherited from ReRAC.
|
|
|
|
---
|
|
|
|
## Phase 4 — Engine core
|
|
|
|
**Goal:** draw a frame.
|
|
|
|
- [ ] Graphics Synthesizer in wgpu, with the GS's own rules: its
|
|
`round(log2(z/n) + K)` mip selection, blend and alpha-test behaviour,
|
|
alpha `0x80` meaning 1.0, CLAMP wrap
|
|
- [ ] VU0/VU1 skinning and lighting on the PS2 float model
|
|
- [ ] LOD/morph/cull per `TfragProc` and `TieProc`
|
|
- [ ] Occlusion culling, fog, shadows
|
|
- [ ] GIF/VIF packet stream interpretation
|
|
|
|
**Gate:** CPU and GPU paths agree on every vertex of a reference level — the
|
|
same identity check ReRAC runs.
|
|
|
|
**Risk:** VU1 microcode is **binary** in the game's `.vutext` section, so it is
|
|
reverse engineered rather than parsed. This is the hardest single item in the
|
|
project and the main threat to the timeline. Note that ReRAC's own moby VU1
|
|
analysis trips their disassembler on two opcodes.
|
|
|
|
---
|
|
|
|
## Phase 5 — Veldin vertical slice
|
|
|
|
**Goal:** one planet, playable start to finish.
|
|
|
|
- [ ] Ratchet's controller on a **spherical** planet
|
|
- [ ] Chargeboots: hold/release timing, chained jumps, dash
|
|
- [ ] Wrench and the camera
|
|
- [ ] Collision and the HUD
|
|
|
|
**Gate:** Veldin is completable and visually matches PCSX2 frame-for-frame under
|
|
replayed input.
|
|
|
|
**This is the go/no-go for the project.** If Veldin works, the other 26 levels
|
|
are content. If it does not, the reason is discovered while it is still cheap.
|
|
Spherical gravity is the thing most likely to break, because it invalidates
|
|
R&C1's movement and camera assumptions wholesale.
|
|
|
|
---
|
|
|
|
## Phase 6 — Remaining overlays and the shell
|
|
|
|
- [ ] Per-level moby class dispatch via `lvl.vtbl`
|
|
- [ ] Remaining 26 levels
|
|
- [ ] Planets, travel, the Gadgetron vendor, RPG upgrades
|
|
- [ ] Weapon modification bitmasks (lock-on, shock, acid, …)
|
|
- [ ] Minigames and challenges
|
|
- [ ] The 25-state memory-card FSM and its UI
|
|
- [ ] IFF chunk saves, including import of a R&C1 save to unlock legacy weapons
|
|
|
|
---
|
|
|
|
## Phase 7 — Audio and video
|
|
|
|
- [ ] 989snd v2 audio decode and playback (not CRI ADX — a different codec)
|
|
- [ ] SPU2 mixing, reverb zones, level sounds
|
|
- [ ] IPU/MPEG-2 video for the title screen and cutscenes
|
|
|
|
---
|
|
|
|
## Phase 8 — Fidelity pass
|
|
|
|
Differential testing against PCSX2 across every level:
|
|
|
|
- [ ] Recorded-input frame comparison, per planet
|
|
- [ ] Geometry, fog, blend, alpha, texture sampling
|
|
- [ ] Audio sync and 60 Hz timing
|
|
- [ ] Behavioural comparison: traversal routes, boss timing, RNG
|
|
|
|
---
|
|
|
|
## Principles
|
|
|
|
- **A gate is pass/fail, not a percentage.** "Looks fine" is not progress.
|
|
- **Negative results are written down.** A ruled-out approach saves the next
|
|
attempt as much as a working one.
|
|
- **The float model is not negotiable.** Approximate is not the same as
|
|
faithful, and the difference is visible in how the game feels.
|
|
- **Fidelity over tidiness.** Where the original does something that looks like
|
|
a bug, reproduce it and say why in a comment. See `AGENTS.md`. |