Skip to content

Platform and Enemy get their real shape, and the type work 120 migrations rest on - #1378

Closed
andrewboudreau wants to merge 1 commit into
mainfrom
cpp/platform-enemy-layout
Closed

Platform and Enemy get their real shape, and the type work 120 migrations rest on#1378
andrewboudreau wants to merge 1 commit into
mainfrom
cpp/platform-enemy-layout

Conversation

@andrewboudreau

Copy link
Copy Markdown
Collaborator

The layout half of #1377, split out so it can be reviewed on its own. Every per-class migration is in #1377's sibling PR; what is here is the base classes, the tooling, the two symbol renames, and the source edits those force.

310 files. The other half is 253. Together they reconstruct #1377's tree exactly — same git tree hash — so nothing was dropped in the split.

What this is

  • Platform's data ends at 0x31e, sizeof 0x320. Four classes derive from it directly and each places a 4-byte-aligned class member at 0x320 — PyramidTop/Model, SwitchPillar/TextureTransformer, MovingBarSmall/ShadowModel, WallSign/MovingCylinderClsnWithPos — each read off that class's own destructor. BowserFireSeaArena's own three s16 sit at 0x31e/0x320/0x322 so its Model lands at 0x324. One layout satisfies all five; ending at 0x324 satisfies only BowserFireSeaArena and contradicts the other four. This corrects ov060: the Bowser fight becomes real C++, and D0 destructors become migratable #1374, which was mine.
  • Enemy derives from Actor — what 51 subclasses were waiting on — plus five more Enemy fields named by its subclasses, and its vtable symbol.
  • func_020072c0 and func_02011508 become Vector3::~Vector3 and Vector3s::~Vector3s.
  • ModelBase::SetFile and ShadowModel::InitCylinder return int, not void.
  • objisolate learns the multiple-inheritance secondary vptr addend: >= VTABLE_PREAMBLE rather than ==, so 44 → 36 for ModelAnim corrects instead of being refused. The ROM names the destination independently — _ZTV9ModelAnim 0x0208e980 against VTable_Animation_ModelAnimThunk 0x0208e9a4, differing by exactly 44 − 8.
  • tools/subclass_migrate.py, tools/platform_subclass.py, tools/d0_migrate.py, and the header-generation fixes behind them.

Why six migrations are here and not in the sibling

ChiefChilly, Unagi, Wiggler (D0 and D1), Goomboss and ChainChomp are migrated here because this half does not build without them, and it took the ROM link to say so rather than the compiler. Giving Vector3 a destructor stops ChiefChilly's hand-written .c files compiling; renaming func_02011508 leaves the other four referencing a symbol that no longer exists — which every file still compiles happily against, and only mwldarm rejects:

mwldarm.exe: Undefined : "func_02011508"
mwldarm.exe: Referenced from "Wiggler::~Wiggler[virtual]()" in _ZN7WigglerD0Ev.o

Found by building, not by reading.

Verified

gate result
rombuild.py -j16, full ROM 106/106 exact, 0 mismatching, 10,813 source-built, 1,943,084 bytes, 87.88%
eligible.py vs cbd57a83 nothing lost; only delta is the intended rename pair
tools/ suite 265 passed, 3 skipped
langmode ratchet / duplicates / port refs PASS / none doubled / 393 resolve

One trap worth recording: a symbol rename drops complete in delinks, because enroll.py preserves it by symbol name. The two renamed entries had to be re-promoted with --complete-list; without that the source-built count silently falls 10,813 → 10,810 while every other gate still passes.

Original work is by the author of #1377 across 37 commits. This split preserves the content, not the history — #1377's branch still carries the derivation.

🤖 Generated with Claude Code

https://claude.ai/code/session_015AXm5k53WFPjCYcDHRDX3x

…ions rest on

The layout half of #1377, split out so it can be reviewed on its own. Every
per-class migration is in the sibling PR; what is here is the base classes, the
tooling, the two symbol renames, and the source edits those force.

WHAT THIS IS

  * Platform's data ends at 0x31e, sizeof 0x320. Four classes derive from it
    directly and each places a 4-byte-aligned class member at 0x320 --
    PyramidTop/Model, SwitchPillar/TextureTransformer, MovingBarSmall/ShadowModel,
    WallSign/MovingCylinderClsnWithPos -- each read off that class's own
    destructor. BowserFireSeaArena's own three s16 sit at 0x31e/0x320/0x322 so its
    Model lands at 0x324. One layout satisfies all five; ending at 0x324 satisfies
    only BowserFireSeaArena and contradicts the other four. This corrects #1374,
    which was mine.
  * Enemy derives from Actor, which is what 51 subclasses were waiting on, plus
    five more Enemy fields named by its subclasses and its vtable symbol.
  * func_020072c0 and func_02011508 become Vector3::~Vector3 and
    Vector3s::~Vector3s.
  * ModelBase::SetFile and ShadowModel::InitCylinder return int, not void.
  * objisolate learns the multiple-inheritance secondary vptr addend: `>=
    VTABLE_PREAMBLE` rather than `==`, so 44 -> 36 for ModelAnim corrects instead
    of being refused. The ROM names the destination independently -- _ZTV9ModelAnim
    0x0208e980 against VTable_Animation_ModelAnimThunk 0x0208e9a4, differing by
    exactly 44 - 8.
  * tools/subclass_migrate.py, tools/platform_subclass.py, tools/d0_migrate.py,
    and the header-generation fixes behind them.

WHY THESE SIX MIGRATIONS ARE HERE AND NOT IN THE SIBLING

ChiefChilly, Unagi, Wiggler (D0 and D1), Goomboss and ChainChomp are migrated
here because this half does not build without them, and it took the ROM link to
say so rather than the compiler. Giving Vector3 a destructor stops ChiefChilly's
hand-written .c files compiling; renaming func_02011508 leaves the other four
referencing a symbol that no longer exists, which every file still compiles
happily against and only mwldarm rejects: "Undefined : func_02011508, referenced
from Wiggler::~Wiggler". Found by building, not by reading.

VERIFIED

rombuild -j16, full ROM: 106/106 exact, 0 mismatching, 10,813 source-built,
1,943,084 bytes, 87.88%. eligible.py against cbd57a8: nothing lost, and the only
delta is the intended rename pair -- func_020072c0 and func_02011508 out,
_ZN7Vector3D1Ev and _ZN8Vector3sD1Ev in. tools suite 265 passed, 3 skipped.
langmode ratchet PASS. No duplicate stems.

A symbol rename drops `complete` in delinks (enroll.py preserves it by symbol
name), so the two renamed entries were re-promoted with --complete-list; without
that the source-built count silently falls 10,813 -> 10,810 while every gate
still passes.

Original work is by the author of #1377 across 37 commits; this split preserves
the content, not the history. #1377's branch still carries the derivation.

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

tangos-validator Bot commented Aug 10, 2026

Copy link
Copy Markdown

❌ PR validation — Failed

refusing to auto-validate 304 source/build-data files (cap 200) -- unusually large, please review by hand

Each changed src/*.c|*.cpp is compiled and its relocated bytes compared to the binary data on a private build box. Passing requires every changed file to reproduce the ROM byte-for-byte with correct relocation targets — this catches WRONG-DEST relocations and non-reproducing near-misses that ledger-scoped linkcheck skips.

@tangos-validator

Copy link
Copy Markdown

⏳ PR validation — Validating

Building cd29f701 on 2ad2889983d5. The worker commits a test merge, builds the stock ROM profile, and compares every executable module against retail.

This comment is replaced with the verdict when it finishes.

@tangosdev

Copy link
Copy Markdown
Owner

The tree claim checks out. Verified independently rather than taken on trust: main + #1378 + #1379 produces tree b25bf20ba182bbff1aff1f5d3628080549b17aec, identical to main + #1377. The split dropped nothing.

But it does not clear the gate. The validator's threshold is 200 source/build-data files, and it counts the test merge, not the PR diff:

PR files counted verdict
#1377 553 refused
this PR 304 refused
#1379 253 refused

So both halves still refuse, and the refusal happens before the build, which means neither half has been byte-gated and the mwccarm failed that #1377 was hitting is unmeasured rather than resolved. Splitting in two is not enough; it needs pieces of 200 or fewer. Precedent from earlier today: #1356's 878-file sweep landed as five chunks of 176 (#1360, #1363, #1364, #1365, #1366), each of which got a real byte gate.

Chunking a mechanical sweep was arithmetic. Chunking this is not, because a header change and the sources it forces have to land together, so the boundaries are yours to choose rather than mine.

Carry-over finding that still applies. I verified #1377's combined tree out of band -- which by the tree-hash equality above is exactly this PR plus #1379 -- and 3 files fail on relocation destinations while matching byte-for-byte:

src/_ZN10ModelAnim2D0Ev.cpp    _ZTV10ModelAnim2 (0x0208e9b4) != 0x0208e9d8
src/_ZN10ModelAnim2D1Ev.cpp
src/_ZN9ModelAnimD1Ev.cpp      _ZTV9ModelAnim  (0x0208e980) != 0x0208e9a4

276 of 441 verified, 162 unenrolled, those 3 failing; control of 8 equivalent on-main destructors passes under the same harness, so it is not an artifact. Detail and the two candidate readings are on #1377.

andrewboudreau added a commit that referenced this pull request Aug 10, 2026
The migration half of #1377, split from the layout work it rests on, with the D0
deleting destructors split off again on top so each PR clears the validator's
200-file cap. Stacked on #1378; the D0s are the PR above this one.

131 files. Hand-written .c destructors and methods become real C++ -- classes
deriving from their actual base with typed members, in place of a flattened
struct and a stand-in. Among them: DonutBlock as the worked example for the
shadow-struct classes, thirty Platform subclasses unblocked by the 0x320
correction in #1378, and the array-member classes -- Wiggler's eight, Goomboss's
three, ChainChomp's seven links, PathLift's Model[3], RotatingFirebar's one
collision cylinder per flame.

Six migrations that belong here by subject are in #1378 instead, because that
half does not link without them: ChiefChilly, Unagi, Wiggler D0 and D1, Goomboss
and ChainChomp all call the renamed Vector3 destructors.

VERIFIED

rombuild -j16, full ROM: 106/106 exact, 0 mismatching, 10,813 source-built,
87.88%. eligible.py differs from main only by the rename pair #1378 introduces.
tools suite 265 passed. langmode ratchet PASS. No duplicate stems.
pr_linkcheck: no new WRONG or NO-REPRO.

Enrollment does not move, and that is expected: a migration replaces the source
of a function that was already enrolled, so the count stays while the source
stops lying.

Original work is by the author of #1377; this split preserves the content, not
the 37-commit history, which remains on that branch.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015AXm5k53WFPjCYcDHRDX3x
andrewboudreau added a commit that referenced this pull request Aug 10, 2026
The migration half of #1377, split from the layout work it rests on, with the D0
deleting destructors split off again on top so each PR clears the validator's
200-file cap. Stacked on #1378; the D0s are the PR above this one.

131 files. Hand-written .c destructors and methods become real C++ -- classes
deriving from their actual base with typed members, in place of a flattened
struct and a stand-in. Among them: DonutBlock as the worked example for the
shadow-struct classes, thirty Platform subclasses unblocked by the 0x320
correction in #1378, and the array-member classes -- Wiggler's eight, Goomboss's
three, ChainChomp's seven links, PathLift's Model[3], RotatingFirebar's one
collision cylinder per flame.

Six migrations that belong here by subject are in #1378 instead, because that
half does not link without them: ChiefChilly, Unagi, Wiggler D0 and D1, Goomboss
and ChainChomp all call the renamed Vector3 destructors.

VERIFIED

rombuild -j16, full ROM: 106/106 exact, 0 mismatching, 10,813 source-built,
87.88%. eligible.py differs from main only by the rename pair #1378 introduces.
tools suite 265 passed. langmode ratchet PASS. No duplicate stems.
pr_linkcheck: no new WRONG or NO-REPRO.

Enrollment does not move, and that is expected: a migration replaces the source
of a function that was already enrolled, so the count stays while the source
stops lying.

Original work is by the author of #1377; this split preserves the content, not
the 37-commit history, which remains on that branch.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015AXm5k53WFPjCYcDHRDX3x
andrewboudreau added a commit that referenced this pull request Aug 10, 2026
The D0 half of #1379, split off so each PR clears the validator's 200-file cap.
Stacked on #1379, which carries the class definitions these need.

D0 is vtable slot 17 (or 1, for classes rooted below Actor): destroy, then return
the object to the actor heap. Each of these was a hand-written .c file spelling
out what the compiler emits anyway -- store the vtable, call the member
destructors in reverse declaration order, chain to the base, call
Memory::Deallocate. Declaring the class properly lets the compiler emit all of
it, and the deallocation comes from the inline operator delete rather than
anything in the file.

Not in arm9: those D0s need an operator delete the arm9 classes do not have in
scope, which is a separate problem and is not attempted here.

VERIFIED

rombuild -j16, full ROM: 106/106 exact, 0 mismatching, 10,813 source-built,
87.88%. eligible.py differs from main only by the rename pair #1378 introduces.
tools suite 265 passed. langmode ratchet PASS. No duplicate stems.

Enrollment does not move: a D0 was already enrolled from its .c file, so the
count stays while the source stops hand-spelling the mangled name.

Original work is by the author of #1377; this split preserves the content, not
the 37-commit history, which remains on that branch.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015AXm5k53WFPjCYcDHRDX3x
@andrewboudreau andrewboudreau changed the title Platform and Enemy get their real shape, and the type work 120 migrations rest on Platform is 0x320, Enemy derives from Actor, and the structors that follow Aug 10, 2026
@andrewboudreau andrewboudreau changed the title Platform is 0x320, Enemy derives from Actor, and the structors that follow Platform and Enemy get their real shape, and the type work 120 migrations rest on Aug 10, 2026
@andrewboudreau

Copy link
Copy Markdown
Collaborator Author

Superseded: split again into #1381 (Platform/Enemy layout, 172 files) and the header-generation/Vector3 half, both now under the validator's 200-file cap so they auto-validate instead of needing a hand review. Same content — #1381 + its successor reproduce this branch's tree exactly.

andrewboudreau added a commit that referenced this pull request Aug 10, 2026
The migration half of #1377, split from the layout work it rests on, with the D0
deleting destructors split off again on top so each PR clears the validator's
200-file cap. Stacked on #1378; the D0s are the PR above this one.

131 files. Hand-written .c destructors and methods become real C++ -- classes
deriving from their actual base with typed members, in place of a flattened
struct and a stand-in. Among them: DonutBlock as the worked example for the
shadow-struct classes, thirty Platform subclasses unblocked by the 0x320
correction in #1378, and the array-member classes -- Wiggler's eight, Goomboss's
three, ChainChomp's seven links, PathLift's Model[3], RotatingFirebar's one
collision cylinder per flame.

Six migrations that belong here by subject are in #1378 instead, because that
half does not link without them: ChiefChilly, Unagi, Wiggler D0 and D1, Goomboss
and ChainChomp all call the renamed Vector3 destructors.

VERIFIED

rombuild -j16, full ROM: 106/106 exact, 0 mismatching, 10,813 source-built,
87.88%. eligible.py differs from main only by the rename pair #1378 introduces.
tools suite 265 passed. langmode ratchet PASS. No duplicate stems.
pr_linkcheck: no new WRONG or NO-REPRO.

Enrollment does not move, and that is expected: a migration replaces the source
of a function that was already enrolled, so the count stays while the source
stops lying.

Original work is by the author of #1377; this split preserves the content, not
the 37-commit history, which remains on that branch.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015AXm5k53WFPjCYcDHRDX3x
andrewboudreau added a commit that referenced this pull request Aug 10, 2026
The D0 half of #1379, split off so each PR clears the validator's 200-file cap.
Stacked on #1379, which carries the class definitions these need.

D0 is vtable slot 17 (or 1, for classes rooted below Actor): destroy, then return
the object to the actor heap. Each of these was a hand-written .c file spelling
out what the compiler emits anyway -- store the vtable, call the member
destructors in reverse declaration order, chain to the base, call
Memory::Deallocate. Declaring the class properly lets the compiler emit all of
it, and the deallocation comes from the inline operator delete rather than
anything in the file.

Not in arm9: those D0s need an operator delete the arm9 classes do not have in
scope, which is a separate problem and is not attempted here.

VERIFIED

rombuild -j16, full ROM: 106/106 exact, 0 mismatching, 10,813 source-built,
87.88%. eligible.py differs from main only by the rename pair #1378 introduces.
tools suite 265 passed. langmode ratchet PASS. No duplicate stems.

Enrollment does not move: a D0 was already enrolled from its .c file, so the
count stays while the source stops hand-spelling the mangled name.

Original work is by the author of #1377; this split preserves the content, not
the 37-commit history, which remains on that branch.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015AXm5k53WFPjCYcDHRDX3x
andrewboudreau added a commit that referenced this pull request Aug 10, 2026
The migration half of #1377, split from the layout work it rests on, with the D0
deleting destructors split off again on top so each PR clears the validator's
200-file cap. Stacked on #1378; the D0s are the PR above this one.

131 files. Hand-written .c destructors and methods become real C++ -- classes
deriving from their actual base with typed members, in place of a flattened
struct and a stand-in. Among them: DonutBlock as the worked example for the
shadow-struct classes, thirty Platform subclasses unblocked by the 0x320
correction in #1378, and the array-member classes -- Wiggler's eight, Goomboss's
three, ChainChomp's seven links, PathLift's Model[3], RotatingFirebar's one
collision cylinder per flame.

Six migrations that belong here by subject are in #1378 instead, because that
half does not link without them: ChiefChilly, Unagi, Wiggler D0 and D1, Goomboss
and ChainChomp all call the renamed Vector3 destructors.

VERIFIED

rombuild -j16, full ROM: 106/106 exact, 0 mismatching, 10,813 source-built,
87.88%. eligible.py differs from main only by the rename pair #1378 introduces.
tools suite 265 passed. langmode ratchet PASS. No duplicate stems.
pr_linkcheck: no new WRONG or NO-REPRO.

Enrollment does not move, and that is expected: a migration replaces the source
of a function that was already enrolled, so the count stays while the source
stops lying.

Original work is by the author of #1377; this split preserves the content, not
the 37-commit history, which remains on that branch.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015AXm5k53WFPjCYcDHRDX3x
andrewboudreau added a commit that referenced this pull request Aug 10, 2026
The D0 half of #1379, split off so each PR clears the validator's 200-file cap.
Stacked on #1379, which carries the class definitions these need.

D0 is vtable slot 17 (or 1, for classes rooted below Actor): destroy, then return
the object to the actor heap. Each of these was a hand-written .c file spelling
out what the compiler emits anyway -- store the vtable, call the member
destructors in reverse declaration order, chain to the base, call
Memory::Deallocate. Declaring the class properly lets the compiler emit all of
it, and the deallocation comes from the inline operator delete rather than
anything in the file.

Not in arm9: those D0s need an operator delete the arm9 classes do not have in
scope, which is a separate problem and is not attempted here.

VERIFIED

rombuild -j16, full ROM: 106/106 exact, 0 mismatching, 10,813 source-built,
87.88%. eligible.py differs from main only by the rename pair #1378 introduces.
tools suite 265 passed. langmode ratchet PASS. No duplicate stems.

Enrollment does not move: a D0 was already enrolled from its .c file, so the
count stays while the source stops hand-spelling the mangled name.

Original work is by the author of #1377; this split preserves the content, not
the 37-commit history, which remains on that branch.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015AXm5k53WFPjCYcDHRDX3x
andrewboudreau added a commit that referenced this pull request Aug 10, 2026
The migration half of #1377, split from the layout work it rests on, with the D0
deleting destructors split off again on top so each PR clears the validator's
200-file cap. Stacked on #1378; the D0s are the PR above this one.

131 files. Hand-written .c destructors and methods become real C++ -- classes
deriving from their actual base with typed members, in place of a flattened
struct and a stand-in. Among them: DonutBlock as the worked example for the
shadow-struct classes, thirty Platform subclasses unblocked by the 0x320
correction in #1378, and the array-member classes -- Wiggler's eight, Goomboss's
three, ChainChomp's seven links, PathLift's Model[3], RotatingFirebar's one
collision cylinder per flame.

Six migrations that belong here by subject are in #1378 instead, because that
half does not link without them: ChiefChilly, Unagi, Wiggler D0 and D1, Goomboss
and ChainChomp all call the renamed Vector3 destructors.

VERIFIED

rombuild -j16, full ROM: 106/106 exact, 0 mismatching, 10,813 source-built,
87.88%. eligible.py differs from main only by the rename pair #1378 introduces.
tools suite 265 passed. langmode ratchet PASS. No duplicate stems.
pr_linkcheck: no new WRONG or NO-REPRO.

Enrollment does not move, and that is expected: a migration replaces the source
of a function that was already enrolled, so the count stays while the source
stops lying.

Original work is by the author of #1377; this split preserves the content, not
the 37-commit history, which remains on that branch.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015AXm5k53WFPjCYcDHRDX3x
andrewboudreau added a commit that referenced this pull request Aug 10, 2026
The D0 half of #1379, split off so each PR clears the validator's 200-file cap.
Stacked on #1379, which carries the class definitions these need.

D0 is vtable slot 17 (or 1, for classes rooted below Actor): destroy, then return
the object to the actor heap. Each of these was a hand-written .c file spelling
out what the compiler emits anyway -- store the vtable, call the member
destructors in reverse declaration order, chain to the base, call
Memory::Deallocate. Declaring the class properly lets the compiler emit all of
it, and the deallocation comes from the inline operator delete rather than
anything in the file.

Not in arm9: those D0s need an operator delete the arm9 classes do not have in
scope, which is a separate problem and is not attempted here.

VERIFIED

rombuild -j16, full ROM: 106/106 exact, 0 mismatching, 10,813 source-built,
87.88%. eligible.py differs from main only by the rename pair #1378 introduces.
tools suite 265 passed. langmode ratchet PASS. No duplicate stems.

Enrollment does not move: a D0 was already enrolled from its .c file, so the
count stays while the source stops hand-spelling the mangled name.

Original work is by the author of #1377; this split preserves the content, not
the 37-commit history, which remains on that branch.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015AXm5k53WFPjCYcDHRDX3x
andrewboudreau added a commit that referenced this pull request Aug 10, 2026
* 59 classes get their real base and real member types

The migration half of #1377, split from the layout work it rests on, with the D0
deleting destructors split off again on top so each PR clears the validator's
200-file cap. Stacked on #1378; the D0s are the PR above this one.

131 files. Hand-written .c destructors and methods become real C++ -- classes
deriving from their actual base with typed members, in place of a flattened
struct and a stand-in. Among them: DonutBlock as the worked example for the
shadow-struct classes, thirty Platform subclasses unblocked by the 0x320
correction in #1378, and the array-member classes -- Wiggler's eight, Goomboss's
three, ChainChomp's seven links, PathLift's Model[3], RotatingFirebar's one
collision cylinder per flame.

Six migrations that belong here by subject are in #1378 instead, because that
half does not link without them: ChiefChilly, Unagi, Wiggler D0 and D1, Goomboss
and ChainChomp all call the renamed Vector3 destructors.

VERIFIED

rombuild -j16, full ROM: 106/106 exact, 0 mismatching, 10,813 source-built,
87.88%. eligible.py differs from main only by the rename pair #1378 introduces.
tools suite 265 passed. langmode ratchet PASS. No duplicate stems.
pr_linkcheck: no new WRONG or NO-REPRO.

Enrollment does not move, and that is expected: a migration replaces the source
of a function that was already enrolled, so the count stays while the source
stops lying.

Original work is by the author of #1377; this split preserves the content, not
the 37-commit history, which remains on that branch.

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

* Re-request validation against the retargeted base

No content change. This PR's base moved from cpp/platform-enemy-layout to
cpp/layout-headergen when the layout half was split in two, and the relay keys on
head SHA -- retargeting alone does not re-submit, so the check still showed the
431-file count from the old base. This empty commit changes the head so the
validator measures the 131 files this PR actually adds.

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

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
andrewboudreau added a commit that referenced this pull request Aug 10, 2026
* 59 classes get their real base and real member types

The migration half of #1377, split from the layout work it rests on, with the D0
deleting destructors split off again on top so each PR clears the validator's
200-file cap. Stacked on #1378; the D0s are the PR above this one.

131 files. Hand-written .c destructors and methods become real C++ -- classes
deriving from their actual base with typed members, in place of a flattened
struct and a stand-in. Among them: DonutBlock as the worked example for the
shadow-struct classes, thirty Platform subclasses unblocked by the 0x320
correction in #1378, and the array-member classes -- Wiggler's eight, Goomboss's
three, ChainChomp's seven links, PathLift's Model[3], RotatingFirebar's one
collision cylinder per flame.

Six migrations that belong here by subject are in #1378 instead, because that
half does not link without them: ChiefChilly, Unagi, Wiggler D0 and D1, Goomboss
and ChainChomp all call the renamed Vector3 destructors.

VERIFIED

rombuild -j16, full ROM: 106/106 exact, 0 mismatching, 10,813 source-built,
87.88%. eligible.py differs from main only by the rename pair #1378 introduces.
tools suite 265 passed. langmode ratchet PASS. No duplicate stems.
pr_linkcheck: no new WRONG or NO-REPRO.

Enrollment does not move, and that is expected: a migration replaces the source
of a function that was already enrolled, so the count stays while the source
stops lying.

Original work is by the author of #1377; this split preserves the content, not
the 37-commit history, which remains on that branch.

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

* Re-request validation against the retargeted base

No content change. This PR's base moved from cpp/platform-enemy-layout to
cpp/layout-headergen when the layout half was split in two, and the relay keys on
head SHA -- retargeting alone does not re-submit, so the check still showed the
431-file count from the old base. This empty commit changes the head so the
validator measures the 131 files this PR actually adds.

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

* 61 deleting destructors become real C++

The D0 half of #1379, split off so each PR clears the validator's 200-file cap.
Stacked on #1379, which carries the class definitions these need.

D0 is vtable slot 17 (or 1, for classes rooted below Actor): destroy, then return
the object to the actor heap. Each of these was a hand-written .c file spelling
out what the compiler emits anyway -- store the vtable, call the member
destructors in reverse declaration order, chain to the base, call
Memory::Deallocate. Declaring the class properly lets the compiler emit all of
it, and the deallocation comes from the inline operator delete rather than
anything in the file.

Not in arm9: those D0s need an operator delete the arm9 classes do not have in
scope, which is a separate problem and is not attempted here.

VERIFIED

rombuild -j16, full ROM: 106/106 exact, 0 mismatching, 10,813 source-built,
87.88%. eligible.py differs from main only by the rename pair #1378 introduces.
tools suite 265 passed. langmode ratchet PASS. No duplicate stems.

Enrollment does not move: a D0 was already enrolled from its .c file, so the
count stays while the source stops hand-spelling the mangled name.

Original work is by the author of #1377; this split preserves the content, not
the 37-commit history, which remains on that branch.

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

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
andrewboudreau added a commit that referenced this pull request Aug 10, 2026
* 59 classes get their real base and real member types

The migration half of #1377, split from the layout work it rests on, with the D0
deleting destructors split off again on top so each PR clears the validator's
200-file cap. Stacked on #1378; the D0s are the PR above this one.

131 files. Hand-written .c destructors and methods become real C++ -- classes
deriving from their actual base with typed members, in place of a flattened
struct and a stand-in. Among them: DonutBlock as the worked example for the
shadow-struct classes, thirty Platform subclasses unblocked by the 0x320
correction in #1378, and the array-member classes -- Wiggler's eight, Goomboss's
three, ChainChomp's seven links, PathLift's Model[3], RotatingFirebar's one
collision cylinder per flame.

Six migrations that belong here by subject are in #1378 instead, because that
half does not link without them: ChiefChilly, Unagi, Wiggler D0 and D1, Goomboss
and ChainChomp all call the renamed Vector3 destructors.

VERIFIED

rombuild -j16, full ROM: 106/106 exact, 0 mismatching, 10,813 source-built,
87.88%. eligible.py differs from main only by the rename pair #1378 introduces.
tools suite 265 passed. langmode ratchet PASS. No duplicate stems.
pr_linkcheck: no new WRONG or NO-REPRO.

Enrollment does not move, and that is expected: a migration replaces the source
of a function that was already enrolled, so the count stays while the source
stops lying.

Original work is by the author of #1377; this split preserves the content, not
the 37-commit history, which remains on that branch.


Claude-Session: https://claude.ai/code/session_015AXm5k53WFPjCYcDHRDX3x

* Re-request validation against the retargeted base

No content change. This PR's base moved from cpp/platform-enemy-layout to
cpp/layout-headergen when the layout half was split in two, and the relay keys on
head SHA -- retargeting alone does not re-submit, so the check still showed the
431-file count from the old base. This empty commit changes the head so the
validator measures the 131 files this PR actually adds.


Claude-Session: https://claude.ai/code/session_015AXm5k53WFPjCYcDHRDX3x

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
andrewboudreau added a commit that referenced this pull request Aug 10, 2026
* 59 classes get their real base and real member types

The migration half of #1377, split from the layout work it rests on, with the D0
deleting destructors split off again on top so each PR clears the validator's
200-file cap. Stacked on #1378; the D0s are the PR above this one.

131 files. Hand-written .c destructors and methods become real C++ -- classes
deriving from their actual base with typed members, in place of a flattened
struct and a stand-in. Among them: DonutBlock as the worked example for the
shadow-struct classes, thirty Platform subclasses unblocked by the 0x320
correction in #1378, and the array-member classes -- Wiggler's eight, Goomboss's
three, ChainChomp's seven links, PathLift's Model[3], RotatingFirebar's one
collision cylinder per flame.

Six migrations that belong here by subject are in #1378 instead, because that
half does not link without them: ChiefChilly, Unagi, Wiggler D0 and D1, Goomboss
and ChainChomp all call the renamed Vector3 destructors.

VERIFIED

rombuild -j16, full ROM: 106/106 exact, 0 mismatching, 10,813 source-built,
87.88%. eligible.py differs from main only by the rename pair #1378 introduces.
tools suite 265 passed. langmode ratchet PASS. No duplicate stems.
pr_linkcheck: no new WRONG or NO-REPRO.

Enrollment does not move, and that is expected: a migration replaces the source
of a function that was already enrolled, so the count stays while the source
stops lying.

Original work is by the author of #1377; this split preserves the content, not
the 37-commit history, which remains on that branch.


Claude-Session: https://claude.ai/code/session_015AXm5k53WFPjCYcDHRDX3x

* Re-request validation against the retargeted base

No content change. This PR's base moved from cpp/platform-enemy-layout to
cpp/layout-headergen when the layout half was split in two, and the relay keys on
head SHA -- retargeting alone does not re-submit, so the check still showed the
431-file count from the old base. This empty commit changes the head so the
validator measures the 131 files this PR actually adds.


Claude-Session: https://claude.ai/code/session_015AXm5k53WFPjCYcDHRDX3x

* 61 deleting destructors become real C++

The D0 half of #1379, split off so each PR clears the validator's 200-file cap.
Stacked on #1379, which carries the class definitions these need.

D0 is vtable slot 17 (or 1, for classes rooted below Actor): destroy, then return
the object to the actor heap. Each of these was a hand-written .c file spelling
out what the compiler emits anyway -- store the vtable, call the member
destructors in reverse declaration order, chain to the base, call
Memory::Deallocate. Declaring the class properly lets the compiler emit all of
it, and the deallocation comes from the inline operator delete rather than
anything in the file.

Not in arm9: those D0s need an operator delete the arm9 classes do not have in
scope, which is a separate problem and is not attempted here.

VERIFIED

rombuild -j16, full ROM: 106/106 exact, 0 mismatching, 10,813 source-built,
87.88%. eligible.py differs from main only by the rename pair #1378 introduces.
tools suite 265 passed. langmode ratchet PASS. No duplicate stems.

Enrollment does not move: a D0 was already enrolled from its .c file, so the
count stays while the source stops hand-spelling the mangled name.

Original work is by the author of #1377; this split preserves the content, not
the 37-commit history, which remains on that branch.


Claude-Session: https://claude.ai/code/session_015AXm5k53WFPjCYcDHRDX3x

---------

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

2 participants