Skip to content

feat: slots and gate driver for functional C - #342

Merged
Veradictus merged 10 commits into
masterfrom
prep/fcheck-gate
Oct 28, 2026
Merged

Veradictus merged 10 commits into
masterfrom
prep/fcheck-gate

Conversation

@Veradictus

Copy link
Copy Markdown
Member

This sets rac2 up for a C backfill: functional C for the native port, kept apart from matching C and never counted.

  • nonmatching/functional/ and nonmatching/functional.tsv (empty) use the same layout as rac1-decomp. tests/check_repository.py now checks that every listed function is still INCLUDE_ASM in src/ and that its file exists. tools/progress.py reads only src/, so nothing listed here can be counted.

  • tools/fcheck.py is the gate's driver for boot functions. It compiles a candidate with its unit's own flags and places the function at a scratch address, along with:

    • its own read-only data and jump tables;
    • $gp references, range-checked;
    • literals;
    • static helpers.

    OpenRAC's test-only interpreter then compares it with retail on captured calls. It has 11 tests in tests/test_fcheck.py.

  • nonmatching/README.md explains how to run the gate.

The OpenRAC side is OpenRAC/OpenRAC#85. Calibration: the 513 matched boot functions reached by the menu and New Game runs give 502 SAME, 0 DIFFERS and 11 unchecked. The progress report is not touched here.

Add nonmatching/functional/ and an empty functional.tsv for C that behaves
like the retail function without matching it, for the native port only.
check_repository.py requires each listed function to still be INCLUDE_ASM in
src/ with its file present; progress.py reads src/ only, so nothing listed is
ever counted.
Compiles a function's unit with its own flags (a candidate in place of its
INCLUDE_ASM line or C, else on its own), places its section at 0xA0000 with
every relocation resolved against retail: the boot build's derived values,
the unit's writable data where retail has it, read-only data and jump tables
copied with the code, gp-relative references kept inside $gp's window.
Captures call states headless from the US disc (menu pages and New Game up
to the first level's load, phased and spread) and runs openrac-fcheck.
@bytematchdev

bytematchdev Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

ByteMatch progress report

SCUS_972.68

Code matched 45.04% · 2.49 MB of 5.53 MB
Fuzzy match 45.04%
Functions 5,623 / 8,584
Data matched 0.00%
Units complete 618 / 734

No function changes.

SCES_516.07

Code matched 15.01% · 856.52 KB of 5.70 MB
Fuzzy match 15.01%
Functions 4,412 / 8,677
Data matched 0.00%
Units complete 571 / 927

No function changes.

SCUS_972.68_v2

Code matched 23.63% · 1.32 MB of 5.58 MB
Fuzzy match 23.63%
Functions 5,304 / 8,639
Data matched 0.00%
Units complete 636 / 891

No function changes.

Commit 32e8cd7 · Ratchet & Clank: Going Commando on ByteMatch

@llesieur99 llesieur99 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed draft head 0d133e4802954709188758e6a98b395e47f6e934. The isolated functional-source layout is supported by current RAC1 source and its active build graph; matching credit remains separate. I am not rejecting that layout or the use of the RAC1 method. The driver needs these fail-closed fixes before acceptance-gate readiness:

  1. Return contract: tools/fcheck.py:445-472,550,633 infers the comparison bank from candidate spelling. A typedef-float is classified as int; aggregate/missing definitions can become void, and a candidate void declaration can disable the retail return comparison. Resolve or reject unsupported contracts and use an independently established retail ABI.
  2. GP address construction: lines 433-434 treat address-taking as a four-byte scalar load. An eight-byte constant object [1,2] can be mapped to an earlier [1,9] object sharing its first word. Map the complete referenced span or reject that relocation shape.
  3. Containment: tests/check_repository.py:156 accepts nonmatching/functional/../game/helper.c. Canonicalize and enforce the functional-root boundary, including symlink escapes.
  4. Capture status: lines 596/690 discard a failed capture subprocess status and return CLI success. Propagate failure distinctly and do not reuse stale snapshots as a completed fresh capture.

The 11 supplied tests pass; five independent synthetic negative controls reproduce these defects (two return-policy controls). No real game/interpreter execution or independent calibration of 502 SAME/11 unchecked was claimed. The return heuristic is inherited from current RAC1, which makes it important to fix rather than proving it correct. Keep this PR draft until these controls pass.

C that does what retail does, for the native port; never counted. Each is SAME under
tools/fcheck.py on every call state captured in the menu and New Game runs (USA v1.01).
The gate's verdicts are behaviour on those calls, not proof of every path.
A variable the candidate defines under a retail name is at that address even
when its reference names the section (a static, an initialised small-data
global), and a $gp-relative addend to a local symbol counts from the object's
gp0. D_XXXXXXXX_sda and SDATA's __gp aliases are the variable they name. A
name nothing else places, a near miss's ref_LNN_XXXXXXXX_kk, takes the value
retail's bytes give it under the candidate's relocations, aligned by
instruction kind.
…karounds removed

func_00132498, func_001325E0, func_002B85F8 and func_00300AA8 are SAME at the
gate now that it compares only a call's arguments; func_00300AA8's gp-relative
globals are at their real addresses. The committed drafts drop what worked
around the old gate: hardcoded addresses become named globals
(func_001334B8, func_00133580, func_00132C48, func_002B88F0), constants held
in locals and leftover-register arguments go (func_002B68C8, func_00132498),
the frame-size helper goes (func_002B85F8), the program's end is its ref_
name again (func_00290B48), and func_00128FD0 passes the 32-bit tag its
callee stores instead of a 64-bit word built on an unset $s4.

@llesieur99 llesieur99 left a comment •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed exact head b9b2ca9, including the two new tooling commits. Please keep this PR draft until these corrections are qualified.

  1. The inline matrix finding remains: func_002E76C0 declares a 16-byte m2 object but its helpers use 48 bytes. Correct the actual object extent/alignment and recalibrate.
  2. All five independent negative controls still fail on this updated driver: typedef-float return becomes int; an unsupported aggregate return does not fail closed; GP address-taking selects a four-byte prefix instead of the complete readonly object; the functional path validator accepts a ../ escape; mocked capture exit 7 becomes successful CLI exit 0. These are fresh synthetic controls, not live-game false-SAME claims.
  3. The new retail_references call handling at tools/fcheck.py:315-319 clears only RA. A synthetic callee overwriting caller-clobbered v0 leaves the caller's earlier high-half provenance live in the enumerator, which then reports a false data address. Clear caller-clobbered provenance at calls unless a separately established contract proves preservation, and handle indirect calls conservatively. Add a regression control before using these references as alignment evidence.
  4. The actual TextureBits candidate (func_0027C878) still resolves to int return mode although it declares the EE 64-bit unsigned-long type. Resolve verified return contracts or reject unsupported aliases. Only this driver mode mismatch is independently demonstrated: the exact gate backend is unavailable in this review environment, so I do not claim that its comparison necessarily truncates the upper half or produces a false SAME.

The 13 supplied fcheck tests, full 299-test suite, 99-row contained index, repository and progress checks pass. Matching credit remains isolated. No independent live capture/calibration or exhaustive semantic audit of the other function bodies is claimed; those passing checks do not close the concrete source and negative-control findings above.

{
int m0[4];
int m1[4];
int m2[4];

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Allocate the complete matrix object. m2[4] contains 16 bytes, but func_00283958(f, m2) at line 58 writes three 16-byte rows at destination offsets 0, 0x10 and 0x20. The following func_00283738 and func_00283098 also use that 48-byte matrix. The retail caller reserves sp+0x20 through sp+0x4F for it. This overruns the declared object by 32 bytes; adjacent arrays or a captured matching stack layout do not make that valid. Use a truthful, suitably aligned 48-byte matrix and recalibrate.

A level00 capture script plays New Game into level 00 (Aranos) to frame 7000: through the
intro, then running with the left stick, jumping, the wrench, the pause menu, weapons and the
map, in four phases. A function with a place in level 00's program is captured there, the CRC
guard keeping the boot's code at the address before the load out; a resident boot function
(below 0x1A7C80) at its boot address.

check groups a function's states by the program they are of and builds it once for each: in a
level its symbols resolve at the level's addresses (its catalogue places, the level build's
placed.json, the boot's resident values, boot addresses the level moved read from the level's
copies of boot code, a boot function's own copy first), against the level's bytes. build takes
--level, capture lists may give states per phase, and check --retail runs retail's own bytes as
the candidate, the harness's self-test.
@Veradictus
Veradictus marked this pull request as ready for review October 28, 2026 03:16
@Veradictus
Veradictus merged commit 85614b4 into master Oct 28, 2026
1 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants