Skip to content

Declare dScMgBase_c slot 35 (Virtual8C) — the 18-35 range is complete - #2112

Merged
andrewboudreau merged 1 commit into
mainfrom
cpp/minigame-slot35
Sep 1, 2026
Merged

Declare dScMgBase_c slot 35 (Virtual8C) — the 18-35 range is complete#2112
andrewboudreau merged 1 commit into
mainfrom
cpp/minigame-slot35

Conversation

@andrewboudreau

Copy link
Copy Markdown
Collaborator

The last of the eighteen. Slots 18-35 of dScMgBase_c are now all declared, one slot per PR, each with every descendant override of that slot. This is the one that closes the range.

What it buys

The family stops being a prefix. Before slot 18, all 32 descendants emitted an 18-slot vtable against a cartridge table of 36 — byte-exact as far as it went, and unownable. After this commit 32 of the 33 family tables score VERIFIED at 144 bytes / 36 slots, emitted length equal to the ROM's extent, blindWords 0:

_ZTV11dScMgBase_c  emitted 144  romExtent 144  blindWords 0  VERIFIED

_ZTV12dScMgAmida_c is 148 because slot 36 is genuinely its own.

What slot 35 is

A predicate on the scene's own spawn parameter. The base body (ov004:0x020ad660, 20 bytes) returns (param1 & 0xff) != 0fBase_c's word at +0x08, the one every fBase_c is constructed with. dScMgAmida_c overrides it (ov006:0x020d1170, 24 bytes) to ask == 1. Nothing else in the family overrides it: 31 of 32 tables carry the base's answer.

The evidence, which is the best in the campaign

Thirteen dispatch sites, all in ov006, all the same shape — mov r0,<this>; ldr r1,[r0]; ldr r1,[r1,#0x8c]; blx r1 — spread across four leaf classes' code regions: dScMgCoin_c (2), dScMgPanel_c (4), dScMgSound_c (3), dScMgSnowball_c (4). Each is a class asking the question of itself, then branching to pick between two variants of the same minigame: two asset tables at 0x02105488, a different field path at 0x0211b9e0, a whole block skipped at 0x02126f58.

Arity: zero explicit parameters, measured. r1 is the loaded function pointer at every one of the thirteen sites and so cannot also be an argument; r2/r3 are never set up.

Return type int, and this is the first slot where the ROM is seen to consume one. All 13 of 13 sites follow the call with cmp r0, #0. Earlier slots only established that a return value was permitted.

The rename hazard

0x020ad660 is an overlay load base, so ov000, ov002, ov003, ov004 and ov007 each have a different, unrelated symbol at that address — ov003's is dScTitle_c's D1. The rename was keyed on the module-qualified symbol func_ov004_020ad660, which is what apply_rename takes. Keying it on the address would have hit five files in four unrelated overlays and every byte gate would still have passed.

dScMgAmida_c::Unk36

Moves from 35 to 36, where the ROM puts it. That was the last DIFFERS in the minigame family and it is closed.

The header also flags something now testable that was not before: Unk36's three call sites still use the pre-migration vtable-shim dispatch, and the reason they had to (mwcc landing Unk36 right after the base's compiler-visible virtuals) is gone. The shims are kept here — that is a measurement nobody has taken, and replacing them is its own commit with its own rombuild behind it.

Verification

  • rombuild -j16 --no-rom: 11,088 / 11,088 reproducing, 106/106 modules exact, 100.000000%

  • romdata_check against the slot-34 baseline:

    slot 34 slot 35 delta
    verified symbols 465 496 +31
    verified bytes 35,428 39,900 +4,472
    partial symbols 253 223 −30
    partial bytes 12,120 7,924 −4,196
    differing symbols 6 5 −1

    Slots 18-34 each moved exactly 128 bytes (+4 verified / +124 partial, one word into each of 32 tables). Slot 35 does not, and that is the point. Completing a table doesn't add a word to it — it moves the whole table. Thirty flipped PARTIAL → VERIFIED and _ZTV12dScMgAmida_c flipped DIFFERS → VERIFIED.

  • check_dead_references, port_refcheck, check_duplicate_sources, check_header_offsets --changed origin/main, check_src_tu_compiles, langmode_audit --check: all exit 0.

One residual, and it is not a missing virtual

_ZTV12dScMgSlot3_c still scores PARTIAL: emitted 144, romExtent 152. The eight bytes past the end read {0x00000000, 0x0213e5a0}, and 0x0213e5a0 is _ZTI12dScMgSlot1_c — the offset-to-top / typeinfo header of the next vtable in the image. _ZTV12dScMgSlot3_c sits at 0x0213eaa8 and _ZTV12dScMgSlot1_c at 0x0213eb40, exactly 152 apart; dsd points a _ZTV symbol at slot 0 rather than at the header, so those two words belong to no symbol and dsd's "extends to the next symbol" sizing swallows them.

So the class model is right and the config extent is eight bytes long. That is a config/arm9/overlays/ov006/symbols.txt change with its own gates, deliberately not folded into a keystone commit. Recorded in notes/dScMgBase_c-slots-18-35.md.

It is the mirror image of the standing "a VERIFIED vtable is not a complete vtable" hazard: there dsd cuts a table short, here it runs one past.


Based on cpp/minigame-slot34 (#2110). Attribution deltas are a stated non-goal for this repo — attribution-override applied.

🤖 Generated with Claude Code

https://claude.ai/code/session_01QhhAeJwXBnfPp7B5DNjCwh

The last of the eighteen. With this the minigame family emits its vtables
from source at full length: 32 of the 33 family tables now score VERIFIED
against the cartridge at 144 bytes / 36 slots, where before slot 18 every
one of them was an 18-slot prefix.

Slot 35 is a predicate on the scene's own spawn parameter. The base body
(ov004:0x020ad660, 20 bytes) returns `(param1 & 0xff) != 0` — fBase_c's
word at +0x08. dScMgAmida_c overrides it (ov006:0x020d1170, 24 bytes) to
ask `== 1`. Nothing else in the family overrides it, so 31 of the 32
tables carry the base's answer.

It has the strongest call-site evidence in the campaign. Thirteen dispatch
sites, all in ov006, all `mov r0,<this>; ldr r1,[r0]; ldr r1,[r1,#0x8c];
blx r1`, spread across four leaf classes' code regions — dScMgCoin_c (2),
dScMgPanel_c (4), dScMgSound_c (3), dScMgSnowball_c (4). Each is a class
asking the question of itself and branching on the answer to pick between
two variants of the same minigame. All 13 of 13 follow the call with
`cmp r0, #0`: the first slot in this range whose return value is seen to
be consumed rather than merely permitted. Arity is measured the same way —
r1 is the loaded pointer at every site and r2/r3 are never set up, so
`this` only.

The rename was keyed on the module-qualified symbol, not the address:
0x020ad660 is an overlay load base, so ov000, ov002, ov003, ov004 and
ov007 each have a different unrelated symbol there (ov003's is
dScTitle_c's D1). Keying on the address would have hit five files in four
unrelated overlays and every byte gate would still have passed.

dScMgAmida_c::Unk36 moves from 35 to 36, which is where the ROM puts it.
That closes the last DIFFERS in the family.

Verified: rombuild 11,088/11,088 reproducing, 106/106 modules exact,
100.000000%. romdata_check against the slot-34 baseline: verified symbols
465 -> 496, verified bytes 35,428 -> 39,900, partial 253 -> 223, differs
6 -> 5. Unlike slots 18-34 this is NOT +4/+124 per table — completing a
table moves the whole table, so 30 flipped PARTIAL -> VERIFIED and
_ZTV12dScMgAmida_c flipped DIFFERS -> VERIFIED.

One family table stays PARTIAL and it is not a missing virtual:
_ZTV12dScMgSlot3_c emits 144 bytes against a romExtent of 152. The eight
bytes past the end are {0, _ZTI12dScMgSlot1_c}, the header of the next
vtable in the image — dsd points a _ZTV symbol at slot 0, so those two
words belong to no symbol and its "extends to the next symbol" sizing
swallows them. A symbols.txt extent fix, with its own gates, not a
keystone change. Recorded in notes/dScMgBase_c-slots-18-35.md.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QhhAeJwXBnfPp7B5DNjCwh
@andrewboudreau andrewboudreau added the attribution-override Maintainer accepts this PR's contributor-credit changes; validation reports them as warnings label Aug 31, 2026
@tangos-validator

tangos-validator Bot commented Aug 31, 2026

Copy link
Copy Markdown

✅ PR validation — Passed

Committed merge passes; 2 contributor credit reassignment(s) noted, not a blocker.

Full merge validation

Check Result
Committed test merge yes
Byte-verified functions 10,781 / 11,347 (95.01%, +0)
Byte-verified code bytes 2,004,460 / 2,211,124 (90.65%, +0)
Claimed, not byte-verified 436 functions, 101,220 bytes (+0)
Perfect source moves 0 R100
Enrolled ranges (delinks complete) 11,059 functions, 2,053,148 bytes (92.86%, +0) -- differs from byte-verified by +278
Contributor credit 0 added, 2 changed, 0 lost
Relocation check 302 checked; 302 VERIFIED
Port reference check 405 checked; 0 stale
Module fidelity 106/106 exact; 100.000000% compared bytes
Code linked from verified source 11,088 functions, 2,067,148 bytes (93.49%)
Module bytes from source 2,067,148 / 3,049,600 (67.8%); 811,492 (26.6%) are data no delink entry reaches
ROM data reproduced from source 496 symbol(s) exact, 223 partial, 5 differ

Contributor credit moved (2)

Function Source Before After
ov004:0x020ad660 src/func_ov004_020ad660.c -> src/_ZN11dScMgBase_c9Virtual8CEv.c tangosdev andrewboudreau
ov006:0x020d1170 src/func_ov006_020d1170.c -> src/_ZN12dScMgAmida_c9Virtual8CEv.c tangosdev andrewboudreau

Byte-verified means the range carries complete in a delinks.txt, so the ROM build compiled it and compared it to the cartridge. The 436 claimed functions have a src/ file named after the symbol with no NONMATCHING banner, and nothing compiles them -- dsd fills their addresses with the ROM's own bytes. Both together are the 11,217 this project calls matched.

Warnings: contributor attribution changed, not a blocker (2 changed, 0 lost -- src/_ZN11dScMgBase_c9Virtual8CEv.c: tangosdev -> andrewboudreau; src/_ZN12dScMgAmida_c9Virtual8CEv.c: tangosdev -> andrewboudreau).

Per-file link-check detail

All 192 changed file(s) compile to the ROM byte-for-byte with correct relocation targets.

File Symbol Result Slots checked
src/_ZN10dScMgCup_c13InitResourcesEv.cpp _ZN10dScMgCup_c13InitResourcesEv ✅ verified 1
src/_ZN10dScMgCup_c6RenderEv.cpp _ZN10dScMgCup_c6RenderEv ✅ verified 1
src/_ZN10dScMgCup_c8BehaviorEv.cpp _ZN10dScMgCup_c8BehaviorEv ✅ verified 1
src/_ZN10dScMgCup_cD0Ev.cpp _ZN10dScMgCup_cD0Ev ✅ verified 1
src/_ZN10dScMgCup_cD1Ev.cpp _ZN10dScMgCup_cD1Ev ✅ verified 1
src/_ZN11dScMgBase_c15OnHitByMegaCharEv.cpp _ZN11dScMgBase_c15OnHitByMegaCharEv ✅ verified 1
src/_ZN11dScMgBase_c15graphCallback_c14GraphCallback0Ev.cpp _ZN11dScMgBase_c15graphCallback_c14GraphCallback0Ev ✅ verified 1
src/_ZN11dScMgBase_c15graphCallback_c14GraphCallback1Ev.cpp _ZN11dScMgBase_c15graphCallback_c14GraphCallback1Ev ✅ verified 1
src/_ZN11dScMgBase_c15graphCallback_c14GraphCallback2Ev.cpp _ZN11dScMgBase_c15graphCallback_c14GraphCallback2Ev ✅ verified 1
src/_ZN11dScMgBase_c15graphCallback_c14GraphCallback3Ev.cpp _ZN11dScMgBase_c15graphCallback_c14GraphCallback3Ev ✅ verified 1
src/_ZN11dScMgBase_c16OnAimedAtWithEggEv.cpp _ZN11dScMgBase_c16OnAimedAtWithEggEv ✅ verified 1
src/_ZN11dScMgBase_c19OnHitFromUnderneathEv.cpp _ZN11dScMgBase_c19OnHitFromUnderneathEv ✅ verified 1
src/_ZN11dScMgBase_c25OnAimedAtWithEggReturnVecEv.cpp _ZN11dScMgBase_c25OnAimedAtWithEggReturnVecEv ✅ verified 1
src/_ZN11dScMgBase_c8OnPushedEv.cpp _ZN11dScMgBase_c8OnPushedEv ✅ verified 1
src/_ZN11dScMgBase_c9Virtual8CEv.c _ZN11dScMgBase_c9Virtual8CEv ✅ verified 1
src/_ZN11dScMgBase_cC2Ev.cpp _ZN11dScMgBase_cC2Ev ✅ verified 1
src/_ZN11dScMgBase_cD0Ev.cpp _ZN11dScMgBase_cD0Ev ✅ verified 1
src/_ZN11dScMgBase_cD1Ev.cpp _ZN11dScMgBase_cD1Ev ✅ verified 1
src/_ZN11dScMgBase_cD2Ev.cpp _ZN11dScMgBase_cD2Ev ✅ verified 1
src/_ZN11dScMgCoin_c13InitResourcesEv.cpp _ZN11dScMgCoin_c13InitResourcesEv ✅ verified 1
src/_ZN11dScMgCoin_c13OnYoshiTryEatEi.cpp _ZN11dScMgCoin_c13OnYoshiTryEatEi ✅ verified 1
src/_ZN11dScMgCoin_c6RenderEv.cpp _ZN11dScMgCoin_c6RenderEv ✅ verified 1
src/_ZN11dScMgCoin_c8BehaviorEv.cpp _ZN11dScMgCoin_c8BehaviorEv ✅ verified 1
src/_ZN11dScMgCoin_cD0Ev.cpp _ZN11dScMgCoin_cD0Ev ✅ verified 1
src/_ZN11dScMgCoin_cD1Ev.cpp _ZN11dScMgCoin_cD1Ev ✅ verified 1
src/_ZN11dScMgJump_c13InitResourcesEv.cpp _ZN11dScMgJump_c13InitResourcesEv ✅ verified 1
src/_ZN11dScMgJump_c13OnTurnIntoEggEi.cpp _ZN11dScMgJump_c13OnTurnIntoEggEi ✅ verified 1
src/_ZN11dScMgJump_c16CleanupResourcesEv.cpp _ZN11dScMgJump_c16CleanupResourcesEv ✅ verified 1
src/_ZN11dScMgJump_c6RenderEv.cpp _ZN11dScMgJump_c6RenderEv ✅ verified 1
src/_ZN11dScMgJump_c8BehaviorEv.cpp _ZN11dScMgJump_c8BehaviorEv ✅ verified 1
src/_ZN11dScMgJump_cD0Ev.cpp _ZN11dScMgJump_cD0Ev ✅ verified 1
src/_ZN11dScMgJump_cD1Ev.cpp _ZN11dScMgJump_cD1Ev ✅ verified 1
src/_ZN12dScMg3DEsp_c13InitResourcesEv.cpp _ZN12dScMg3DEsp_c13InitResourcesEv ✅ verified 1
src/_ZN12dScMg3DEsp_c13OnYoshiTryEatEi.cpp _ZN12dScMg3DEsp_c13OnYoshiTryEatEi ✅ verified 1
src/_ZN12dScMg3DEsp_c16CleanupResourcesEv.cpp _ZN12dScMg3DEsp_c16CleanupResourcesEv ✅ verified 1
src/_ZN12dScMg3DEsp_c6RenderEv.cpp _ZN12dScMg3DEsp_c6RenderEv ✅ verified 1
src/_ZN12dScMg3DEsp_c8BehaviorEv.cpp _ZN12dScMg3DEsp_c8BehaviorEv ✅ verified 1
src/_ZN12dScMg3DEsp_cD0Ev.cpp _ZN12dScMg3DEsp_cD0Ev ✅ verified 1
src/_ZN12dScMg3DEsp_cD1Ev.cpp _ZN12dScMg3DEsp_cD1Ev ✅ verified 1
src/_ZN12dScMgAmida_c13InitResourcesEv.cpp _ZN12dScMgAmida_c13InitResourcesEv ✅ verified 1
src/_ZN12dScMgAmida_c21AfterCleanupResourcesEj.cpp _ZN12dScMgAmida_c21AfterCleanupResourcesEj ✅ verified 1
src/_ZN12dScMgAmida_c5Unk36Ev.cpp _ZN12dScMgAmida_c5Unk36Ev ✅ verified 1
src/_ZN12dScMgAmida_c6RenderEv.cpp _ZN12dScMgAmida_c6RenderEv ✅ verified 1
src/_ZN12dScMgAmida_c8BehaviorEv.cpp _ZN12dScMgAmida_c8BehaviorEv ✅ verified 1
src/_ZN12dScMgAmida_c9Virtual8CEv.c _ZN12dScMgAmida_c9Virtual8CEv ✅ verified 1
src/_ZN12dScMgAmida_cD0Ev.cpp _ZN12dScMgAmida_cD0Ev ✅ verified 1
src/_ZN12dScMgAmida_cD1Ev.cpp _ZN12dScMgAmida_cD1Ev ✅ verified 1
src/_ZN12dScMgJump2_c13InitResourcesEv.cpp _ZN12dScMgJump2_c13InitResourcesEv ✅ verified 1
src/_ZN12dScMgJump2_c16CleanupResourcesEv.cpp _ZN12dScMgJump2_c16CleanupResourcesEv ✅ verified 1
src/_ZN12dScMgJump2_c6RenderEv.cpp _ZN12dScMgJump2_c6RenderEv ✅ verified 1
src/_ZN12dScMgJump2_c8BehaviorEv.cpp _ZN12dScMgJump2_c8BehaviorEv ✅ verified 1
src/_ZN12dScMgJump2_cD0Ev.cpp _ZN12dScMgJump2_cD0Ev ✅ verified 1
src/_ZN12dScMgJump2_cD1Ev.cpp _ZN12dScMgJump2_cD1Ev ✅ verified 1
src/_ZN12dScMgLuigi_c13InitResourcesEv.cpp _ZN12dScMgLuigi_c13InitResourcesEv ✅ verified 1
src/_ZN12dScMgLuigi_c21AfterCleanupResourcesEj.cpp _ZN12dScMgLuigi_c21AfterCleanupResourcesEj ✅ verified 1
src/_ZN12dScMgLuigi_c6RenderEv.cpp _ZN12dScMgLuigi_c6RenderEv ✅ verified 1
src/_ZN12dScMgLuigi_c8BehaviorEv.cpp _ZN12dScMgLuigi_c8BehaviorEv ✅ verified 1
src/_ZN12dScMgLuigi_cD0Ev.cpp _ZN12dScMgLuigi_cD0Ev ✅ verified 1
src/_ZN12dScMgLuigi_cD1Ev.cpp _ZN12dScMgLuigi_cD1Ev ✅ verified 1
src/_ZN12dScMgPanel_c13InitResourcesEv.cpp _ZN12dScMgPanel_c13InitResourcesEv ✅ verified 1
src/_ZN12dScMgPanel_c6RenderEv.cpp _ZN12dScMgPanel_c6RenderEv ✅ verified 1
src/_ZN12dScMgPanel_c8BehaviorEv.cpp _ZN12dScMgPanel_c8BehaviorEv ✅ verified 1
src/_ZN12dScMgPanel_cD0Ev.cpp _ZN12dScMgPanel_cD0Ev ✅ verified 1
src/_ZN12dScMgPanel_cD1Ev.cpp _ZN12dScMgPanel_cD1Ev ✅ verified 1
src/_ZN12dScMgSlot1_c13InitResourcesEv.cpp _ZN12dScMgSlot1_c13InitResourcesEv ✅ verified 1
src/_ZN12dScMgSlot1_c15OnHitByMegaCharEv.cpp _ZN12dScMgSlot1_c15OnHitByMegaCharEv ✅ verified 1
src/_ZN12dScMgSlot1_c19OnHitFromUnderneathEv.cpp _ZN12dScMgSlot1_c19OnHitFromUnderneathEv ✅ verified 1
src/_ZN12dScMgSlot1_c6RenderEv.cpp _ZN12dScMgSlot1_c6RenderEv ✅ verified 1
src/_ZN12dScMgSlot1_c9betIcon_c6RenderEv.cpp _ZN12dScMgSlot1_c9betIcon_c6RenderEv ✅ verified 1
src/_ZN12dScMgSlot1_c9betIcon_c8BehaviorEv.cpp _ZN12dScMgSlot1_c9betIcon_c8BehaviorEv ✅ verified 1
src/_ZN12dScMgSlot1_cD0Ev.cpp _ZN12dScMgSlot1_cD0Ev ✅ verified 1
src/_ZN12dScMgSlot1_cD1Ev.cpp _ZN12dScMgSlot1_cD1Ev ✅ verified 1
src/_ZN12dScMgSlot3_c13InitResourcesEv.cpp _ZN12dScMgSlot3_c13InitResourcesEv ✅ verified 1
src/_ZN12dScMgSlot3_c6RenderEv.cpp _ZN12dScMgSlot3_c6RenderEv ✅ verified 1
src/_ZN12dScMgSlot3_c8BehaviorEv.cpp _ZN12dScMgSlot3_c8BehaviorEv ✅ verified 1
src/_ZN12dScMgSlot3_cD0Ev.cpp _ZN12dScMgSlot3_cD0Ev ✅ verified 1
src/_ZN12dScMgSlot3_cD1Ev.cpp _ZN12dScMgSlot3_cD1Ev ✅ verified 1
src/_ZN12dScMgSound_c13InitResourcesEv.cpp _ZN12dScMgSound_c13InitResourcesEv ✅ verified 1
src/_ZN12dScMgSound_c6RenderEv.cpp _ZN12dScMgSound_c6RenderEv ✅ verified 1
src/_ZN12dScMgSound_c8BehaviorEv.cpp _ZN12dScMgSound_c8BehaviorEv ✅ verified 1
src/_ZN12dScMgSound_cD0Ev.cpp _ZN12dScMgSound_cD0Ev ✅ verified 1
src/_ZN12dScMgSound_cD1Ev.cpp _ZN12dScMgSound_cD1Ev ✅ verified 1
src/_ZN13dScMgFlower_c13InitResourcesEv.cpp _ZN13dScMgFlower_c13InitResourcesEv ✅ verified 1
src/_ZN13dScMgFlower_c6RenderEv.cpp _ZN13dScMgFlower_c6RenderEv ✅ verified 1
src/_ZN13dScMgFlower_c8BehaviorEv.cpp _ZN13dScMgFlower_c8BehaviorEv ✅ verified 1
src/_ZN13dScMgFlower_cD0Ev.cpp _ZN13dScMgFlower_cD0Ev ✅ verified 1
src/_ZN13dScMgFlower_cD1Ev.cpp _ZN13dScMgFlower_cD1Ev ✅ verified 1
src/_ZN13dScMgMemory_c13InitResourcesEv.cpp _ZN13dScMgMemory_c13InitResourcesEv ✅ verified 1
src/_ZN13dScMgMemory_c13OnTurnIntoEggEi.c _ZN13dScMgMemory_c13OnTurnIntoEggEi ✅ verified 1
src/_ZN13dScMgMemory_c13OnYoshiTryEatEi.c _ZN13dScMgMemory_c13OnYoshiTryEatEi ✅ verified 1
src/_ZN13dScMgMemory_c15OnGroundPoundedEv.c _ZN13dScMgMemory_c15OnGroundPoundedEv ✅ verified 1
src/_ZN13dScMgMemory_c6RenderEv.cpp _ZN13dScMgMemory_c6RenderEv ✅ verified 1
src/_ZN13dScMgMemory_c8BehaviorEv.cpp _ZN13dScMgMemory_c8BehaviorEv ✅ verified 1
src/_ZN13dScMgMemory_cD0Ev.cpp _ZN13dScMgMemory_cD0Ev ✅ verified 1
src/_ZN13dScMgMemory_cD1Ev.cpp _ZN13dScMgMemory_cD1Ev ✅ verified 1
src/_ZN13dScMgTeresa_c13InitResourcesEv.cpp _ZN13dScMgTeresa_c13InitResourcesEv ✅ verified 1
src/_ZN13dScMgTeresa_c6RenderEv.cpp _ZN13dScMgTeresa_c6RenderEv ✅ verified 1
src/_ZN13dScMgTeresa_c8BehaviorEv.cpp _ZN13dScMgTeresa_c8BehaviorEv ✅ verified 1
src/_ZN13dScMgTeresa_cD0Ev.cpp _ZN13dScMgTeresa_cD0Ev ✅ verified 1
src/_ZN13dScMgTeresa_cD1Ev.cpp _ZN13dScMgTeresa_cD1Ev ✅ verified 1
src/_ZN14dScMgBomroom_c13InitResourcesEv.cpp _ZN14dScMgBomroom_c13InitResourcesEv ✅ verified 1
src/_ZN14dScMgBomroom_c6RenderEv.cpp _ZN14dScMgBomroom_c6RenderEv ✅ verified 1
src/_ZN14dScMgBomroom_c8BehaviorEv.cpp _ZN14dScMgBomroom_c8BehaviorEv ✅ verified 1
src/_ZN14dScMgBomroom_cD0Ev.cpp _ZN14dScMgBomroom_cD0Ev ✅ verified 1
src/_ZN14dScMgBomroom_cD1Ev.cpp _ZN14dScMgBomroom_cD1Ev ✅ verified 1
src/_ZN14dScMgCurling_c13InitResourcesEv.cpp _ZN14dScMgCurling_c13InitResourcesEv ✅ verified 1
src/_ZN14dScMgCurling_c13OnYoshiTryEatEi.cpp _ZN14dScMgCurling_c13OnYoshiTryEatEi ✅ verified 1
src/_ZN14dScMgCurling_c6RenderEv.cpp _ZN14dScMgCurling_c6RenderEv ✅ verified 1
src/_ZN14dScMgCurling_c8BehaviorEv.cpp _ZN14dScMgCurling_c8BehaviorEv ✅ verified 1
src/_ZN14dScMgCurling_cD0Ev.cpp _ZN14dScMgCurling_cD0Ev ✅ verified 1
src/_ZN14dScMgCurling_cD1Ev.cpp _ZN14dScMgCurling_cD1Ev ✅ verified 1
src/_ZN14dScMgD3DBase_c11AfterRenderEj.cpp _ZN14dScMgD3DBase_c11AfterRenderEj ✅ verified 1
src/_ZN14dScMgD3DBase_c12BeforeRenderEv.cpp _ZN14dScMgD3DBase_c12BeforeRenderEv ✅ verified 1
src/_ZN14dScMgD3DBase_c14BeforeBehaviorEv.cpp _ZN14dScMgD3DBase_c14BeforeBehaviorEv ✅ verified 1
src/_ZN14dScMgD3DBase_c16OnAimedAtWithEggEv.cpp _ZN14dScMgD3DBase_c16OnAimedAtWithEggEv ✅ verified 1
src/_ZN14dScMgD3DBase_c18AfterInitResourcesEj.cpp _ZN14dScMgD3DBase_c18AfterInitResourcesEj ✅ verified 1
src/_ZN14dScMgD3DBase_c19BeforeInitResourcesEv.cpp _ZN14dScMgD3DBase_c19BeforeInitResourcesEv ✅ verified 1
src/_ZN14dScMgD3DBase_c21AfterCleanupResourcesEj.cpp _ZN14dScMgD3DBase_c21AfterCleanupResourcesEj ✅ verified 1
src/_ZN14dScMgD3DBase_c25OnAimedAtWithEggReturnVecEv.c _ZN14dScMgD3DBase_c25OnAimedAtWithEggReturnVecEv ✅ verified 1
src/_ZN14dScMgD3DBase_c9Virtual7CEv.c _ZN14dScMgD3DBase_c9Virtual7CEv ✅ verified 1
src/_ZN14dScMgD3DBase_cD0Ev.cpp _ZN14dScMgD3DBase_cD0Ev ✅ verified 1
src/_ZN14dScMgD3DBase_cD1Ev.cpp _ZN14dScMgD3DBase_cD1Ev ✅ verified 1
src/_ZN14dScMgMemory2_c13InitResourcesEv.cpp _ZN14dScMgMemory2_c13InitResourcesEv ✅ verified 1
src/_ZN14dScMgMemory2_c13OnTurnIntoEggEi.c _ZN14dScMgMemory2_c13OnTurnIntoEggEi ✅ verified 1
src/_ZN14dScMgMemory2_c13OnYoshiTryEatEi.c _ZN14dScMgMemory2_c13OnYoshiTryEatEi ✅ verified 1
src/_ZN14dScMgMemory2_c15OnGroundPoundedEv.c _ZN14dScMgMemory2_c15OnGroundPoundedEv ✅ verified 1
src/_ZN14dScMgMemory2_c6RenderEv.cpp _ZN14dScMgMemory2_c6RenderEv ✅ verified 1
src/_ZN14dScMgMemory2_c8BehaviorEv.cpp _ZN14dScMgMemory2_c8BehaviorEv ✅ verified 1
src/_ZN14dScMgMemory2_cD0Ev.cpp _ZN14dScMgMemory2_cD0Ev ✅ verified 1
src/_ZN14dScMgMemory2_cD1Ev.cpp _ZN14dScMgMemory2_cD1Ev ✅ verified 1
src/_ZN15dScMgCurling2_c13InitResourcesEv.cpp _ZN15dScMgCurling2_c13InitResourcesEv ✅ verified 1
src/_ZN15dScMgCurling2_c13OnYoshiTryEatEi.cpp _ZN15dScMgCurling2_c13OnYoshiTryEatEi ✅ verified 1
src/_ZN15dScMgCurling2_c6RenderEv.cpp _ZN15dScMgCurling2_c6RenderEv ✅ verified 1
src/_ZN15dScMgCurling2_c8BehaviorEv.cpp _ZN15dScMgCurling2_c8BehaviorEv ✅ verified 1
src/_ZN15dScMgCurling2_cD0Ev.cpp _ZN15dScMgCurling2_cD0Ev ✅ verified 1
src/_ZN15dScMgCurling2_cD1Ev.cpp _ZN15dScMgCurling2_cD1Ev ✅ verified 1
src/_ZN15dScMgHanachan_c13InitResourcesEv.cpp _ZN15dScMgHanachan_c13InitResourcesEv ✅ verified 1
src/_ZN15dScMgHanachan_c16CleanupResourcesEv.cpp _ZN15dScMgHanachan_c16CleanupResourcesEv ✅ verified 1
src/_ZN15dScMgHanachan_c6RenderEv.cpp _ZN15dScMgHanachan_c6RenderEv ✅ verified 1
src/_ZN15dScMgHanachan_c8BehaviorEv.cpp _ZN15dScMgHanachan_c8BehaviorEv ✅ verified 1
src/_ZN15dScMgHanachan_cD0Ev.cpp _ZN15dScMgHanachan_cD0Ev ✅ verified 1
src/_ZN15dScMgHanachan_cD1Ev.cpp _ZN15dScMgHanachan_cD1Ev ✅ verified 1
src/_ZN15dScMgPachinko_c13InitResourcesEv.cpp _ZN15dScMgPachinko_c13InitResourcesEv ✅ verified 1
src/_ZN15dScMgPachinko_c6RenderEv.cpp _ZN15dScMgPachinko_c6RenderEv ✅ verified 1
src/_ZN15dScMgPachinko_c8BehaviorEv.cpp _ZN15dScMgPachinko_c8BehaviorEv ✅ verified 1
src/_ZN15dScMgPachinko_cD0Ev.cpp _ZN15dScMgPachinko_cD0Ev ✅ verified 1
src/_ZN15dScMgPachinko_cD1Ev.cpp _ZN15dScMgPachinko_cD1Ev ✅ verified 1
src/_ZN15dScMgRoulette_c13InitResourcesEv.cpp _ZN15dScMgRoulette_c13InitResourcesEv ✅ verified 1
src/_ZN15dScMgRoulette_c13OnYoshiTryEatEi.cpp _ZN15dScMgRoulette_c13OnYoshiTryEatEi ✅ verified 1
src/_ZN15dScMgRoulette_c16CleanupResourcesEv.cpp _ZN15dScMgRoulette_c16CleanupResourcesEv ✅ verified 1
src/_ZN15dScMgRoulette_c6RenderEv.cpp _ZN15dScMgRoulette_c6RenderEv ✅ verified 1
src/_ZN15dScMgRoulette_c8BehaviorEv.cpp _ZN15dScMgRoulette_c8BehaviorEv ✅ verified 1
src/_ZN15dScMgRoulette_cD0Ev.cpp _ZN15dScMgRoulette_cD0Ev ✅ verified 1
src/_ZN15dScMgRoulette_cD1Ev.cpp _ZN15dScMgRoulette_cD1Ev ✅ verified 1
src/_ZN15dScMgSnowball_c13InitResourcesEv.cpp _ZN15dScMgSnowball_c13InitResourcesEv ✅ verified 1
src/_ZN15dScMgSnowball_c16CleanupResourcesEv.cpp _ZN15dScMgSnowball_c16CleanupResourcesEv ✅ verified 1
src/_ZN15dScMgSnowball_c6RenderEv.cpp _ZN15dScMgSnowball_c6RenderEv ✅ verified 1
src/_ZN15dScMgSnowball_c8BehaviorEv.cpp _ZN15dScMgSnowball_c8BehaviorEv ✅ verified 1
src/_ZN15dScMgSnowball_c8OnKickedEv.c _ZN15dScMgSnowball_c8OnKickedEv ✅ verified 1
src/_ZN15dScMgSnowball_cD0Ev.cpp _ZN15dScMgSnowball_cD0Ev ✅ verified 1
src/_ZN15dScMgSnowball_cD1Ev.cpp _ZN15dScMgSnowball_cD1Ev ✅ verified 1
src/_ZN16dScMgPachinko2_c13InitResourcesEv.cpp _ZN16dScMgPachinko2_c13InitResourcesEv ✅ verified 1
src/_ZN16dScMgPachinko2_c6RenderEv.cpp _ZN16dScMgPachinko2_c6RenderEv ✅ verified 1
src/_ZN16dScMgPachinko2_c8BehaviorEv.cpp _ZN16dScMgPachinko2_c8BehaviorEv ✅ verified 1
src/_ZN16dScMgPachinko2_cD0Ev.cpp _ZN16dScMgPachinko2_cD0Ev ✅ verified 1
src/_ZN16dScMgPachinko2_cD1Ev.cpp _ZN16dScMgPachinko2_cD1Ev ✅ verified 1
src/_ZN16dScMgSmartball_c21AfterCleanupResourcesEj.cpp _ZN16dScMgSmartball_c21AfterCleanupResourcesEj ✅ verified 1
src/_ZN16dScMgSmartball_c6RenderEv.cpp _ZN16dScMgSmartball_c6RenderEv ✅ verified 1
src/_ZN16dScMgSmartball_c8BehaviorEv.cpp _ZN16dScMgSmartball_c8BehaviorEv ✅ verified 1
src/_ZN16dScMgSmartball_cD0Ev.cpp _ZN16dScMgSmartball_cD0Ev ✅ verified 1
src/_ZN16dScMgSmartball_cD1Ev.cpp _ZN16dScMgSmartball_cD1Ev ✅ verified 1
src/_ZN17dScMgTrampoline_c13InitResourcesEv.cpp _ZN17dScMgTrampoline_c13InitResourcesEv ✅ verified 1
src/_ZN17dScMgTrampoline_c13OnYoshiTryEatEi.c _ZN17dScMgTrampoline_c13OnYoshiTryEatEi ✅ verified 1
src/_ZN17dScMgTrampoline_c16CleanupResourcesEv.cpp _ZN17dScMgTrampoline_c16CleanupResourcesEv ✅ verified 1
src/_ZN17dScMgTrampoline_c6RenderEv.cpp _ZN17dScMgTrampoline_c6RenderEv ✅ verified 1
src/_ZN17dScMgTrampoline_c8BehaviorEv.cpp _ZN17dScMgTrampoline_c8BehaviorEv ✅ verified 1
src/_ZN17dScMgTrampoline_cD0Ev.cpp _ZN17dScMgTrampoline_cD0Ev ✅ verified 1
src/_ZN17dScMgTrampoline_cD1Ev.cpp _ZN17dScMgTrampoline_cD1Ev ✅ verified 1
src/_ZN18dScMgTrampoline2_c13InitResourcesEv.cpp _ZN18dScMgTrampoline2_c13InitResourcesEv ✅ verified 1
src/_ZN18dScMgTrampoline2_c16CleanupResourcesEv.cpp _ZN18dScMgTrampoline2_c16CleanupResourcesEv ✅ verified 1
src/_ZN18dScMgTrampoline2_c6RenderEv.cpp _ZN18dScMgTrampoline2_c6RenderEv ✅ verified 1
src/_ZN18dScMgTrampoline2_c8BehaviorEv.cpp _ZN18dScMgTrampoline2_c8BehaviorEv ✅ verified 1
src/_ZN18dScMgTrampoline2_cD0Ev.cpp _ZN18dScMgTrampoline2_cD0Ev ✅ verified 1
src/_ZN18dScMgTrampoline2_cD1Ev.cpp _ZN18dScMgTrampoline2_cD1Ev ✅ verified 1
src/actors/dScMgBSC_c.cpp _ZN10dScMgBSC_cD1Ev + _ZN10dScMgBSC_cD0Ev + func_ov006_02124a04 + func_ov006_02124a08 + func_ov006_02124ae4 + func_ov006_02124b58 + func_ov006_02124bb4 + func_ov006_02124cb4 + func_ov006_02124dc0 + func_ov006_02124e1c + func_ov006_02124ec4 + func_ov006_02124fd8 + func_ov006_021250e4 + _ZN10dScMgBSC_c15OnGroundPoundedEv + _ZN10dScMgBSC_c13OnTurnIntoEggEi + _ZN10dScMgBSC_c13OnYoshiTryEatEi + _ZN10dScMgBSC_c6RenderEv + _ZN10dScMgBSC_c8BehaviorEv + _ZN10dScMgBSC_c13InitResourcesEv ✅ verified 19
src/actors/dScMgBase_c.cpp _ZN11dScMgBase_c16OnPendingDestroyEv + _ZN11dScMgBase_c6RenderEv + _ZN11dScMgBase_c12BeforeRenderEv + _ZN11dScMgBase_c8BehaviorEv + _ZN11dScMgBase_c14BeforeBehaviorEv + _ZN11dScMgBase_c21AfterCleanupResourcesEj + _ZN11dScMgBase_c18AfterInitResourcesEj + _ZN11dScMgBase_c19BeforeInitResourcesEv ✅ verified 8
src/actors/dScMgCard_c.cpp _ZN11dScMgCard_cD1Ev + _ZN11dScMgCard_cD0Ev + func_ov006_020d96e0 + func_ov006_020d96f0 + func_ov006_020d970c + func_ov006_020d978c + func_ov006_020d9998 + func_ov006_020d99a4 + func_ov006_020d99ec + func_ov006_020d9a14 + func_ov006_020d9bd0 + func_ov006_020d9bdc + func_ov006_020d9c5c + func_ov006_020da00c + func_ov006_020da0ac + func_ov006_020da154 + func_ov006_020da174 + func_ov006_020da420 + func_ov006_020da4ac + func_ov006_020da5e8 + func_ov006_020da834 + func_ov006_020da860 + func_ov006_020da88c + func_ov006_020da8b8 + func_ov006_020da8e4 + func_ov006_020da974 + _ZN11dScMgCard_c16CleanupResourcesEv + _ZN11dScMgCard_c6RenderEv + _ZN11dScMgCard_c8BehaviorEv + func_ov006_020dac34 + _ZN11dScMgCard_c15OnGroundPoundedEv + _ZN11dScMgCard_c13OnTurnIntoEggEi + _ZN11dScMgCard_c13OnYoshiTryEatEi + _ZN11dScMgCard_c13InitResourcesEv ✅ verified 34
src/actors/dScMgMCarlo2_c.cpp _ZN14dScMgMCarlo2_cD1Ev + _ZN14dScMgMCarlo2_cD0Ev + func_ov006_020f8ff0 + func_ov006_020f9000 + func_ov006_020f94f4 + func_ov006_020f9560 + func_ov006_020f95f0 + func_ov006_020f9668 + func_ov006_020f96e0 + func_ov006_020f9760 + func_ov006_020f98dc + func_ov006_020f9994 + func_ov006_020f9bec + func_ov006_020f9cbc + func_ov006_020f9d68 + func_ov006_020f9db8 + func_ov006_020f9f40 + _ZN14dScMgMCarlo2_c16CleanupResourcesEv + _ZN14dScMgMCarlo2_c6RenderEv + _ZN14dScMgMCarlo2_c8BehaviorEv + _ZN14dScMgMCarlo2_c13OnTurnIntoEggEi + _ZN14dScMgMCarlo2_c13OnYoshiTryEatEi + _ZN14dScMgMCarlo2_c13InitResourcesEv ✅ verified 23
src/actors/dScMgMCarlo_c.cpp _ZN13dScMgMCarlo_cD1Ev + _ZN13dScMgMCarlo_cD0Ev + func_ov006_020f7730 + func_ov006_020f7740 + func_ov006_020f7994 + func_ov006_020f7a00 + func_ov006_020f7a90 + func_ov006_020f7b10 + func_ov006_020f7b90 + func_ov006_020f7c10 + func_ov006_020f7e2c + func_ov006_020f7ee4 + func_ov006_020f8154 + func_ov006_020f8224 + func_ov006_020f82d0 + func_ov006_020f8320 + func_ov006_020f84a8 + func_ov006_020f8540 + _ZN13dScMgMCarlo_c6RenderEv + _ZN13dScMgMCarlo_c8BehaviorEv + _ZN13dScMgMCarlo_c13OnTurnIntoEggEi + _ZN13dScMgMCarlo_c13OnYoshiTryEatEi + _ZN13dScMgMCarlo_c13InitResourcesEv ✅ verified 23
src/actors/dScMgSingle3DBase_c.cpp _ZN19dScMgSingle3DBase_cD1Ev + _ZN19dScMgSingle3DBase_cD0Ev + func_ov006_0210a534 + _ZN19dScMgSingle3DBase_c24OnHitByCannonBlastedCharEv + _ZN19dScMgSingle3DBase_c21AfterCleanupResourcesEj + _ZN19dScMgSingle3DBase_c12BeforeRenderEv + _ZN19dScMgSingle3DBase_c14BeforeBehaviorEv + _ZN19dScMgSingle3DBase_c18AfterInitResourcesEj + _ZN19dScMgSingle3DBase_c9Virtual84Ev ✅ verified 9
src/func_ov004_020b0a54.cpp func_ov004_020b0a54 ✅ verified 1
src/func_ov006_0210d6b8.cpp func_ov006_0210d6b8 ✅ verified 1

The private worker commits a test merge, builds the stock ROM profile, compares every executable module, measures matched and source-built code, checks contributor lineage, and verifies affected relocations. The mod profile is opt-in and is not part of this merge gate.

@andrewboudreau

Copy link
Copy Markdown
Collaborator Author

Self-review — slot 35, and the end of the 18-35 range

Reviewing my own work because no PR in this repo can be APPROVED — one shared account, so this is a written sign-off, not a GitHub approval. Findings are inline rather than linked, per the standing rule that a reviewer should not have to chase a second artifact.

What landed

dScMgBase_c::Virtual8C() — slot 35, SIG 'v', returns int. Base body ov004:0x020ad660 (0x14 bytes) is return (param1 & 0xff) != 0 on fBase_c::param1 at this+0x08. One override, dScMgAmida_c at ov006:0x020d1170 (0x18 bytes), which asks the narrower == 1. Census: 32 tables, 31 inherit, 1 override.

Two renames, both scoped by module-qualified symbol rather than by address:

func_ov004_020ad660 -> _ZN11dScMgBase_c9Virtual8CEv
func_ov006_020d1170 -> _ZN12dScMgAmida_c9Virtual8CEv

0x020ad660 is an overlay load base. ov000, ov002, ov003, ov004 and ov007 each have a different, unrelated symbol at that address — ov003's is dScTitle_c's D1. Renaming by address rather than by module-qualified symbol would have hit five files across four unrelated overlays. Only func_ov004_020ad660 is this one.

Call-site evidence — the strongest in the campaign

Thirteen dispatch sites in ov006, spread across four leaf classes that are not the declaring class: dScMgCoin_c (2), dScMgPanel_c (4), dScMgSound_c (3), dScMgSnowball_c (4). Every one is the same shape:

mov r0, <this>
ldr r1, [r0]
ldr r1, [r1, #0x8c]
blx r1
cmp r0, #0

All 13 of 13 follow with cmp r0, #0. This is the first slot in the whole 18-35 range whose return value is seen to be consumed rather than merely permitted. Arity is measured, not assumed: r1 holds the loaded function pointer at every site so it cannot also be an argument, and r2/r3 are never set up. this only.

Three representative uses: 0x02105488 picks between two asset tables, 0x0211b9e0 takes a different field path, 0x02126f58 skips a block entirely.

Verification

gate result
rombuild.py -j 16 11,088 / 11,088 objects reproducing
module fidelity 106/106 exact, 100.000000%
eligible.py bracket no regressions
langmode_audit.py --check langmode-baseline.json exit 0
check_dead_references.py exit 0 (checked the exit status, not the tail — see finding 2)
port_refcheck.py exit 0
check_header_offsets.py exit 0
prepush_attribution.py exit 0

The measurement, and why it does not look like slots 18-34

Every slot from 18 to 34 moved exactly 32 tables x 4 bytes = 128, split verifiedBytes +4 / partialBytes +124. Slot 35 does not, and a reviewer expecting +4/+124 here will read a correct result as a defect.

Completing a table does not add a word to it — it moves the whole table from PARTIAL to VERIFIED:

romdata_check before after delta
verified 465 496 +31
verifiedBytes 35,428 39,900 +4,472
partial 253 223 -30
partialBytes 12,120 7,924 -4,196
differs 6 5 -1

Thirty tables flipped PARTIAL to VERIFIED and _ZTV12dScMgAmida_c flipped DIFFERS to VERIFIED — that was the family's last DIFFERS, and it closes because Unk36 now lands on index 36, where the ROM puts it, instead of being pushed down by the missing declaration.

Per-symbol (driven through RD.check_object, since romdata_check --json carries counts plus a differing list only and has no per-symbol status):

_ZTV11dScMgBase_c   emitted 144   romExtent 144   blindWords 0   VERIFIED

32 of 33 family tables are VERIFIED at 144 bytes / 36 slots with blindWords 0.

Finding 1 — the one remaining PARTIAL is a dsd config artifact, not a class-model gap. NOT fixed here, deliberately.

_ZTV12dScMgSlot3_c   addr 0x0213eaa8   emitted 144   romExtent 152   PARTIAL
_ZTV12dScMgSlot1_c   addr 0x0213eb40                 (exactly 152 apart)

The eight bytes past the end read {0x00000000, 0x0213e5a0}, and 0x0213e5a0 is _ZTI12dScMgSlot1_c. That is Slot1's two-word header, not two more of Slot3's virtuals. dsd points a _ZTV symbol at slot 0, not at the offset-to-top/typeinfo header, so the "extends to the next symbol" sizing swallows the following table's header. Slot3's class model is right and complete at 36 slots; the config extent is eight bytes long.

This is the mirror image of the standing "a VERIFIED vtable is not a complete vtable" hazard — same cause, opposite direction: the symbol boundaries are dsd's guesses, not the ABI's.

Why it is not in this PR: the fix is a one-line config/arm9/overlays/ov006/symbols.txt extent change. It is a config edit with a different blast radius from a header declaration, and it deserves its own gates rather than riding in on a keystone commit. Filed as a follow-up.

A reviewer who takes a PARTIAL vtable at face value here would go looking for two virtuals that do not exist.

Finding 2 — two process traps this PR tripped over

  • apply_rename renames the definition, symbols.txt and delinks.txt — never callers. Slot 34 had six call sites, not the four I first counted. A post-rename grep for every old symbol across .c/.cpp/.h/.md/.json/.txt (excluding build/) is now standing procedure and was run here. Slot 35's base has no direct callers — all thirteen uses are virtual dispatch, so no call site names the symbol — and the one override has none either.
  • check_dead_references prints "no broken markdown links" even when it FAILS. Its success line is "no new dead references". Check the exit status. I did.

notes/data/tu-merge-candidates.json still carries the old src/func_ov004_020ad660 path. That is generated output and matches precedent — slot 34's func_ov004_020ae3b4 is still in there and the gate exits 0.

Finding 3 — a claim I explicitly did not make

include/dScMgAmida_c.h constraint 1 now says the cause of the Unk36 vtable-shim workaround is gone, and in the same breath says the shims are kept, with "do not remove them on the strength of this paragraph." Whether the three shim call sites can become plain this->Unk36() is a measurement nobody has taken. It gets its own commit and its own rombuild.

Likewise notes/minigame-provenance.md: the earlier note that nothing calls slot 35 is affirmed as a correct observation (no cross-reference in src/, nothing in the class's own methods) and corrected as a conclusion — the slot is dispatched thirteen times virtually, which is exactly why no call site names it.

Prose re-counted

Four files carried sentences the declaration invalidates, all updated to past tense with the new state stated positively:

  • include/dScMgBase_c.h — banner now leads "ALL EIGHTEEN ARE DECLARED", plus a warning that anything added from here is past the cartridge's own table with no safety net.
  • include/dScMgAmida_c.h — the Unk36 DIFFERS explanation rewritten to the closed state.
  • notes/dScMgBase_c-slots-18-35.md — worklist row 35 DONE, plus a new section carrying the before/after counter table and the Slot3 extent finding.
  • notes/minigame-provenance.md — as above.

The thirteen borrowed dActor_c names for slots 18-30 are flagged in the header as a coincidence of index, not shared identity — dScMgBase_c is not a dActor_c descendant (fBase_c -> dBase_c -> dScene_c -> dScMgBase_c). Retiring those names is a separate, deliberately open question.

Verdict

Signed off. attribution-override applied per the standing instruction that attribution is a non-goal here.

Stack context: base is cpp/minigame-slot34 (#2110). The stack #2099 through #2112 is rooted at main and all eight PRs are MERGEABLE. This is the last of the eighteen — the 18-35 keystone range is complete.

@andrewboudreau andrewboudreau left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Reviewed against the cartridge. Approve on content — with one count correction and one lever disagreement.

I rebuilt this head in a wired worktree and independently re-derived every ROM claim in the PR body and the notes file. Almost all of it holds, and the return-type evidence here is the strongest the slot campaign has produced. Two things need changing before merge, neither of them a rebuild.

What I reproduced

rombuild.py -j16 --no-rom --data-json at b2ffb512ca64:

source-built functions: 11,088   reproducing: 11,088   mismatching: 0
module fidelity: 106/106 exact, 100.000000% of compared bytes
ROM data from source: 496 verified, 223 partial, 5 differ, 522 unnamed by config

Exactly the counters in the table. Premerge, both bases:

vs origin/cpp/minigame-slot34 @bdb2510ca600   merge tree 4d4dfb4c60c3   8/8 pass -> pass
vs origin/main @5e88bfb5ed0f                  merge tree fe50770a3b71   8/8 pass -> pass

Independently confirmed from the ROM image:

  • Both bodies. ov004:0x020ad660, 20 bytes: ldr r0,[r0,#8] / ands r1,r0,#0xff / movne r0,#1 / moveq r0,#0 / bx lr(this[2] & 0xff) != 0. ov006:0x020d1170, 24 bytes: and r0,r0,#0xff / cmp r0,#1 / moveq r0,#1 / movne r0,#0== 1. Both match the source exactly.
  • Thirteen dispatch sites, all in ov006, found with an 8-word lookahead window and break-on-redefinition (an adjacency scan finds only ten — worth knowing if anyone re-derives this).
  • 13 of 13 followed by cmp r0, #0. The return-type claim is fully earned. This really is the first slot in the range where the ROM is seen consuming the value rather than merely permitting one.
  • Class attribution. All 13 sit inside unnamed func_ov006_* symbols, so I bracketed each with the nearest _ZN symbol before and after in config/arm9/overlays/ov006/symbols.txt. Both brackets agree on all thirteen, and the split is exactly yours: dScMgCoin_c 2, dScMgPanel_c 4, dScMgSound_c 3, dScMgSnowball_c 4.
  • The five-overlay rename hazard, including ov000 — which I checked specifically because overlays.yaml bases ov000 at 0x020aa420 and it looked like an error. It isn't: ov000 spans 0x020ad660 and carries data_ov000_020ad660 … ambiguous there. The list is right and better-evidenced than it first reads.
  • The Slot3 residual, to the byte. _ZTV12dScMgSlot3_c 0x0213eaa8, _ZTV12dScMgSlot1_c 0x0213eb40, gap exactly 152. ROM at 0x0213eb38 is {0x00000000, 0x0213e5a0} and 0x0213e5a0 is _ZTI12dScMgSlot1_c. Your reading of it is correct.

1. The "32 of 33" headline is inflated by one table — and the odd one out is hiding this PR's own slot

Measured at this head, from --data-json, deduped on (module, symbol, addr):

(bytes 144, romExtent 144, VERIFIED)  x30
(bytes 148, romExtent 148, VERIFIED)  _ZTV12dScMgAmida_c
(bytes 144, romExtent  80, VERIFIED)  _ZTV14dScMgD3DBase_c     <-- here
(bytes 144, romExtent 152, PARTIAL )  _ZTV12dScMgSlot3_c
(bytes   8, romExtent   8, VERIFIED)  _ZTVN12dScMgSlot1_c9betIcon_cE  (nested, excluded from the 33)

Thirty-two do score VERIFIED. But the notes say something stronger than that:

Thirty-two of the thirty-three family tables read exactly like that — 144 bytes, 36 slots, emitted length equal to the cartridge's extent — except _ZTV12dScMgAmida_c, which is 148…

That sentence is true of thirty-one. _ZTV14dScMgD3DBase_c emits 144 against a config romExtent of 80, and it scores VERIFIED only because check_symbol's verdict test is len(linked) >= extent — an over-emitting table passes. Its extent is 80 because data_ov006_0213c67c sits inside the table and rom_data_index() sizes every data symbol as distance-to-the-next-symbol.

I dumped all 36 slots. Slots 20–35 are all valid ov006/ov00x code pointers, and the table genuinely ends at 0x0213c6bc with the standard {0, 0x0213c5c8} preamble at V−8:

slot 20  0x213c67c  020b2990   <<< data_ov006_0213c67c — the config extent stops here
slot 21  0x213c680  020b298c
   ...
slot 34  0x213c6b4  020ae3b4   <- #2110's Virtual88 base body
slot 35  0x213c6b8  020ad660   <- THIS PR's Virtual8C base body

Sixteen of its slots are never compared, including slot 34 from #2110 and slot 35 from this commit. That is the "a VERIFIED vtable is not a complete vtable" hazard you name by name two paragraphs later, occurring inside your own count. The delta table is unaffected — D3DBase contributed 0 to the +31 (30 flips + Amida), so the arithmetic is fine; it's the prose claim that overreaches.

Ask: restate as 31 tables read exactly like the sample; 32 score VERIFIED, one of them on a truncated config extent that hides 16 slots (_ZTV14dScMgD3DBase_c, extent 80 of a real 144); 1 remains PARTIAL. Prose only, in both the PR body and notes/dScMgBase_c-slots-18-35.md.

2. The Slot3 fix belongs in romdata_check.py, not in symbols.txt

That is a config/arm9/overlays/ov006/symbols.txt change with its own gates

I argued against exactly this on #2111 a few hours ago, so let me put the measurement here rather than ask you to go read it.

This is not a one-off. On origin/main there are 540 _ZTV symbols, 18 owned by source, 522 unowned, and the two-word mwcc preamble at V−8 belongs to no symbol in any of them. 48 of the 256 live PARTIAL records are partial solely because romExtent − padded_emitted == 8 with a _ZTV as the next symbol. _ZTV12dScMgSlot3_c is one of them — instance #523 of a tool defect, not a config error.

check_symbol already knows about the preamble: it applies preamble = OI.VTABLE_PREAMBLE if name.startswith("_ZTV") else 0 on the emitted side and corrects reloc addends by the same 8. Only rom_data_index() doesn't, and that is a one-line change there — it fixes all 522 at once, and the 18 already-owned tables plus this PR's own 30 flips are its positive control.

A symbols.txt row here would instead be a row the next intact-object promotion of dScMgSlot1_c has to delete again (a promoted TU must drop the V−8 row, and must not invent a name for it — that is settled anti-pattern #3 from #2061).

Ask: drop the "that is a symbols.txt change" sentence and point at rom_data_index() instead. I'll take the tool fix as a separate tools-only PR onto main — this PR gives it a second free positive control, so I'm not asking you to carry it.


Non-blocking

3. "r1 … at every one of the thirteen sites" is false at one site. Twelve load the pointer into r1; 0x020dd9cc loads it into r2:

0x20dd9c4  ldr  r2, [r0]          <- vptr
0x20dd9cc  ldr  r2, [r2, #0x8c]   <- pointer in r2
0x20dd9d8  sub  r7, r1, fp        <- r1 written, but consumed here, before the call
0x20dd9dc  blx  r2

The arity conclusion survives — r1 there is a local computation dead before the blx, r3 is never set, and the other twelve are decisive on their own. Just don't say "every one of the thirteen". (Related: the gap between ldr and blx is 1 at ten sites, 2 at two, 4 at one, so "all the same shape" is a little loose too.)

4. 0x0211b9e0 selects a different table, not "a different field path". Both arms execute the identical add r0,r4,#0x5000 ; ldrb r1,[r0,#0x627] — same field. They diverge only on which literal-pool pointer they load: ldrne r0,[pc,#0x8c]0x0211ba80 holding 0x0212ef7c, vs ldreq r0,[pc,#0x7c]0x0211ba84 holding 0x0212ef8c. So it's the same shape as 0x02105488 (two parallel tables), not a third distinct one. Your other two descriptions are exact: 0x02105488 is ldrne r0,[r8,sb,lsl#2] / ldreq r0,[r6,sb,lsl#2], and 0x02126f58 is beq #0x2126f7c, skipping 8 instructions.

5. Column-0 line inside an indented comment blockinclude/dScMgBase_c.h:

+ALL EIGHTEEN ARE DECLARED (2026-08-31). This class and all 32 of
+       its descendants emit their full 36-slot vtables from source; …

The first line starts at column 0 inside a block indented 7 spaces, which reads at a glance as if the comment had terminated. Re-indent it.


Verdict

Approve on content. The slot itself is correct, the bodies are byte-verified, the arity and return type are both properly earned, and the range really is complete. Fixes 1 and 2 are prose-only; 3–5 are notes. No rebuild needed for any of them — I'll re-read and merge once they're in.

Nice piece of work on the dispatch evidence in particular. Thirteen sites across four classes with a uniform cmp r0,#0 afterwards is the first time in this campaign the ROM has answered the return-type question instead of just declining to contradict it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

attribution-override Maintainer accepts this PR's contributor-credit changes; validation reports them as warnings

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant