Match 600+ P2 functions & VU0/splice foundations#263
Draft
lindskogen wants to merge 125 commits into
Draft
Conversation
scripts/decomp_lhf_draft.workflow.js fans out parallel read-only agents that draft matching C for batches of tiny leaf functions. scripts/decomp_apply_drafts.py verifies each draft in isolation (instructions + relocations vs the asm), rejects ones that grow non-.text sections (rodata-layout ripple) or whose raw FUN_ symbol has asm callers, then applies the byte-exact matches and renames FUN_ symbols. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
AddWaterExternalAccelerations dispatches through the SO vtable (offset 0x128) via STRUCT_OFFSET; FUN_001ef830 clears zpd.cploThrow. Annotate the remaining INCLUDE_ASM stubs with why they're deferred (VU0 SIMD, or permuter-class codegen for PostWaterLoad/HandleWaterMessage). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Small getters/setters and helpers on ALO/SO/SW: interact/look-at/throb/ack accessors, FInflictSoZap, UpdateSoPosWorldPrev, and SW list/world helpers. Base-struct fields past the truncated structs are reached via STRUCT_OFFSET. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Small wrappers/getters/setters across act, chkpnt, cplcy, difficulty, dmas, path, po, puffer, pzo, rog, sb, sensor, stepguard, and xform, drafted by the batch workflow and verified byte-exact (instructions + relocations). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
VU_VECTOR was a 2-byte stub { ushort data; }, so passing it by value compiled
to lhu instead of lq. It is a 128-bit VU quadword passed in a single 128-bit GP
register, so define it as { qword data; } (TImode, 16 bytes, 16-aligned). Kept
as a struct named VU_VECTOR to preserve the 9VU_VECTOR by-value mangling.
This resolves the long-standing 89.47% "single load mismatch" in
ClipVismapSphereOneHop, now matched as plain C. VU_VECTOR is only used by value
in signatures (never embedded), so the size change ripples nothing.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
VU_FLOAT was a 2-byte stub { uint16_t data; }; like VU_VECTOR it is a 128-bit
VU scalar (the VU_FLOAT(float) ctor stores it with sq, and it is passed by value
in a single 128-bit GP register). Define it as { qword data; }. Used only by
value in signatures, so the size change ripples nothing; full checksum OK.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
VU_VECTOR was a 2-byte stub { ushort data; }, so passing it by value compiled
to lhu instead of lq. It is a 128-bit VU quadword passed in a single 128-bit GP
register, so define it as { qword data; } (TImode, 16 bytes, 16-aligned). Kept
as a struct named VU_VECTOR to preserve the 9VU_VECTOR by-value mangling.
This resolves the long-standing 89.47% "single load mismatch" in
ClipVismapSphereOneHop, now matched as plain C. VU_VECTOR is only used by value
in signatures (never embedded), so the size change ripples nothing.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
VU_FLOAT was a 2-byte stub { uint16_t data; }; like VU_VECTOR it is a 128-bit
VU scalar (the VU_FLOAT(float) ctor stores it with sq, and it is passed by value
in a single 128-bit GP register). Define it as { qword data; }. Used only by
value in signatures, so the size change ripples nothing; full checksum OK.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Establish the inline-asm VU0 idiom on the small vector/scalar primitives, building on the VU_VECTOR/VU_FLOAT 128-bit type fixes: - VU_VECTOR(const VECTOR&) and VECTOR::operator=(VU_VECTOR): lq/sq quadword copies (plain C through the qword member). - operator*(VU_FLOAT, VU_VECTOR): qmtc2.ni / vmulx.xyzw / qmfc2.ni via inline asm, with VU types passed by value in single 128-bit GP registers ($4/$5/$2, no extra moves). Adds the ctor/operator declarations to vec.h. VU_FLOAT(float) stays INCLUDE_ASM for now (one scheduling instruction off). Full checksum OK. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
First water VU0 function matched: the vadd.xyzw combining the warp result is emitted via inline asm (lqc2/lqc2/vadd.xyzw/sqc2) over VU_VECTOR stack locals; the surrounding ConvertAloVec/CalculateAloTransformAdjust/WarpWrTransform calls and the *pv/*pw quadword copies are plain C. Adds WATER::vecCurrent at 0x570 and references the shared D_00248D30 rodata constant. Full checksum OK. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The sphere-bounds length (dot product + vsqrt) and the AABB vsub/vadd are emitted as one VU0 inline-asm block over the GetWrBounds result and the SO bounds fields (sRadiusBounds/unk_0x3d4/vecBoundsMin/vecBoundsMax). vsqrt has no assembler mnemonic so it's a .word, like the original .s. Full checksum OK. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
LSG's apos/anormal are 16-byte VU vectors; widen them to VECTOR4 and 16-align the struct so sizeof(LSG) is the real 0x70 (au at 0x40/0x44, apos[1] at 0x10). This is the key fix for water's clip-using functions: a typed LSG[16] clip workspace now lands at frame 0x20 (after the edge vectors) instead of the base, matching the original frame layout. No ripple — full checksum OK. With that, UGetWaterSubmerged is written (VU0 vmulax/vmaddx transform, g_pjt height logic, ClsgClipEdgeToBsp into LSG[16], float return) and ~95% matched; kept under SKIP_ASM (asm still used by the real build) pending permuter work on a few g_pjt-branch scheduling instructions. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Byte-exact, checksum-green (out/SCUS_971.98: OK): - PtnfnFromTn (tn): null-check ternary returning &D_00275980 or ptn+0x2F0 - FUN_0014c820 (crusher): indexed pointer into 0xB0-stride array - extern "C" leaf accessors called by other asm via unmangled names: InitCplcy, FActiveCplcy, SetCpmanCpmt, FUN_001e4880, FUN_00145DD8, FUN_001c9a48 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Byte-exact, checksum-green: - RetractLasen/ExtendLasen (sensor): +-1.0f/dt stored at 0xB08 - GetActvalScale (act): 3x qword copy ACTVAL+0x90 -> MATRIX3 rows - SetLookerSgvr (shdanim): store 3 LOOKER field addrs into SGVR - InvalidateSwXpForObject (bbmark): flags &= ~grfpva on pso->sw - FUN_0014c838 (crusher): indexed append into 0x14-stride array Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
These showed t==c "DIFFER" in the dual-build only because their jal targets a still-INCLUDE_ASM sibling (reloc-name artifact); the full link is byte-identical (out/SCUS_971.98: OK): - ImpactClue -> ImpactSo - func_0015F658 -> func_0015F618 - AddStepCustomXps -> AddStepCustomXpsBase Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two tail-call wrappers whose raw C-linkage siblings needed forward declarations (extern "C"). checksum-green (out/SCUS_971.98: OK): - call_search_level_by_id -> search_level_by_id - FUN_001d34e0 -> FUN_001bc4d8 (renamed FUN_001d34e0 -> mangled in symbol_addrs; it has no asm callers) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
UMaxCrv/SMaxCrv (crv): mpicvu/mpicvs[ccv-1]. TouchFlake/TouchBublet/ OnTrailRemove (rip): thin guarded/forwarding wrappers. All showed "DIFFER" only via dual-build artifacts (trailing unsymboled JUNK macros, and jal-to-external-sibling reloc names); full link is byte-identical (out/SCUS_971.98: OK). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
checksum-green (out/SCUS_971.98: OK): - GetActrefScale (act): explicit qword copy of MATRIX3 via loaded ptr (MATRIX3 is float[3][3], align-4; a plain *pmat=*p went unaligned) - AddFrzgObject (frzg): cache coid in a local so it isn't reloaded - FUN_001a93c8 (rwm, extern "C", has asm callers) + FUN_001a86f8 (renamed to mangled; no asm callers) - FUN_001cf138 (stephide, extern "C"): add 1000.0f loaded from D_00274E3C rather than materialized as an immediate Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two leaf functions; checksum-green (out/SCUS_971.98: OK): - GetSuvCpdefi: tail-call forwarding to GetSoCpdefi - SetAsegaSpeed: chained float multiply stored at 0x18 (match.sh's per-symbol check falsely reported DIFFER on both due to flaky configure.py asm regeneration / trailing-junk artifacts; the full-link checksum is the ground truth.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…CrvcCache/DuGetCrvSearchIncrement/MeasureCrvl) checksum-green (out/SCUS_971.98: OK). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
checksum-green (out/SCUS_971.98: OK): UpdateSuvInternalXps, OidFromSmIsms, PostFlyingEmit (+SgnCmpHp), InitDysh (wrapper to InitAlo), FUN_0014f900. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…harness checksum-green (out/SCUS_971.98: OK). Accessors, wrappers and vtable-dispatch leaves across 6 units; size/content-mismatched drafts auto-rejected. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…F harness checksum-green (out/SCUS_971.98: OK). Accessors, vtable-dispatch and forwarding wrappers; size/content/data-growth mismatches auto-rejected. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
checksum-green (out/SCUS_971.98: OK): PushCplookLookk, FUN_0014a8d0, VacateCredit, SetLightHighlightColor, BreakClue, SetShadowFrustrumUp. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Tail wrapper FUN_001aea70(1, 0xFFFF); extern "C" fwd-decl of the raw sibling; renamed FUN_001aec90 -> mangled (no asm callers). checksum-green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
grfrob bit tests on g_plsCur; '(x & 2) > 0' steers GCC to andi+sltu (matching the ROM) instead of the shift-fold. Removed stale 80%% scratch. checksum-green (out/SCUS_971.98: OK). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Builds the freeze group's MRG: allocates apalo, resolves each aoid via PloFindSwChild, and registers the merge group with the SW. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…s hook) SetSoCameraStyle/EdgeGrab/NoGravity/Iceable/IgnoreLocked/NoXpsAll/Self/ Center, UpdateSoPosWorldPrev, AddSoXa, RemoveSoXa, PsoFindSoPhysHook, SetSoConstraints, SendSoMessage — first compiled-C inroads into the so unit, using the reversed SO layout (docs/SO_LAYOUT.md). DiscardSoXps is declared via its literal truncated-mangled symbol since callers pass a discard mode in $a1. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ildSoPhysHook, ConstrFromCnstr) CNSTR becomes an enum (mangles identically as 5CNSTR) so ConstrFromCnstr can be defined; its table D_002746E0 holds (VECTOR*, CT) pairs. OnSoRemove also matches per-symbol but is kept INCLUDE_ASM: enabling it triggers an asm-emission bug that inflates the still-asm TranslateSoToPosSafe by 0x14 bytes and fails the checksum (see comment). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
In-place heapsort with a unified build/extract sift loop. Matching hinged on the rotated while + else-break shape (keeps the swap arm inline and duplicates the bottom test) and a post-decrement on the last index. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
RefOpSetCar/RefOpSetCdr (sret passthrough into RefOpSetCadr with
BIFK_SetCar/SetCdr = 35/36), CFrame::FFindBinding (symid is unsigned
per the Uii mangling), CSidebag::FFindBinding and CSidebag::CloneTo
(the sidebag is a single pointer to 0x10-byte {n, CRef, next} nodes
allocated by FUN_0011C498 from g_splotheapUnk1; reached via
STRUCT_OFFSET to keep the placeholder class layout intact).
Splice: 19.44% -> 20.19% perfect (119 -> 124 functions).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Standard RefOp shape: stack CRef temp, setter, copy-construct into the hidden sret. Cdr reads the private m_ppairNext via STRUCT_OFFSET. Splice perfect match: 20.19% -> 20.90%. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…Coerce) RefOpFloor/Truncate/Ceiling/Round/Modulo/SetMusicRegister plus CRef::RefCoerceS32/F32 (ref.h signatures corrected to the sret form). SetAMRegister bound by literal mangled name: ROM callers pass the second arg unnarrowed. RefOpStopSound matches but stays wrapped (asm-emission bug inflates the following still-asm function). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ame::CloneTo, RefEvalSymbol) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…rtObject*)
CFrame/CSidebag Ref{Add,Set}Binding return CRef via sret (headers fixed).
RefOpAbs also matches (__builtin_abs gives the branchy abssi2) but stays
wrapped: unwrapping it inflates the still-asm RefOpPredictAnimationEffect
emission and fails the checksum (same bug as RefOpStopSound).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
HitTest pair: sret-temp wrappers over RefOpHitTestObjectsImpl with BIFK 113/114. DeferObjectUpdate needed a nested-scope named temp so the coerced float survives the temp's destructor in $f20. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…w splice helpers) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…, sw splice setters, alo restore/lookat) RenderSoSelf reverted: so.c emits +0x18 zero-fill on a still-asm neighbor when added (same pathology as OnSoRemove). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… sw splice ops) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…AG/exc, mpeg oids, bq CbFill, so wkr) SetAloPosition/RotationSmooth reverted: scheduler diff vs ROM (false per-symbol match from flaky asm regen). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…oc/stop, sw oxa, act/actseg goals, mpeg) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…/jlo/mpeg cross-unit) Fix game.h: UnlockEndgameCutscenesFromFgs & PchzFriendlyFromWid are raw symbols (extern C). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…and, dartgun/screen) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…coin/game/jt/blip/chkpnt) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…obj/jlo cross-unit) game.h: reload_post_death is a raw symbol (extern C). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…turret/vifs/wr etc.) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… dartgun, sky/tv/ub etc.) game.h: tally_world_completion is a raw symbol (extern C). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The previous commit (5a38764 "wip: more function matching") did not build and, once made to build, produced a binary ~64% different from the ROM: it "finished" 37 functions (deleted INCLUDE_ASM) of which most did not actually match, plus left several compile/link breakages. HEAD~1 (wave-9) builds byte-perfect, so the damage was isolated to that one commit. This restores out/SCUS_971.98: OK by: - Reverting all 37 finished functions to in-progress form (INCLUDE_ASM + #ifdef SKIP_ASM-wrapped C), preserving the C drafts as diff targets. The dual-build flagged 14 as matching, but 7 of those were false matches (size-shifters: ProjectActPose, UpdateBarrier, MatchCnvoScrollerToBeltSpeed, check_anticrack_antigrab, AddSwProxySource, InitTn, DrawWipe) and the rest caused residual rodata/assembler-state side effects, so all were reverted. - Reverting two game.c switch functions (UnlockIntroCutsceneFromWid, DefeatBossFromWid) whose ROM jump tables dangle when built from C. - Reverting header perturbations no longer needed (sw.h PFNFILTER, act.c clock.h include, difficulty.h extern "C") now that their callers are asm. - Keeping harmless build-enabling decls (mpeg CMpegAudio::Update, shadow memset include) so the wrapped drafts still dual-build. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…dding) Brings the two changes from decomp/p2-leaf-matches that fable did not already have (its water.c STRUCT_OFFSET refactor and TODO cleanup were already present and superseded). Both verified byte-identical to the prior green build. - Name FUN_001c0c50/68 as GetAMRegister/UpdateAMRegister and drop extern "C" (the functions are already decompiled; rename only, mangled symbols, no asm callers depend on the raw names). - Pad SO to its real 0x550 size (STRUCT_PADDING(160)) instead of carrying the 0x2d0..0x550 base gap in each subclass: WATER drops its leading 160-pad and JT's pad goes 1090 -> 930, both keeping their fields at the same absolute offsets. This is the root-cause fix requested in the PR TheOnlyZac#257 review. - Drop the verbose truncated-base / STRUCT_OFFSET explanatory comments in water.c per the same review (the STRUCT_OFFSET calls are self-documenting). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Remove the extern "C" shortcut from matched C++ functions by renaming their
symbol_addrs.txt entries to the GCC2-mangled form, so a plain C++ decl/def
emits the exact target symbol (manglings read from nm, not hand-computed).
Symbol renames are checksum-neutral (stripped ELF); splat regenerates asm
callers and linker scripts. 38 extern "C" removed, 0 added.
- Data globals + __asm__ aliases: just drop extern "C" (C++ doesn't mangle
namespace-scope variable names; the asm alias already forces the symbol).
- Real-named free functions (OnRwmRemove, InitCplcy/FActiveCplcy/SetCpmanCpmt/
InitCplook, SetCmLookAt/ResetCmLookAtSmooth, HandleJtGrfjtsc,
SfxhMusicUnknown1/2, MvgkUnknown3/4, UnlockEndgameCutscenesFromFgs):
pre-mangle + convert owning-header/caller decls to plain C++.
- UpdateAloXfWorld: fix alo.h (it declared the literal mangled identifier),
drop the so.c _UpdateAloXfWorld __asm__ alias, call it directly.
SetAMRegister and OnDifficultyPlayerDeath keep extern "C" -- both are
load-bearing: they pass match.sh per-symbol but fail the full checksum when
converted (caller-side uchar narrowing / a GCC 2.95 register-allocation flip).
Also trim decomp/tooling-explanation, AI-process, and TODO/blocker comments
this branch added; condense the load-bearing "kept wrapped (unwrapping breaks
the checksum)" notes to one line each. Struct-field annotations and Doxygen
kept.
Verified: out/SCUS_971.98: OK.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
These were local agent/tooling artifacts that don't belong in the repo. The ignore patterns (CLAUDE.md, .claude/, docs/DECOMP_PROMPT.md, report_*.json) move to .git/info/exclude so they stay ignored locally without being tracked; .gitignore returns to its upstream form. CLAUDE.md stays on disk, untracked. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
origin/main's content is fully contained in fable (every main commit has a patch-equivalent here), so this records the integration without changing the tree. Verified: out/SCUS_971.98: OK. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
TheOnlyZac
reviewed
Jun 23, 2026
Owner
There was a problem hiding this comment.
Wow that's a lot of code! it might a while to review this. The first and main thing I've noticed is that many functions are declared extern "C" in the P2 sources, but I think the P2 sources are all CPP (the only place I've seen C functions are the statically linked libraries).
Owner
|
I'm actually getting build errors after configuring the project with the |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Brings the P2 decomp from 2567 → 1890 remaining INCLUDE_ASM stubs (~677 functions newly matched) across 118 source files / 23 headers, plus the supporting struct, SIMD, and tooling work to make those matches possible. Every change is verified against the ground-truth checksum (out/SCUS_971.98: OK).
Highlights
Symbol & comment cleanup (final commit)
Notes for reviewers
Verification
./scripts/build.sh → out/SCUS_971.98: OK.