Skip to content

Declare the Sound namespace in a header instead of per file - #2727

Merged
andrewboudreau merged 1 commit into
mainfrom
cpp/sound-decls
Sep 18, 2026
Merged

andrewboudreau merged 1 commit into
mainfrom
cpp/sound-decls

Conversation

@andrewboudreau

Copy link
Copy Markdown
Collaborator

31 call sites reached Sound:: through a mangled-name extern each file wrote for itself. This replaces those copies with one set of declarations in include/Sound.h.

The copies had already drifted: daBmb_c.cpp and daObjPushblock_c.cpp both declared PlayLong returning u32, while the definition at src/_ZN5Sound8PlayLongEjjjRK7Vector3s.cpp:61 returns int. Adopting the header made that an ambiguous overload, so both stale declarations are removed here. Worth noting for anyone doing the same elsewhere: only the full rombuild catches this class of break — it reds files the PR never touches, and a per-file build_pin.verify never sees it.

Two functions deliberately left alone

Sound::PlaySub and Sound::ChangeMusicVolume keep the extern spelling. Their definitions are mangled-name C symbols carrying 5Fix12IiE, and no type this tree defines mangles to that — types.h refuses to make Fix12 a template, and a by-value Fix12<int> parameter would contradict the bytes anyway. A namespace-qualified call cannot reach them, so declaring them just breaks the link. The header says so, in place.

struct dActor_c is forward-declared at global scope on purpose. Inside namespace Sound it would declare Sound::dActor_c, which is not the type any caller holds.

The two config/ rows are a renumber, not a rename

d_a_obj_fm_battan.cpp mentions none of the added declarations and is untouched by this PR, but its intact-TU prep failed with binding rewrite symbol @348 has 0 entries. mwccarm numbers file-local entities in parse order, so adding declarations to a shared header renumbers them in every TU that includes it. tools/tu_manifest_resync.py paired both rows by position with section and size agreeing entry-for-entry; only the numbers move — addresses, sizes and evidence are unchanged.

Gates

gate result
rombuild --no-rom 106/106 exact, mismatching: 0, ROM-build analysis: PASS, 59 compiled
tiers_ratchet --check PASS — CONVERTED 2812 to 2822
eligible.py name set identical, 11243 / 11302
check_decl_agreement no new disagreements; 34 banked ones disappear
port_refcheck 423 references, 0 stale
MSVC port smoke build 12/12, all five targets link

The decl-agreement baseline is not updated here — the 34 vanished rows are a pure shrink and the gate does not fail on them, so this avoids adding churn to a known rebase chokepoint.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Y4FxdKHG3a6hQRYRAbx29c

Thirty-one call sites reached Sound:: through a mangled-name extern each
file wrote for itself. Those copies had already drifted -- daBmb_c.cpp and
daObjPushblock_c.cpp both declared PlayLong returning u32, while the
definition returns int -- so the drift is removed here too.

Sound::PlaySub and Sound::ChangeMusicVolume deliberately stay on the extern
spelling. Their definitions are mangled-name C symbols carrying 5Fix12IiE,
and no type this tree defines mangles to that (types.h refuses to make Fix12
a template), so a namespace-qualified call cannot reach them at all.

struct dActor_c is forward-declared at GLOBAL scope on purpose: inside
namespace Sound it would name Sound::dActor_c, which is not the type any
caller holds.

The two config/ rows are a compiler renumber, not a rename. mwccarm numbers
file-local entities in parse order, so adding declarations to a header shifts
them in every TU that includes it -- including d_a_obj_fm_battan.cpp, which
mentions none of them. tools/tu_manifest_resync.py paired both rows by
position with section and size matching; addresses, sizes and evidence are
untouched.

Gates: 106/106 exact, mismatching 0, ROM-build analysis PASS (59 compiled).
CONVERTED 2812 -> 2822. Eligible set identical at 11243. No new declaration
disagreements, and 34 banked ones disappear. port_refcheck 423/423, MSVC
port smoke build 12/12.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y4FxdKHG3a6hQRYRAbx29c
@tangos-validator

tangos-validator Bot commented Sep 16, 2026

Copy link
Copy Markdown

✅ PR validation — Passed

Committed merge introduces no reconstruction or attribution regression.

Full merge validation

Check Result
Committed test merge yes
Byte-verified functions 11,177 / 11,344 (98.53%, +0)
Byte-verified code bytes 2,136,384 / 2,211,124 (96.62%, +0)
Claimed, not byte-verified 147 functions, 51,284 bytes (+0)
Perfect source moves 0 R100
Contributor credit 0 added, 0 changed, 0 lost
Relocation check 323 checked; 2 BLIND, 321 VERIFIED
Port reference check 423 checked; 0 stale
Module fidelity 106/106 exact; 100.000000% compared bytes
Code linked from verified source 11,206 functions, 2,150,424 bytes (96.08%)
Module bytes from source 2,150,424 / 3,049,600 (70.5%); 806,508 (26.4%) are data no delink entry reaches
Retail-gap contribution 894,192 module bytes (87,684 function-code; 806,508 data/non-function)
ROM data reproduced from source 717 symbol(s) exact, 231 partial, 4 differ

Byte-verified means the range carries complete in a delinks.txt, so the ROM build compiled it and compared it to the cartridge. The 147 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,324 this project calls matched.

Warnings: 2 linkcheck result(s) have unresolved relocations.

Per-file link-check detail

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

File Symbol Result Slots checked
src/_ZN10PyramidTop8BehaviorEv.cpp _ZN10PyramidTop8BehaviorEv ✅ verified 1
src/_ZN10dScEntry_c16CleanupResourcesEv.cpp _ZN10dScEntry_c16CleanupResourcesEv ✅ verified 1
src/_ZN10dScTitle_c16CleanupResourcesEv.cpp _ZN10dScTitle_c16CleanupResourcesEv ✅ verified 1
src/_ZN11BillBlaster4KillEv.cpp _ZN11BillBlaster4KillEv ✅ verified 1
src/_ZN11CrazedCrate13OnTurnIntoEggER6Player.cpp _ZN11CrazedCrate13OnTurnIntoEggER6Player ✅ verified 1
src/_ZN11dScMiniGm_c16CleanupResourcesEv.cpp _ZN11dScMiniGm_c16CleanupResourcesEv ✅ verified 1
src/_ZN11dScMiniGm_c8BehaviorEv.cpp _ZN11dScMiniGm_c8BehaviorEv ✅ verified 1
src/_ZN12FortressWall4KillEv.cpp _ZN12FortressWall4KillEv ✅ verified 1
src/_ZN12dScStarSel_c13InitResourcesEv.cpp _ZN12dScStarSel_c13InitResourcesEv ✅ verified 1
src/_ZN13BasementWater8BehaviorEv.cpp _ZN13BasementWater8BehaviorEv ✅ verified 1
src/_ZN13BigBrickBlock4KillEv.cpp _ZN13BigBrickBlock4KillEv ✅ verified 1
src/_ZN13KoopaTheQuick16CleanupResourcesEv.cpp _ZN13KoopaTheQuick16CleanupResourcesEv ✅ verified 1
src/_ZN14ArrowSignRight4KillEv.cpp _ZN14ArrowSignRight4KillEv ✅ verified 1
src/_ZN14KnockDownPlank8BehaviorEv.cpp _ZN14KnockDownPlank8BehaviorEv ✅ verified 1
src/_ZN15dScMgSnowball_c8BehaviorEv.cpp _ZN15dScMgSnowball_c8BehaviorEv ✅ verified 1
src/_ZN5Sound17InfoSequenceEntry9GetWithIDEj.cpp _ZN5Sound17InfoSequenceEntry9GetWithIDEj ✅ verified 1
src/_ZN5Sound23InfoInstrumentBankEntry9GetWithIDEj.cpp _ZN5Sound23InfoInstrumentBankEntry9GetWithIDEj ✅ verified 1
src/_ZN5Sound6Player19SetPlayableSeqCountEii.cpp _ZN5Sound6Player19SetPlayableSeqCountEii ✅ verified 1
src/_ZN5Stage10PS_CleanupEv.cpp _ZN5Stage10PS_CleanupEv ✅ verified 1
src/_ZN5Stage7PS_InitEv.cpp _ZN5Stage7PS_InitEv ✅ verified 1
src/_ZN6Player13St_Throw_InitEv.cpp _ZN6Player13St_Throw_InitEv ✅ verified 1
src/_ZN6Player14St_Thrown_InitEv.cpp _ZN6Player14St_Thrown_InitEv ✅ verified 1
src/_ZN6Player16St_BurnFire_InitEv.cpp _ZN6Player16St_BurnFire_InitEv ✅ verified 1
src/_ZN6Player16St_BurnLava_InitEv.cpp _ZN6Player16St_BurnLava_InitEv ✅ verified 1
src/_ZN6Player16St_LongJump_InitEv.cpp _ZN6Player16St_LongJump_InitEv ✅ verified 1
src/_ZN6Player17St_HoldHeavy_InitEv.cpp _ZN6Player17St_HoldHeavy_InitEv ✅ verified 1
src/_ZN6Player17St_LedgeGrab_InitEv.cpp _ZN6Player17St_LedgeGrab_InitEv ✅ verified 1
src/_ZN6Player19St_Electrocute_InitEv.cpp _ZN6Player19St_Electrocute_InitEv ✅ verified 1
src/_ZN6Player19St_GroundPound_InitEv.cpp _ZN6Player19St_GroundPound_InitEv ✅ verified 1
src/_ZN6Player19St_GroundPound_MainEv.cpp _ZN6Player19St_GroundPound_MainEv ✅ verified 1
src/_ZN6Player24St_SlideKickRecover_InitEv.cpp _ZN6Player24St_SlideKickRecover_InitEv ✅ verified 1
src/_ZN8IceBlock4KillEv.cpp _ZN8IceBlock4KillEv ✅ verified 1
src/_ZN8SignPost4KillEv.cpp _ZN8SignPost4KillEv ✅ verified 1
src/_ZN9BootScene13InitResourcesEv.cpp _ZN9BootScene13InitResourcesEv ✅ verified 1
src/_ZN9TinyWater8BehaviorEv.cpp _ZN9TinyWater8BehaviorEv ✅ verified 1
src/_ZN9TowerStep4KillEv.cpp _ZN9TowerStep4KillEv ✅ verified 1
src/_ZN9dScDSMT_c16CleanupResourcesEv.cpp _ZN9dScDSMT_c16CleanupResourcesEv ✅ verified 1
src/_ZN9dScDSMT_c8BehaviorEv.cpp _ZN9dScDSMT_c8BehaviorEv 🔶 blind (a reloc slot could not be resolved) 1
src/actors/dBgActor_c.cpp _ZN10dBgActor_cD1Ev + _ZN10dBgActor_cD0Ev + _ZN10dBgActor_c14KillByMegaCharER6Player + _ZN10dBgActor_c4KillEv + func_ov002_020ee5d0 + _ZN10dBgActor_c20UpdateKillByMegaCharEsss5Fix12IiE + _ZN10dBgActor_c19UpdateClsnPosAndRotEv + _ZN10dBgActor_c21UpdateModelPosAndRotYEv + _ZN10dBgActor_c13IsClsnInRangeE5Fix12IiES1_ + _ZN10dBgActor_c21IsClsnInRangeOnScreenE5Fix12IiES1_ + _ZN10dBgActor_cC2Ev ✅ verified 11
src/actors/da1up_c.cpp _ZN7da1up_cD1Ev + _ZN7da1up_cD0Ev + func_ov002_020aeee4 + func_ov002_020aefa4 + func_ov002_020aefb8 + func_ov002_020af0c0 + func_ov002_020af1dc + func_ov002_020af218 + func_ov002_020af248 + _ZN7da1up_c13OnTurnIntoEggER6Player + _ZN7da1up_c13OnYoshiTryEatEv + func_ov002_020af3a8 + func_ov002_020af474 + func_ov002_020af4ec + func_ov002_020af684 + func_ov002_020af724 + func_ov002_020af7cc + func_ov002_020af838 + func_ov002_020af908 + func_ov002_020af924 + func_ov002_020af950 + func_ov002_020afa50 + func_ov002_020afa6c + func_ov002_020afa98 + func_ov002_020afbb4 + func_ov002_020afc44 + func_ov002_020afc68 + func_ov002_020afd10 + func_ov002_020afde4 + func_ov002_020afe4c + func_ov002_020aff10 + _ZN7da1up_c16CleanupResourcesEv + _ZN7da1up_c16OnPendingDestroyEv + _ZN7da1up_c6RenderEv + _ZN7da1up_c8BehaviorEv + _ZN7da1up_c13InitResourcesEv + da1up_c_classInit_SCALEUP_KINOKO + da1up_c_classInit_ONEUPKINOKO ✅ verified 38
src/actors/daBmb_c.cpp _ZN7daBmb_cD1Ev + _ZN7daBmb_cD0Ev + _ZN7daBmb_c16OnAimedAtWithEggEv + func_ov102_0214aa18 + func_ov102_0214ab1c + func_ov102_0214ad14 + func_ov102_0214ad40 + _ZN7daBmb_c13OnTurnIntoEggER6Player + func_ov102_0214ae1c + func_ov102_0214b03c + func_ov102_0214b128 + func_ov102_0214b248 + func_ov102_0214b384 + func_ov102_0214b3b8 + func_ov102_0214b3f0 + func_ov102_0214b444 + func_ov102_0214b53c + func_ov102_0214b988 + _ZN7daBmb_c6State5Ev + func_ov102_0214baa0 + _ZN7daBmb_c6State4Ev + func_ov102_0214bc20 + _ZN7daBmb_c6State3Ev + func_ov102_0214bd20 + func_ov102_0214bd90 + _ZN7daBmb_c6State1Ev + func_ov102_0214beb4 + func_ov102_0214bf64 + func_ov102_0214c0b8 + _ZN7daBmb_c16CleanupResourcesEv + _ZN7daBmb_c6RenderEv + _ZN7daBmb_c8BehaviorEv + _ZN7daBmb_c13InitResourcesEv + _ZN7daBmb_c13OnYoshiTryEatEv + daBmb_c_classInit ✅ verified 35
src/actors/daObjCtMecha03_c.cpp _ZN16daObjCtMecha03_cD1Ev + _ZN16daObjCtMecha03_cD0Ev + func_ov065_02119fe8 + func_ov065_0211a114 + _ZN16daObjCtMecha03_c16CleanupResourcesEv + _ZN16daObjCtMecha03_c6RenderEv + _ZN16daObjCtMecha03_c8BehaviorEv + _ZN16daObjCtMecha03_c13InitResourcesEv + daObjCtMecha03_c_classInit ✅ verified 9
src/actors/daObjFallBlock_c.cpp func_ov098_0213a00c + func_ov098_0213a0a8 + func_ov098_0213a0e8 + func_ov098_0213a148 + _ZN16daObjFallBlock_c4KillEv + func_ov098_0213a23c + _ZN16daObjFallBlock_c15OnHitByMegaCharER6Player + func_ov098_0213a2cc + _ZN16daObjFallBlock_c6RenderEv + _ZN16daObjFallBlock_c8BehaviorEv ✅ verified 10
src/actors/daObjFl_London_c.cpp _ZN16daObjFl_London_cD1Ev + _ZN16daObjFl_London_cD0Ev + func_ov022_02111a1c + _ZN16daObjFl_London_c16CleanupResourcesEv + _ZN16daObjFl_London_c6RenderEv + _ZN16daObjFl_London_c8BehaviorEv + _ZN16daObjFl_London_c13InitResourcesEv + daObjFl_London_c_classInit ✅ verified 8
src/actors/daObjMarioCap_c.cpp _ZN15daObjMarioCap_cD1Ev + _ZN15daObjMarioCap_cD0Ev + func_ov002_020b6fcc + func_ov002_020b71e8 + func_ov002_020b71f0 + func_ov002_020b7200 + func_ov002_020b7330 + func_ov002_020b74d0 + func_ov002_020b76ec + func_ov002_020b781c + func_ov002_020b7b70 + func_ov002_020b7c30 + func_ov002_020b7cdc + func_ov002_020b7cec + func_ov002_020b7d58 + func_ov002_020b7d6c + func_ov002_020b7d94 + func_ov002_020b7d9c + func_ov002_020b7e08 + func_ov002_020b7e1c + func_ov002_020b7f24 + func_ov002_020b7f2c + func_ov002_020b7f7c + _ZN15daObjMarioCap_c13OnTurnIntoEggER6Player + _ZN15daObjMarioCap_c13OnYoshiTryEatEv + _ZN15daObjMarioCap_c16CleanupResourcesEv + _ZN15daObjMarioCap_c16OnPendingDestroyEv + _ZN15daObjMarioCap_c6RenderEv + _ZN15daObjMarioCap_c8BehaviorEv ✅ verified 29
src/func_02051fb4.cpp func_02051fb4 ✅ verified 1
src/game/actors/d_a_obj_abuku.cpp _ZN12daObjAbuku_cD1Ev + _ZN12daObjAbuku_cD0Ev + func_ov002_020b330c + func_ov002_020b3344 + _ZN12daObjAbuku_c8BehaviorEv + _ZN12daObjAbuku_c13InitResourcesEv + daObjAbuku_c_classInit ✅ verified 7
src/game/actors/d_a_obj_bk_dossunbar.cpp _ZN19daObjBk_Dossunbar_cD1Ev + _ZN19daObjBk_Dossunbar_cD0Ev + _ZN19daObjBk_Dossunbar_c4KillEv + _ZN19daObjBk_Dossunbar_c15OnHitByMegaCharER6Player + func_ov015_02111ce0 + func_ov015_02111d28 + func_ov015_02111d4c + func_ov015_02111d8c + func_ov015_02111d98 + func_ov015_02111dd4 + func_ov015_02111df4 + func_ov015_02111e60 + func_ov015_02111e80 + func_ov015_02111ee0 + func_ov015_02111eec + func_ov015_02111f4c + func_ov015_02111f6c + func_ov015_02111fac + func_ov015_02111fb8 + _ZN19daObjBk_Dossunbar_c16CleanupResourcesEv + _ZN19daObjBk_Dossunbar_c6RenderEv + _ZN19daObjBk_Dossunbar_c8BehaviorEv + _ZN19daObjBk_Dossunbar_c13InitResourcesEv + daObjBk_Dossunbar_c_classInit_BK_DOSSUNBAR_L + daObjBk_Dossunbar_c_classInit_BK_DOSSUNBAR_S ✅ verified 26 (+1 passenger)
src/game/actors/d_a_obj_c1_trap.cpp _ZN14daObjC1_Trap_cD1Ev + _ZN14daObjC1_Trap_cD0Ev + _ZN14daObjC1_Trap_c6State4Ev + _ZN14daObjC1_Trap_c6State3Ev + _ZN14daObjC1_Trap_c6State2Ev + _ZN14daObjC1_Trap_c6State1Ev + _ZN14daObjC1_Trap_c6State0Ev + _ZN14daObjC1_Trap_c10GetSpawnerEv + _ZN14daObjC1_Trap_c24UpdateCollisionTransformEv + _ZN14daObjC1_Trap_c20UpdateModelTransformEv + _ZN14daObjC1_Trap_c16CleanupResourcesEv + _ZN14daObjC1_Trap_c6RenderEv + _ZN14daObjC1_Trap_c8BehaviorEv + _ZN14daObjC1_Trap_c13InitResourcesEv + _ZN14daObjC1_Trap_c11OnCollisionER8dActor_c + func_ov010_02111984 + daObjC1_Trap_c_classInit ✅ verified 17
src/game/actors/d_a_obj_clock_huriko.cpp _ZN18daObjClockHuriko_cD1Ev + _ZN18daObjClockHuriko_cD0Ev + _ZN18daObjClockHuriko_c16CleanupResourcesEv + func_ov013_02111238 + _ZN18daObjClockHuriko_c6RenderEv + _ZN18daObjClockHuriko_c8BehaviorEv + _ZN18daObjClockHuriko_c13InitResourcesEv + daObjClockHuriko_c_classInit ✅ verified 8
src/game/actors/d_a_obj_fm_battan.cpp _ZN16daObjFm_Battan_cD1Ev + _ZN16daObjFm_Battan_cD0Ev + _ZN16daObjFm_Battan_c12UpdateShadowEv + _ZN16daObjFm_Battan_c20UpdateModelTransformEv + _ZN16daObjFm_Battan_c16CleanupResourcesEv + _ZN16daObjFm_Battan_c6RenderEv + _ZN16daObjFm_Battan_c8BehaviorEv + _ZN16daObjFm_Battan_c13InitResourcesEv + daObjFm_Battan_c_classInit + __sinit_d_a_obj_fm_battan.cpp 🔶 blind (a reloc slot could not be resolved) 10
src/game/actors/d_a_obj_hatena_switch.cpp _ZN19daObjHatenaSwitch_cD1Ev + _ZN19daObjHatenaSwitch_cD0Ev + _ZN19daObjHatenaSwitch_c15OnGroundPoundedER8dActor_c + _ZN19daObjHatenaSwitch_c15UpdateClsnStateEv + _ZN19daObjHatenaSwitch_c19UpdateClsnTransformEv + _ZN19daObjHatenaSwitch_c20UpdateModelTransformEv + _ZN19daObjHatenaSwitch_c16CleanupResourcesEv + _ZN19daObjHatenaSwitch_c6RenderEv + _ZN19daObjHatenaSwitch_c8BehaviorEv + _ZN19daObjHatenaSwitch_c13InitResourcesEv + _ZN19daObjHatenaSwitch_c10HandleClsnER8dActor_c + _ZN19daObjHatenaSwitch_c17AfterClsnCallbackEP4dBgWP8dActor_cS3_ + daObjHatenaSwitch_c_classInit ✅ verified 13
src/game/actors/d_a_obj_ice_board.cpp _ZN15daObjIceBoard_cD1Ev + _ZN15daObjIceBoard_cD0Ev + _ZN15daObjIceBoard_c15OnHitByMegaCharER6Player + _ZN15daObjIceBoard_c4KillEv + _ZN15daObjIceBoard_c15OnGroundPoundedER8dActor_c + _ZN15daObjIceBoard_c16CleanupResourcesEv + _ZN15daObjIceBoard_c6RenderEv + _ZN15daObjIceBoard_c8BehaviorEv + _ZN15daObjIceBoard_c13InitResourcesEv + daObjIceBoard_c_classInit ✅ verified 10
src/game/actors/d_a_obj_ks_water.cpp _ZN14daObjKsWater_cD1Ev + _ZN14daObjKsWater_cD0Ev + _ZN14daObjKsWater_c16CleanupResourcesEv + _ZN14daObjKsWater_c6RenderEv + _ZN14daObjKsWater_c8BehaviorEv + _ZN14daObjKsWater_c13InitResourcesEv + daObjKsWater_c_classInit ✅ verified 7
src/game/actors/d_a_obj_rc_hane.cpp _ZN14daObjRc_Hane_cD1Ev + _ZN14daObjRc_Hane_cD0Ev + _ZN14daObjRc_Hane_c16CleanupResourcesEv + func_ov036_02111618 + _ZN14daObjRc_Hane_c6RenderEv + _ZN14daObjRc_Hane_c8BehaviorEv + _ZN14daObjRc_Hane_c13InitResourcesEv + daObjRc_Hane_c_classInit ✅ verified 8
src/game/actors/d_a_obj_wanwan_shutter.cpp _ZN20daObjWanwanShutter_cD1Ev + _ZN20daObjWanwanShutter_cD0Ev + func_ov014_02112ea8 + _ZN20daObjWanwanShutter_c16CleanupResourcesEv + _ZN20daObjWanwanShutter_c6RenderEv + _ZN20daObjWanwanShutter_c8BehaviorEv + _ZN20daObjWanwanShutter_c13InitResourcesEv + daObjWanwanShutter_c_classInit ✅ verified 8
src/game/actors/d_a_obj_wc_mizu.cpp _ZN14daObjWc_Mizu_cD1Ev + _ZN14daObjWc_Mizu_cD0Ev + func_ov029_02112250 + func_ov029_021122b4 + _ZN14daObjWc_Mizu_c16CleanupResourcesEv + _ZN14daObjWc_Mizu_c6RenderEv + _ZN14daObjWc_Mizu_c8BehaviorEv + _ZN14daObjWc_Mizu_c13InitResourcesEv + daObjWc_Mizu_c_classInit ✅ verified 9
src/game/actors/d_a_obj_wc_obj03.cpp _ZN15daObjWc_Obj03_cD1Ev + _ZN15daObjWc_Obj03_cD0Ev + _ZN15daObjWc_Obj03_c19CheckClsnWithPlayerEv + _ZN15daObjWc_Obj03_c10SetWaterIDEv + _ZN15daObjWc_Obj03_c20UpdateModelTransformEv + _ZN15daObjWc_Obj03_c16CleanupResourcesEv + _ZN15daObjWc_Obj03_c6RenderEv + _ZN15daObjWc_Obj03_c8BehaviorEv + _ZN15daObjWc_Obj03_c13InitResourcesEv + daObjWc_Obj03_c_classInit ✅ verified 10
src/game/actors/daObjPushblock_c.cpp _ZN16daObjPushblock_cD1Ev + _ZN16daObjPushblock_cD0Ev + _ZN16daObjPushblock_c4KillEv + _ZN16daObjPushblock_c15OnHitByMegaCharER6Player + _ZN16daObjPushblock_c8OnPushedER8dActor_c + _ZN16daObjPushblock_c16CleanupResourcesEv + _ZN16daObjPushblock_c6RenderEv + _ZN16daObjPushblock_c8BehaviorEv + _ZN16daObjPushblock_c13InitResourcesEv + daObjPushblock_c_classInit ✅ verified 10
src/game/actors/daTBasket_c/_ZN11daTBasket_c8BehaviorEv.cpp _ZN11daTBasket_c8BehaviorEv ✅ verified 1
  • src/game/actors/d_a_obj_bk_dossunbar.cpp also verified 1 emitted passenger(s): _ZN7Vector3D1Ev

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

Merge-tree check: main + #2725 + #2726 + #2727 is green.

Each of these PRs is gated alone, so green-alone does not imply green-merged. I built the three-way merge locally and ran the full set:

gate merge tree
rombuild --no-rom 106/106 exact, mismatching: 0, PASS (22 compiled)
tiers_ratchet --check PASS, CONVERTED 2812 to 2887 (+75)
check_decl_agreement no new disagreements; 76 banked ones disappear
eligible.py name set identical, 11243 / 11302
port_refcheck 423 references, 0 stale
MSVC port smoke build 230/230 from a clean configure, all five targets link

File sets are disjoint across all three pairs, so there is no textual conflict either.

One thing specifically checked, since it would have been invisible otherwise: #2727 carries a tu_manifest_resync renumber for ov023/daObjFm_Battan_c, and mwccarm uniquifier shifts compose additively. #2725 edits include/SharedFilePtr.h, which battan also includes, so the merged tree could have emitted a third number that neither branch names. It does not -- tu_manifest_resync --check reports the manifest in sync on the merge tree. Merge order does not matter here.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Y4FxdKHG3a6hQRYRAbx29c

@andrewboudreau

Copy link
Copy Markdown
Collaborator Author

Five-way merge tree gated (main + #2725 + #2726 + #2727 + #2728 + #2729)

Each of these five PRs is green on its own, but this repo has produced a red main from
two individually-green PRs before, so the set was gated as a merged tree rather than as
five branches.

Disjointness. 122 changed files across the five; all 10 pairs share zero files,
and the five branches merge onto 013370480 with no conflict.

Cold rebuild (rombuild.py -j 16 --no-rom --no-cache, all 8,760 enrolled files
compiled from scratch):

source-built functions: 11,206   reproducing: 11,206   mismatching: 0
module fidelity: 106/106 exact, 100.000000% of compared bytes
ROM-build analysis: PASS

The rebuild is deliberately cold. A warm run on this same tree reported
8759 reused from cache, 1 compiled — the objects were already built on the individual
branches and the cache is content-keyed, so the compile step exercised nothing. 0/1 compiled is this repo's classic false-green, so the numbers above come from a run with
no cache at all.

tools/tu_manifest_resync.py --check ov023/daObjFm_Battan_c reads in sync on the
merged tree — that is the manifest that shifted earlier in this batch when a shared
header gained declarations.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Y4FxdKHG3a6hQRYRAbx29c

@andrewboudreau

Copy link
Copy Markdown
Collaborator Author

Adversarial review — seven-PR set (#2725#2731)

Re-reviewed as a critic rather than as the author. Every check below is new work, not a
restatement of the PR's own gate. Head SHAs reviewed: 2725 4f3927bd9, 2726 81040f19c,
2727 4c08d9a4d, 2728 00b15daef, 2729 fe61a984c, 2730 38aaa986b, 2731 e9b49b072;
merge tree a7f68c8b3 off 013370480.

1. Relocation destinations — the hazard the byte gate cannot see

A relocated word is a wildcard: a bl retargeted to a different but valid symbol still
byte-matches. Every verification run on these PRs so far used build_pin.verify without
the strict tuple, so none of them checked this. Re-ran all of them with
strict=(reloc_audit, name_index, config_relocs, sym_index), which resolves each relocation
and fails on WRONG-DEST:

PR files result
2725 26 26/26 OK
2726 42 42/42 OK
2727 27 + 4 promoted TUs (61 fns) 27/27 + 61/61 OK
2728 10 10/10 OK
2729 7 7/7 OK
2730 13 13/13 OK
2731 header-only n/a

0 WRONG-DEST across 125 files and 61 TU functions. This also closes the cast-receiver
question on #2726/#2728: ((X *)p)->X::M() cast to the wrong class would resolve to a
different symbol, and that is exactly what this check would have caught.

2. Merge tree, cold

mergetest/seven = all seven merged onto 013370480, 0 overlapping file pairs,
rombuild.py -j16 --no-rom --no-cache (the [3/6] line prints no cache line, so the
enrolled stage really did run cold):

reproducing: 11,206   mismatching: 0
source-owned data claims: 26  (reproducing 26, mismatching 0)
module fidelity: 106/106 exact, 100.000000% of compared bytes
ROM-build analysis: PASS

3. Change-kind census

139 files changed in the merge tree, all M — zero adds, deletes or renames. The whole
rename-hazard family (stranded port/ references, phantom file citations, stem collisions,
a stranded near-miss bank) is therefore structurally absent, not merely untriggered.

4. Local-only gates, run on the merge tree (CI runs none of these on a merge tree)

  • port_refcheck.py423/423 references resolve, 0 stale (manifests 249, cmake-symbols 21, hal-links 153).
  • check_decl_agreement.pyno new declaration disagreements; 80 banked ones are now gone.
  • langmode_audit.py --check (baseline from chaos-data) — PASS. local struct body 1676 → 1660.
  • check_decl_return_types.py — output byte-identical to origin/main, so its 3 conflicting
    symbols are pre-existing and not introduced here.

5. The merged decl baseline is not a silent clobber

config/decl-agreement-baseline.json takes edits from several of these branches and git merges
it line-wise, which can silently drop meaning. Regenerated it from scratch on the merge tree and
compared structurally against the merged copy:

merged symbols: 3891   regenerated: 3887
in MERGED but not regenerated (stale/banked, harmless):  4
in REGENERATED but not merged (a real regression the merge hid):  0

0 hidden. Every difference is in the safe direction. (Separately: ~84 stale banked entries
could be pruned — worth its own PR, not worth touching here, since a baseline edit would conflict
across all seven branches.)

6. The MSVC port build — differential, not absolute

main's port build is red and no CI job builds it, so "the port is red" proves nothing about
these PRs on its own. Made it a differential instead.

LNK2001: 106    LNK2019: 30    LNK2005: 22    LNK1120: 9      <- identical in both
distinct failing symbols: 8 vs 8, set difference in both directions: EMPTY
full LNK line sets: IDENTICAL

The other six PRs introduce zero new port link errors. What remains is the pre-existing
Model / ModelBase / ShadowModel Destructor0/Destructor1 C1/C2 collapse, which needs its
own port/hal change and is untouched by this set.

7. Per-PR findings

Verdict

No defect found, and no change requested. Both incomplete migrations are incomplete on
purpose and say so in their titles (#2725 converts 26 of ~920 references; #2727 lands the header
ahead of the bulk of the call-site conversion) — that is slicing, not a gap.

The one thing this review cannot supply is the thing the review gate actually wants: I am the
author of these PRs, so none of the above is independent. All seven still show
reviewDecision: <empty> and need a human to look at them.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Y4FxdKHG3a6hQRYRAbx29c

@andrewboudreau
andrewboudreau merged commit 3fb9112 into main Sep 18, 2026
16 of 18 checks passed
@andrewboudreau
andrewboudreau deleted the cpp/sound-decls branch September 18, 2026 05:07
andrewboudreau pushed a commit to lunavyqo/sm64ds-decomp that referenced this pull request Sep 18, 2026
The six one-function BasementWater sources and the d_a_obj_c0_water.c
factory become one class TU under the cartridge's own RTTI name. ov012
0x02111450..0x02111768 is now a single `complete` delinks range, and
symbols.txt drops the coined _ZTV13BasementWater alias that shared a word
with _ZTV14daObjC0Water_c.

Rebased onto main, which had landed changes in two of the absorbed
sources. Both are carried forward rather than reverted:

  - _ZN13BasementWater6RenderEv.cpp (tangosdev#2728) traded the mangled
    _ZN18TextureTransformer6UpdateER15ModelComponents for a real member
    call. The absorbed body already spells it
    `mTextureTransformer.Update(mModel.data)` on named members, and Update
    is non-virtual -- _ZTV18TextureTransformer at 0x0208e7c4 holds only the
    destructor pair -- so this is the direct call the ROM has, not a
    vtable dispatch.

  - _ZN13BasementWater8BehaviorEv.cpp (tangosdev#2727) replaced the per-file Sound
    externs with Sound.h. The absorbed body had the PlaySecretSound half
    but still declared its own `namespace Sound` block and still called
    PlayLong by mangled name, so both are ported here: the TU includes
    Sound.h and calls Sound::PlayLong. The file comment claiming Sound.h
    has no PlayLong was stale and is corrected.

The class rename had also rewritten historical file citations into paths
that never existed -- `src/_ZN14daObjC0Water_c*.cpp` in this entry's
`legacy_source` and `substitutedObjectPaths`, and in the
notes/data/tu-merge-candidates.json census row. Those name the files that
were absorbed, which were the BasementWater spellings; they are restored,
matching how ov012/daObjC0_Switch_c.json records its own fold.

config/decl-agreement-baseline.json is edited to this PR's own entries
only -- 11 removed with the deleted sources, 5 added for the class TU --
rather than regenerated, so main's unrelated staleness is not laundered
through here. converted-baseline.json keeps its sorted shape: 3 paths out,
3 #symbol identities in, count unchanged at 2813.

rombuild -j16 --no-rom --no-cache: 8,749 enrolled sources compiled cold,
11,206 source-built functions, 11,206 reproducing, 0 mismatching, 106/106
modules exact at 100.000000%, ROM-build analysis PASS. check_decl_agreement,
check_dead_references, check_src_tu and check_tubuild_conflicts --list all
pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G1Gdj5fTjMsW2VjRLpXxYA
andrewboudreau pushed a commit to lunavyqo/sm64ds-decomp that referenced this pull request Sep 18, 2026
The six one-function BasementWater sources and the d_a_obj_c0_water.c
factory become one class TU under the cartridge's own RTTI name. ov012
0x02111450..0x02111768 is now a single `complete` delinks range, and
symbols.txt drops the coined _ZTV13BasementWater alias that shared a word
with _ZTV14daObjC0Water_c.

Rebased onto main, which had landed changes in two of the absorbed
sources. Both are carried forward rather than reverted:

  - _ZN13BasementWater6RenderEv.cpp (tangosdev#2728) traded the mangled
    _ZN18TextureTransformer6UpdateER15ModelComponents for a real member
    call. The absorbed body already spells it
    `mTextureTransformer.Update(mModel.data)` on named members, and Update
    is non-virtual -- _ZTV18TextureTransformer at 0x0208e7c4 holds only the
    destructor pair -- so this is the direct call the ROM has, not a
    vtable dispatch.

  - _ZN13BasementWater8BehaviorEv.cpp (tangosdev#2727) replaced the per-file Sound
    externs with Sound.h. The absorbed body had the PlaySecretSound half
    but still declared its own `namespace Sound` block and still called
    PlayLong by mangled name, so both are ported here: the TU includes
    Sound.h and calls Sound::PlayLong. The file comment claiming Sound.h
    has no PlayLong was stale and is corrected.

The class rename had also rewritten historical file citations into paths
that never existed -- `src/_ZN14daObjC0Water_c*.cpp` in this entry's
`legacy_source` and `substitutedObjectPaths`, and in the
notes/data/tu-merge-candidates.json census row. Those name the files that
were absorbed, which were the BasementWater spellings; they are restored,
matching how ov012/daObjC0_Switch_c.json records its own fold. The
manifest note describing a "PlayLong seam kept below" is updated with
it: there is no mangled seam here any more.

config/decl-agreement-baseline.json is edited to this PR's own entries
only -- 11 removed with the deleted sources, 5 added for the class TU --
rather than regenerated, so main's unrelated staleness is not laundered
through here. converted-baseline.json keeps its sorted shape: 3 paths out,
3 #symbol identities in, count unchanged at 2813.

rombuild -j16 --no-rom --no-cache: 8,749 enrolled sources compiled cold,
11,206 source-built functions, 11,206 reproducing, 0 mismatching, 106/106
modules exact at 100.000000%, ROM-build analysis PASS. check_decl_agreement,
check_dead_references, check_src_tu and check_tubuild_conflicts --list all
pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G1Gdj5fTjMsW2VjRLpXxYA
andrewboudreau added a commit that referenced this pull request Sep 18, 2026
* Cleanup ov012/daObjC0Water_c: promote BasementWater class TU

The six one-function BasementWater sources and the d_a_obj_c0_water.c
factory become one class TU under the cartridge's own RTTI name. ov012
0x02111450..0x02111768 is now a single `complete` delinks range, and
symbols.txt drops the coined _ZTV13BasementWater alias that shared a word
with _ZTV14daObjC0Water_c.

Rebased onto main, which had landed changes in two of the absorbed
sources. Both are carried forward rather than reverted:

  - _ZN13BasementWater6RenderEv.cpp (#2728) traded the mangled
    _ZN18TextureTransformer6UpdateER15ModelComponents for a real member
    call. The absorbed body already spells it
    `mTextureTransformer.Update(mModel.data)` on named members, and Update
    is non-virtual -- _ZTV18TextureTransformer at 0x0208e7c4 holds only the
    destructor pair -- so this is the direct call the ROM has, not a
    vtable dispatch.

  - _ZN13BasementWater8BehaviorEv.cpp (#2727) replaced the per-file Sound
    externs with Sound.h. The absorbed body had the PlaySecretSound half
    but still declared its own `namespace Sound` block and still called
    PlayLong by mangled name, so both are ported here: the TU includes
    Sound.h and calls Sound::PlayLong. The file comment claiming Sound.h
    has no PlayLong was stale and is corrected.

The class rename had also rewritten historical file citations into paths
that never existed -- `src/_ZN14daObjC0Water_c*.cpp` in this entry's
`legacy_source` and `substitutedObjectPaths`, and in the
notes/data/tu-merge-candidates.json census row. Those name the files that
were absorbed, which were the BasementWater spellings; they are restored,
matching how ov012/daObjC0_Switch_c.json records its own fold. The
manifest note describing a "PlayLong seam kept below" is updated with
it: there is no mangled seam here any more.

config/decl-agreement-baseline.json is edited to this PR's own entries
only -- 11 removed with the deleted sources, 5 added for the class TU --
rather than regenerated, so main's unrelated staleness is not laundered
through here. converted-baseline.json keeps its sorted shape: 3 paths out,
3 #symbol identities in, count unchanged at 2813.

rombuild -j16 --no-rom --no-cache: 8,749 enrolled sources compiled cold,
11,206 source-built functions, 11,206 reproducing, 0 mismatching, 106/106
modules exact at 100.000000%, ROM-build analysis PASS. check_decl_agreement,
check_dead_references, check_src_tu and check_tubuild_conflicts --list all
pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G1Gdj5fTjMsW2VjRLpXxYA

* Credit the four consolidated BasementWater members to their real owners

The promotion wrote all seven members of the folded TU from the factory's
file-level owner instead of each function's own. Four of the seven were
wrong: Behavior belongs to lunavyqo, both destructors to tangosdev, and
classInit to andrewboudreau.

Derived from the attribution gate's own prior-owner computation across the
fold, not from git blame: run base..head, every consolidated function reports
the owner it carried before the move. With these four rows the run reports
0 changed, 0 lost and all 7 consolidations preserved; without them it reports
4 changed. No path-wide override is added -- the correction is per member.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G1Gdj5fTjMsW2VjRLpXxYA

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: andrewboudreau <andrewboudreau@gmail.com>
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.

1 participant