4 Commits
Author SHA1 Message Date
ReaperOfVeriod 615ff937ea Decomp advanced 18 commits to 8a174cb; 14 new matched functions land in our .text
Checked upstream as asked. The vendored checkout moved from aca4f59 to 8a174cb,
18 commits ahead of origin/master.

What the update is: all 18 commits are src/game function-matching work. No header,
no docs, no config, and crucially no change to config/levels/functions.tsv -- so
the function extents and level catalogue this project depends on are unaffected.

What is usable: 14 of the newly matched functions fall inside the overlay .text
(0x2a3b80-0x447900), which makes them decomp addresses verified against the same
code this project runs:

    002A7750 002AA800 002B3868 002B6120 002B7E28 002C7748 002CD8C0
    002D5E98 002DDEC8 002DF1B8 002E7018 002F5220 002FC8D0 00300618

That matters because PROVENANCE.md records exactly this split: the decomp's struct
layouts were usable while its function addresses were not (they failed for the moby
class loader). Addresses that are now matched inside our own overlay are the
reliable kind, and 20655c7 needs precisely that to follow UpdateCamera_6 out of
lvl.camvtbl without repeating the mistake that cost the moby_anim chase.

What it does not give: none of the 14 is camera, frustum or GS code. The bodies are
game logic and toolchain-status material -- one is a header-offset relocation pass,
another raises a flag and forces a state word out of {6,7}. So the frustum hunt
does not advance from this, and the next step remains verifying UpdateCamera_6's
address against the overlay and reading the function that writes 0x18aa70.

Vendored checkout is gitignored, so moving it changes nothing committed except the
PROVENANCE note recording the pin and what was in the update.
2026-10-15 03:33:31 +02:00
ReaperOfVeriod f182a4a0ac Transcribe the 256-byte MobyInstance; oClass closes the instance-to-routine loop
The decomp carries an authentic MobyInstance layout of 0x100 bytes with every
field named. It is now in gc_formats::moby with a 67-entry offset map, attributed
as a transcription rather than presented as our own finding.

Two fields matter:

    0x40  animSeq      animation sequence pointer
    0x44  animSeqT     animation time parameter
    0x48  animSpeed    playback speed
    0x50  animLayers   animation layer list
    0x57  jointCnt     joint count -- non-zero means the moby is skinned
    0x58  jointCache   joint matrix cache
    0xBC  oClass       object class id -- the dispatch key

OCLASS CLOSES A LOOP THAT HAS BEEN OPEN FOR SEVERAL COMMITS. It is the field the
runtime uses to reach the lvl.vtbl handler tables, so an instance and its routine
are now linked by a named offset, rather than by an assumption about which axis
the two tables share. That assumption -- the positional correspondence -- is still
refuted, and still does not need to be true.

jointCnt and jointCache settle something from earlier in this log BY CONTRADICTION
rather than confirmation. A tie vertex's i16[3] was tested as a skin/joint index
and disproved: vertices sharing it were 0.95x FARTHER apart than vertices differing
in it. That stands and is unaffected -- this is the moby struct, and a moby is a
different object from a tie. Recording the distinction rather than letting "joints"
appear in two places look like a confirmation of the earlier hypothesis.

WHAT THIS DOES NOT DO IS FIND moby_anim. It names the instance-side state, so the
asset to locate is whatever those pointers reference, and we still do not know
where that is. The item's unblock condition is now sharper -- it needs the data
behind those pointers, not the struct.

PROVENANCE, AND WHAT THE TEST CAN ACTUALLY CHECK

This is not verified against disc data and cannot be: moby instances are runtime
objects and nothing of that shape is in the container. The test asserts what can be
asserted without the bytes -- offsets ascend, do not overlap, the last field lands
exactly on 0x100, and o_class sits at 0xBC immediately before triggers.

Worth keeping distinct from the collision finding. That project's FUNCTION ADDRESSES
did not transfer: they landed in NOBITS memory. A struct's field order is a
different kind of claim from an address in a program's layout, so one failing does
not impeach the other -- but the distinction is real, and PROVENANCE.md now records
which is which rather than citing the project as a single authority.

268 tests.
2026-10-14 03:47:15 +02:00
ReaperOfVeriod 1f903efe23 An external decomp turns two overlay constants from measured into named
OpenRAC/rac2-gc-decomp is the same disc as ours, verified rather than assumed:

  ours    3,828,350,976  9db2e33e...b1ce5
  theirs  3,828,350,976  9db2e33e...b1ce5

Vendored read-only under vendor/ (already gitignored) and added to PROVENANCE.

It names two constants this parser had measured but never explained:

  * overlay.bin +0x00 = 1,735,808 = 0x1A7C80, the .lit load base. It is constant
    across all 27 levels because every level's literal section loads at the same
    address. So the first header word is the load base of the section the file IS,
    which also establishes that overlay.bin is that section -- it holds both code
    and the debug string pool. $gp = 0x1AEFF0 comes with it, and that closes the
    roadmap's open "$gp for this game" item.

The program around it is seven sections -- .lit .bss .data vtbl camvtbl sndvtbl
.text -- which is precisely the "seven-section parser and relocation" Phase 3
item for this format. Known target now rather than a guess.

I then tested the obvious follow-up and it is wrong: if +0x00 is the .lit load
base, +0x0c ought to be the .lit size. It is SMALLER than the file on 16 of 27
levels, by up to 8.9% (level11: file 3,151,128 vs 2,870,792). Excluded by sign,
not by a near miss. Still unnamed.

Collision: still blocked, but no longer blocked on tooling. The decomp names
Camera_CollPrimTest(void *prim, void *grid) without decompiling it, so the cell
layout is not handed over. What it does supply is $gp, and that is enough to
start. "Camera_CollPrimTest WARNING! - grid out of bounds! (RAR)" is at
overlay+0xd18, the only copy in the file, so:

    string  overlay+0xd18 -> RAM 0x1a8998;  delta to $gp -0x6658;  $lo 0x99a8

MIPS puts the $lo half in bytes 1..3 of a 4-byte instruction. Searching the whole
overlay instruction-aligned gives exactly ONE hit:

    0x354cc8    (overlay+0x1ad048)

One occurrence in 2.75 MB of code is unambiguous: that is the instruction
materialising the assert string, and its enclosing function is
Camera_CollPrimTest. That is the route in, and it needs no symbols.

tools/overlay-anchors does this step, dependency-free. I started this with
capstone for the disassembly proper and dropped it: capstone 0.14 does not
re-export Mode/Endian from a public path, and an excluded-from-the-workspace
native dependency is a poor trade for a step that does not need one yet. The
anchor search is pure byte work, so it is pure Rust. No native dependency is
added anywhere, and the tool is an ordinary workspace member.

Surveyed the rest of that project against our open items: no volume structures,
no tie vertex layouts, no SBlk/DAW fields, no wavelet tables. It is a guide to
the code, not a replacement for reading it.
2026-10-13 07:07:21 +02:00
ReaperOfVeriod a02f79a6d1 Initialise native Rust port of Ratchet & Clank: Going Commando
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.
2026-10-09 15:42:10 +02:00