decomp-cpp-class-formlisted
Install: claude install-skill tangosdev/sm64ds-decomp
# Making mwccarm emit the ROM's shape
Byte-matching a real C++ class is a question about **source form**, not about being
"more correct". The compiler is an oracle — ask it rather than reasoning about the ABI.
## Ask the compiler, don't hand-mangle
```sh
python tools/mangle.py candidate.cpp
python tools/mangle.py candidate.cpp --expect _ZN5Actor8BehaviorEv
python tools/mangle.py candidate.cpp --mangled-only --json
```
It compiles with the pinned 2004/b56 C++ flags and reads defined GLOBAL/WEAK ELF
symbols, so substitutions, thunks, ctor/dtor variants and static data are the
compiler's answer, not a guess. It uses `match.DEFAULT_FLAGS`, which differs from
`rombuild.CFLAGS` (`-w illpragmas` vs `-Cpp_exceptions off`) — **verified not to change
the emitted symbol set**, so it is sound for the checks below.
**Ordering cannot be read from `st_value`.** mwccarm emits one `.text` section per
function, so every symbol's `st_value` is 0. Order lives in the **section index**.
## Destructor variants: the form decides the order
The ROM typically has **D1 then D0 and no D2** (e.g. ov045: D1 `0x021111a0` size 0x44,
D0 `0x021111e4` size 0x58, landing exactly on the next function). Measured:
| source form | instantiated in TU? | emits | order |
|---|---|---|---|
| `X::~X() {}` out of line | no | D2, D0, D1 + `_ZTV` | **D0 before D1** — wrong |
| `X::~X() {}` out of line | yes (`new X()`) | D2, D0, D1 + `_ZTV` | **D0 before D1** — wrong |
| `virtual ~X() {}` in class body | **no** | **