Active compiler experiment (scope corrected 2026-09-06): Only game-category MWCC C/C++ units use the common GC/1.3
config.compiler_version. The older math block, includingrand, belongs to theMSL_C/PPCEABI/bare/H/source tree and third-party grouping by user instruction (2026-09-20). Its eleven math TUs retain their exact GC/1.2.5n profiles;rand.cretains GC/1.3. These are independent library settings, not game compiler exceptions. MSL ownership is provisional; descriptive filenames do not claim recovered vendor source names. Seedocs/msl_math_rehome.mdfor the path mapping. Keepmain/zlb.con its ProDG toolchain. Dolphin SDK, MSL, MusyX, and compiler runtime retain their pre-migration compiler profiles: GC/1.2.5n library defaults where applicable and their existing per-unit exceptions. Their inclusion in the global migration was a scope mistake; do not move them to GC/1.3 as part of game-code work. Optimization profiles remain; the linker independently stays GC/1.3.2. Bothninja all_sourceand the strict retail checksum target must pass before pushing. Mark regressed game unitsNonMatchingso the matching link uses retail objects whileall_sourcestill compiles their C/C++. Do not restore game compiler exceptions, change the expected checksum, or disable the check to make the build green.
Integration workflow (effective 2026-07-29): High-frequency decomp commits land on the permanent
stagingbranch, not directly onmain. Fetch before starting, check outorigin/staging, and rebase local unpushed work onto the fresh remote staging tip before every push. Never resolve a rebase by clobbering with--theirs; abort and re-derive on conflict.A maintainer or bot performs one normal merge commit from
stagingtomainper UTC day. This preserves the useful per-change history while causing the main Actions build to run once for the batch. After that merge is published, fast-forwardstagingto the newmainmerge commit before reopening it for work; do not rotate or date the staging branch. If commits land directly onmain, reconcile them intostagingwithout dropping either side before the next staging push. Larger coherent PRs remain welcome, butstagingis the default low-overhead landing path.
Automation note (resolved 2026-06-18): JackPriceBurns paused the loop because it was re-pushing a stale patch series (cherry-pick with
--theirsconflict resolution reverted other contributors' fixes) and only gated on the 30s strict-hash target, so it never sawall_sourcebreaks (dupposPtr,fn_802ABFBCmismatch). Both are fixed andmainis green again. Resumed by zcanann with corrected discipline: every push rebases onto the fresh active remote integration branch, never--theirs-clobbers (abort + re-derive on conflict), and gates onninja all_sourceexiting 0 — not just the match target.
Keep this light. The project is still in the "recover the game" phase, not the "polish an already-understood decomp" phase.
- Active target version in this repo is
EN v1.0(GSAE01). orig/may contain EN, PAL, and JP artifacts. Use them for cross-checking only.resources/ghidra-decomp-4-12-2026/contains the raw EN v1.0 Ghidra decomp with no code analysis. Use it for basic code structure only.resources/DolphinSymbolExport_GSAE01.txtcontains the active EN symbol export based on Dolphin Signature analysis. This is good for SDK matching, but the addresses need to be translated against the currentconfig/GSAE01/symbols.txtanchors before using them for splits.- Other
resources/*content should also be treated as rough shape data, not analysis. reference_projects/*may exist in some checkouts to mine patterns, especially SDK code and common Nintendo/GameCube layouts. Do not edit anything in this folder, as it is reference code from other game decompilation efforts. Known donors:marioparty4(2002, closest-era Nintendo title),tww(2002–03 Wind Waker),melee(2001, oldest SDK baseline),mkdd(2003 Mario Kart DD),pikmin2(2004, richest Dolphin SDK coverage —src/Dolphin/mirrors our layout),prime(2002 Metroid Prime, same year as SFA).- SDK files have already been added to this project, but they are from another game. We will need to assign our splits to use these files, update symbols, and possibly learn from
reference_projects/*to drive our SDK files to matching. reference_projects/rena-tools*may exist in some checkouts with Rena's SFA decomp projects, and may have some information that helps.
- Recover real functions, data, class boundaries, globals, vtables, and file structure.
- Improve buildability, linkage, and objdiff results.
- Make the source look more like plausible original code, not a pile of coercion hacks.
- Generate new understanding when the repo has none yet.
This repo starts from very little. Expect to do naming, struct recovery, type cleanup, file splitting, and tool building as part of normal work.
objdiffis still the final measure of whether a change helped.- Treat provisional per-TU compiler overrides and false-set experiments as hypotheses, not provenance. Source accumulated under an override will usually regress when it is removed, so that aggregate regression does not validate the compiler choice. Require independent evidence such as retail call/inlining topology, symbol shape, neighboring build settings, or contemporary source lineage before retaining an exceptional compiler version.
- Ghidra output is raw input. It will often be wrong about types, signatures, control flow details, and boundaries.
- There are no symbol maps to lean on here. Do not write the workflow as if names or section layout are already known.
- Reference projects are evidence, not truth. Match version, compiler behavior, ABI, and surrounding code before borrowing anything.
- Prioritize source changes shared across retail versions. Check promising changes against available secondary targets and carry verified exact objects into their progress manifests. Verify each input DOL against its configured hash; introduce regional source differences only when the binaries establish different behavior.
- Start from one promising function, object, data block, or subsystem.
- Work outward aggressively if the blocker is adjacent code, missing types, unknown globals, constructor patterns, SDK reuse, or bad file boundaries.
- Do not get trapped in local optima. If a path stops yielding structure, switch level: inspect related code, assets, rodata, strings, object layouts, SDK analogs, or write tooling.
- Prefer recovering coherent source over narrowly chasing one assembly diff while the surrounding code remains obviously wrong.
- Please try to keep the build in a functional state (ninja should compile successfully on work complete and a fresh run).
- Infer function names where none exist yet.
- Recover data definitions and rodata ownership.
- Establish class and subsystem boundaries.
- Replace guessed offsets and anonymous blobs with real fields when justified.
- Identify reusable SDK or middleware code from
reference_projects/*. - Mine strings, assets, tables, and binary patterns when that reveals structure.
- Use
python tools/orig/source_leaks.pywhen checking whetherorig/*still preserves direct source/header names before leaning on external debug-side references. - Use
python tools/orig/source_matrix.pywhen PAL / JP / EN rev1 may strengthen or rename a weak source-tag clue before you commit to a file name or materialize a stub. - Use
python tools/version_progress.py <version> --writeafter EN split changes when refreshing the conservative EN rev1 / JP / PAL progress configs; the tool claims only uniquely anchored text units and leaves uncertain code and data in full-DOL autogenerated report units. - Use
python tools/orig/source_recovery.pywhenmain.dolsource-tagged strings may give EN file/function anchors for naming or splits. - Use
python tools/orig/source_boundaries.pywhen you need those retail EN source tags turned into concrete current EN work windows and split coverage status before planning a first-pass source skeleton, including cross-bundle nearby-string context and low-confidence indirect neighborhoods for no-direct-xref tags liken_attractmode.c. - Use
python tools/orig/source_skeleton.pywhen retail source-tagged xrefs need to be grouped into current EN address islands so you can plan first-pass source skeletons or split windows instead of treating each clue in isolation. - Use
python tools/orig/source_corridors.pywhen a retail-backed EN span needs debug-side file-size or short source-order context so you can tell whether the current seed is too small, too wide, or sitting next to one obvious missing source file. - Use
python tools/orig/source_windows.pywhen a retail-backed EN span also has an exact debug-side split and you want ranked current EN whole-file window candidates instead of only a seed-too-small / seed-too-wide verdict. - Use
python tools/orig/source_gap_packets.pywhen two retail-backed anchors leave one or a few plausible missing files between them and you want resolved debug-side path hints plus the current EN gap functions for immediate split planning, or--materialize-allto emit ready corridor briefs underdocs/orig/source_gap_packet_briefs/. - Use
python tools/orig/source_gap_windows.pywhen a short retail-backed gap packet already names the missing files and you want estimated current EN per-file windows instead of one large anonymous gap span, or--broad-exact-intervalswhen the same anchors bound a larger exact debug interval you want projected into EN windows, or--materialize-allto emit ready briefs underdocs/orig/source_gap_window_briefs/. - Use
python tools/orig/source_layout.pywhen you want one address-ordered per-file skeleton that interleaves retail-backed anchor windows with the short-gap file estimates so you can start claiming source boundaries directly, or--broad-exact-layoutwhen you want that same layout view to include larger exact-debug corridor windows, or--materialize-allto emit ready layout briefs underdocs/orig/source_layout_briefs/. - Use
python tools/orig/source_functions.pywhen you want a tighter report of retail-labeled function candidates such assetBlendMove/Initwith their current EN xref clusters. - Use
python tools/orig/source_worklist.pywhen you want one prioritized queue of retail-backed boundary jobs, or--materialize-allto emit ready markdown packets underdocs/orig/source_worklist_packets/for handoff. Missing optional debug references are reported on stderr; retail EN source tags and xrefs remain available, but debug-backed size and source-order estimates may be absent. - Use
python tools/orig/source_blueprints.pywhen you want one address-ordered neighborhood view that merges anchor windows and short gap packets, or--materialize-allto emit ready neighborhood briefs underdocs/orig/source_blueprint_briefs/. - Use
python tools/orig/source_reference_hints.pywhen retail EN evidence names a file but you want clearly-labeled reference-project path, function, DLL, or object hints without promoting them to source-truth. - Use
python tools/sdk_import_probe.pywhen testing candidate SDK source files against current EN windows by compiled size, function-count shape, or assigned-split audits before changingsplits.txt. - Use
python tools/sdk_dol_match.pywhen reference projects also have knownsplits.txtplusorig/*/sys/main.dol; it compares normalized PPC instruction signatures across reference SDK windows and current EN windows, andsource-likelyresults are strong candidates for retargeting or source import. - Use
python3 tools/refcorpus/search_corpus.py --seq '<mnemonics>'or--asm '<regex>'when an unfamiliar MWCC instruction shape needs real compiled-C examples. Discovery returns compact size-ranked function IDs; inspect only a selected result with--show ID. - Use
python3 tools/symbol_context.py relevant --source <source> --function <symbol> --context <ctx>when you need a compact list of types used by one function, thenpython3 tools/symbol_context.py get <type> --context <ctx>to retrieve the complete definition without broad source searches. Harness workspaces provide those target arguments automatically. - Use
python tools/orig/source_object_packets.pywhen a retail source tag needs to be tied back to current EN object/class/DLL packets or materialized into a non-built source packet undersrc/main/unknown/source_packets/. - Use
python tools/orig/source_materialize.pywhen you want to materialize retail-backed stubs undersrc/and/or export exact disc source/header artifacts to a local non-source folder. - Use
python tools/orig/object_family_packets.pywhen you want retail-backed object/DLL family packets or non-builtsrc/main/unknown/boundary stubs for exploratory split planning before a real source filename is proven. - Use
python tools/orig/object_def_packets.pywhen you want retail-backed per-object exploratory stubs undersrc/main/unknown/objects/, cross-linked to class packets, DLL families, placement widths, and EN descriptor slots for rapid split planning. - Use
python tools/orig/object_bin2_audit.pywhenOBJECTS.bin2may clarify a shaky object boundary, inline substructure, or exploratory packet by comparing the sibling retail object-table lineage against liveOBJECTS.bin. - Use
python tools/orig/tab_catalog.pywhen you need real retail chunk boundaries from.tab/.binfamilies before proposing asset splits. - Use
python tools/xref/asset_clusters.pywhen you want regenerablesrc/xref/JSON packets plusdocs/xref/handoff briefs that link retail EN DOL source/file-string xrefs to current EN functions,orig/filesasset families, and Rena file-slot metadata so you can cluster loader code, singleton-ish systems, or source islands before naming or splitting them. - Use
python tools/orig/dol_vtables.py --stores-onlywhen hunting constructor-backed class boundaries, vtables, or callback tables in the retail DOL. - Use
python tools/orig/constructor_packets.pywhen a store-backed DOL vtable/callback-table hit should become a non-builtsrc/main/unknown/constructors/packet for class-boundary or hierarchy recovery. - Use
python tools/orig/romlist_params.pywhen recovering object placement structs, param widths, or variable-length romlist families from retail data. - Use
python tools/orig/resource_registry_audit.py <version>when regional resource names or descriptor splits look wrong. It checks retail registry pointers and callback correspondence without changing ownership; review complete neighboring units before applying its findings. - Use
python tools/orig/sda_symbol_audit.py <version> --section sbss(orsdata/sdata2) when regional small-data names or boundaries look wrong. It reads retail r13/r2 bases and paired instruction operands, excluding functions that overwrite the base register; normalized objdiff relocations and equal section widths cannot establish global addresses. - Use
python tools/orig/call_symbol_audit.py <version>when repeated small functions lack regional names. It reports read-only candidates from two independent unique callers, consistent call destinations and equal normalized callee bodies; inspect actual operands and the containing TU before applying a name. - Use
python tools/verify_source_link.py <version> <source-unit>...when regional completion depends on final relocation addresses or discarded source data. It links the built retail objects first, then substitutes only the selected source objects; both DOLs must exactly match the verified original. This supplements objdiff and does not claim a complete regional source link. - Write small custom tools and scripts under the tools/ folder when the repo lacks the visibility needed to move quickly.
- Bias toward EN
GSAE01for addresses, sizes, and matching decisions. - Treat TU boundaries confirmed from the retail DOL as structural ground truth. Do not split a confirmed TU into multiple source files for per-function cflags, pragma substitutes, match percentage, or convenience.
- Treat each confirmed numbered DLL folder as a real slot, not a disposable naming hint. Never collapse an adjacent slot merely because its functions, descriptor, or data are currently attributed to the wrong file; preserve both slots and re-audit the misplaced contents against the DOL. A multi-descriptor TU needs independent DOL evidence and is not implied by one source file currently defining multiple descriptors.
- Treat the generated
src/dlls/<bank>/<slot>[_<name>]/<file>.cpath as immutable source-truth. Rename symbols and types freely when evidence supports them, but do not rename an existing DLL source folder or filename. Usepython tools/regenerate_dll_scaffold.py --audit-ref <ref>with--slots <range>when checking a suspected path change. - Rehome DLL source one numbered slot at a time. Before moving a source into its canonical folder, audit the complete TU, neighbouring text/data boundaries, descriptor ownership, artificial fragments, and section-alignment overrides, then build it. Do not bulk-rehome DLL sources with path-only mechanical moves.
- Address-suffixed fragments such as
foo_80123456.care not acceptable once DOL evidence shows they belong to one TU. Merge them in retail function order, keep one TU-level compiler profile, and accept match regressions rather than preserving an artificial split. Only redraw a boundary when DOL section, pool, function-order, or source-tag evidence establishes a different real TU. - End an object DLL TU with its
ObjectDescriptordefinition. Do not move the descriptor earlier merely to reproduce post-link section order; keep the source structure plausible and leave the unitNonMatchingwhen the reconstructed declaration order exposes a data mismatch. Conversely, retain a descriptor's proven earlier position when an already-exact TU and retail symbol order show that other TU-owned data follows it; do not reduce a 100% unit for cosmetic uniformity. - When a descriptor symbol provably includes data beyond its advertised callback count, model the complete symbol as an object-descriptor-plus-tail layout. Keep unexplained tail words opaque and size-asserted until real consumers establish their fields; do not inflate the callback type or assign meanings from value patterns alone.
- In an object descriptor initializer, use the function designator directly when its declared
prototype exactly matches the slot typedef, such as
int getExtraSize(void)for anObjectDescriptorExtraSizeCallback. Keep an explicit callback cast when the generic descriptor slot intentionally stores a differently shaped function; do not cast away a real signature mismatch merely for visual uniformity. - Keep a functionless DLL's proven eight-byte null resource record as two raw words. Do not widen
it into
ResourceDescriptororObjectDescriptormerely because the generic registry stores its address; give the record a unit-owned name and cast only at that registry boundary. - Give each cleaned object DLL one canonical unit-owned header under
include/dlls/<bank>/. Keep that header self-contained, put all unit-owned struct definitions and the public API there, and do not use one legacy aggregate header to declare adjacent DLLs. Include the canonical header first in its source; keep private constants in the TU. - Keep layout assertions beside the canonical definition owned by that unit. Do not include neighboring object headers in a consumer solely to repeat unrelated state-size assertions; remove those imports and duplicate assertions when canonicalizing the owning types.
- Before promoting a local state or placement typedef into a canonical header, search the tree for the same type name. If another subsystem already uses that name for a different layout, choose an explicit object-specific name instead of exporting an ambiguous typedef or creating an include collision; back-apply the rename only to consumers of the recovered layout.
- Before renaming a public DLL function, descriptor, or data symbol, search both the source tree and the active-target symbol config for the proposed name. If another TU already owns it, keep the roles distinct with the evidenced source/object namespace; do not let an internal cleanup create a duplicate declaration, duplicate config key, or ambiguous cross-TU API.
- Prefer the canonical
GameObjectandObjAnimComponentfields when they already express a cleaned DLL's accesses. Do not publish a unit-local object overlay that duplicates the common object prefix,extra, animation callback, oruserDataslots; retain a custom overlay only for evidenced class-specific storage that the canonical object record cannot represent. - Keep an engine-owned object table or deliberately reused scratch pointer in its codegen-proven
storage shape when a fully typed local changes MWCC register allocation. Use a narrow cast and
canonical
offsetofexpression at each semantic dereference instead of hard-coded offsets or a fake object overlay; do not extend a typed pointer's lifetime across phases merely for cosmetic uniformity. - Format the object TU and its unit-owned header, but keep shared consumer edits surgical. Adding a
canonical header to a registry such as
modelEngine.cdoes not authorize whole-file formatting or unrelated cleanup there; change only the required include, declaration, cast, or use sites unless that consumer TU is itself the active cleanup target. - Use attached braces for control flow in cleaned code:
if (...) {,} else {,for (...) {,while (...) {, andswitch (...) {. Keep the controlled block on its own indented lines even when it contains one statement. Do not introduce Allman-style control-flow braces into a cleaned TU or preserve them merely because the surrounding imported decomp is inconsistent. - Run
clang-format -ion the active cleaned TU and its unit-owned canonical header, then review the formatting diff and requireclang-format --dry-run --Werrorto pass for both. Commit formatting separately from source changes so compiler and code cleanup diffs remain easy to review. Verify that the formatting-only commit preserves code generation. Do not format a shared consumer merely because the active TU adds one include or declaration there; keep those shared edits surgical unless that consumer is itself the cleanup target. - For the object-DLL housekeeping pass, assign each TU cleanup to a sub-agent and keep no more than three sub-agents active at once. The primary agent must personally review every resulting diff, stale-symbol search, match report, shared-consumer edit, and generated-path audit before committing. Commit and push reviewed TUs one coherent slot at a time.
- When a newly evidenced housekeeping rule exposes the same purely mechanical, byte-neutral issue across previously reviewed object TUs, audit the complete cleaned range and land the back-apply as a dedicated range-audit commit rather than burying it in the current slot. Record every intentional exception, compare each affected object before and after, and keep non-mechanical or codegen-changing cases in their own TU reviews.
- Claim a cleaned TU's complete evidenced constant pool only when the source can emit it without
duplicate named constants, invented section placement, or codegen changes. MWCC may copy a
same-TU named
constscalar into a second anonymous literal while leaving the named definition unreferenced, so source-only ownership and matching values are not enough. Keep the automatic gap until the original ownership model is recoverable; after any claim, verify generated section size, symbols, bytes, relocations, and the final DOL. - Preserve a packed TU-owned diagnostic or string block when splitting it into unreferenced named
constarrays makes MWCC migrate bytes between.dataand.rodata. Give the backing block a semantic name and recover a typed offset view instead, then verify section sizes and contents. - Size placement/setup structs from active-target evidence such as a direct
Obj_AllocObjectSetupallocation or retail romlist width, not from the last field accessed by the TU or donor-project padding. Assert the proven total size as well as every recovered field offset. - Size struct arrays from evidenced indices or explicit runtime capacity, not merely from the gap to the next known field. If the code caps a six-element child list and the next field is eight words later, model six elements plus an unknown trailing word; do not invent a seventh child to consume the offset.
- Consolidate contiguous, entirely unaccessed imported byte fields into one opaque byte span unless
retail layout or a real consumer distinguishes their roles. Do not preserve separate
unkXXbyte scalars merely because Ghidra guessed field boundaries; conversely, keep any byte that is independently loaded, stored, or interpreted at a codegen-significant width as its own field. - Size an object state from that object's allocation contract, not from every helper defined in its
TU. If a helper is called only with another object's larger state and accesses beyond the owner's
getExtraSize, model the owning state and the cross-object helper overlay as separate types, assert both layouts, and keep the cast explicit at the reuse boundary. A proven zerogetExtraSize()owns no extra-state typedef unless independent object-owned storage is separately evidenced. - Do not cast one object's state to an unrelated object's type merely because useful fields happen to share offsets. Recover those fields in the owning state; share a state type only when allocation and behavioral evidence establish a real common contract.
- When retail code provably reads or writes past an allocation or object boundary, preserve and document the exact access without enlarging the owning type to make the bug appear in bounds. Keep the allocation-backed size assertion, use an explicit byte access for the overrun, and audit adjacent data ownership so the source records what is actually clobbered.
- When two proven consumers interpret the same shared placement byte with different signedness and both views are codegen-significant, model explicit union views at the shared offset. Do not force one canonical signedness and scatter casts; assert both offsets and rebuild every consumer.
- Recover a placement field at the width actually loaded. If a supposed
s16is only accessed as*(u8*)&fieldat the field's address, model the evidencedu8plus the following unknown byte; on the big-endian target that cast selects the first/high-order byte, not a generic "low byte." - When a shared placement record has proven mode-specific roles at the same offsets, expose explicit semantic union views in the owning canonical header and use the relevant view in each mode or consumer. Do not preserve one misleading generic name or duplicate the record as competing padded structs; assert every recovered view at the shared offset.
- Include an object's canonical header at direct consumers instead of repeating hand-written
declarations. Where a generic registry intentionally stores differently shaped descriptor
types, use an explicit cast at that boundary rather than lying about the descriptor's type in an
extern. - In an already-exact unit, test apparently redundant predicates, casts, and integer/pointer launders before normalizing them. If the simpler spelling changes codegen, retain the proven spelling and improve the surrounding names instead; cosmetic uniformity does not justify a match regression.
- Do not split a multi-role local in an already-exact function solely to give each lifetime a more specific name without testing codegen. MWCC can assign different nonvolatile registers even when the lifetimes do not overlap; if the split changes codegen, retain one neutrally named local and let each use site supply the meaning.
- Do not delete an unused static helper or collapse a semantic enum into macros in an already-exact TU without comparing the raw object hash and symbol table. Even when MWCC emits no body and objdiff still reports every function and section exact, removing the source-position anchor can renumber anonymous symbols. Retain a compact documented anchor or the clearer declaration when the cosmetic rewrite changes object identity.
- Preserve TU-global declaration order while renaming or retyping symbols, especially mixed-width
.sbssand.sdataobjects. Objdiff can report zero-filled sections and symbol-normalized relocations as exact while MWCC has changed the symbols' packed offsets; inspect object symbol offsets and require the final DOL checksum before accepting the cleanup. - Preserve a literal's C type when lifting it into a named macro or enum. Suffixes are semantic:
replacing
1uwith a macro whose replacement is1can change the usual arithmetic conversions and turn a targetcmplwiintocmpwi, even when the runtime values are identical. Rebuild the affected function after every such lift. - Do not infer unsigned storage merely because a byte is used as a boolean. Preserve signedness
evidenced by the exact load/compare sequence; an
s8zero-test can require the target's sign extension even when negative values have no separately recovered meaning. - Before retaining a unit-prefixed alias for an engine-wide flag, enum value, or ID, search the
canonical engine headers. Use the shared definition when it already exists; local duplicates such
as per-object names for
OBJECT_OBJFLAG_HITDETECT_DISABLEDobscure that multiple DLLs implement the same engine contract. - When an object-family group or ID is owned by one DLL but used by other TUs, define it once in the owning canonical header and use that definition at every proven consumer. Do not preserve consumer-local aliases or raw numeric lookups for the same family contract; audit all active-target uses before declaring the migration complete.
- When a unit-local semantic name conflicts with the canonical name for the same engine value, audit every active-target consumer before choosing either name. If the combined behavior establishes a stronger shared meaning, correct the canonical definition and back-apply it to all consumers instead of preserving divergent aliases or adopting a misleading canonical name.
- Do not promote an unnamed DLL to a named object solely from a nearby debug-side source tag, an adjacent named slot, or behavioral resemblance. Keep a numbered namespace until retail object mappings, strings, symbols, or similarly direct evidence establishes the identity; recover semantic field, state, and helper names in the meantime.
- Once an unnamed numbered slot provably owns an address-labeled descriptor, give that descriptor a
stable numbered symbol such as
gDll144ObjDescriptorand update the active symbol config. Keep the functions in the same numbered namespace and do not treat this internal rename as evidence for renaming the generated source folder or filename. - Prefer real definitions and linkage over
externplaceholders. - Do not hardcode addresses or invent junk
lbl_/fn_names just to force progress. - Scope relocation exclusions for proven integer literals to their source instructions or data objects. A target-address block can hide real callbacks in another version; verify the affected sites independently in each region.
- Do not commit literal recovered source/header artifacts from
orig/intosrc/; keep them in manifests/docs or export them to a local non-source folder when needed. - When materializing retail-backed stubs without a proven directory, keep them at
src/<basename>instead of inventing synthetic folders. - Do not manually force compiler-generated sections or synthesize likely-generated init/ctor/dtor glue unless there is strong evidence.
- Keep code clean. No analysis debris, commented-out experiments, or notes in the project tree.
- Use other regions to inform understanding, not to override the EN target.
- Do not paste raw Dolphin addresses straight into config. Run
python tools/dolphin_sdk_symbols.py summaryfirst, use the inferred address translation, then review the surrounding translated cluster manually.
- Pick a target with real leverage.
- Recover the surrounding dependency cluster, not just the leaf symbol.
- Build the strict hash target with
python configure.py --matchingfollowed byninja(if anything goes wrong, consider runningpython configure.py -v GSAE01 --matching). Always timeout ninja to 30s; plain non-matchingninjacan stop atmain.doland miss the checksum target that CI runs. - Run objdiff and judge net progress.
- If stuck, change angle quickly: adjacent code, data, assets, references, or tooling.
- Commit real progress to the permanent staging branch and push to
origin/staging. Do not push normal development commits directly tomain. - If the process changes (ie we expect ninja to pass after a session, etc.) please update this document.
Commit when both are true:
- The repo is materially better: codegen, data, linkage, structure, or tooling improved, even if marginally.
- The result is plausible original source or materially improves the ability to recover it.
Less process is intentional. Use judgment, move fast, and avoid spending an hour proving a bad assumption.
The Ghidra-imported dll_XXX.c files are usually decomp'd from a different snapshot
than the v1.0 .s we're matching against. Symptom: the report says "1 stub" but
when you read the asm you find 3-5 stubs, and the src functions (often named
FUN_801cXXXX) are at addresses INSIDE the asm symbol ranges - Ghidra split
them differently than the binary did.
Do not try to fix the misaligned FUN_xxx functions in place. Compile order
matters less than you think. Instead use the add-new-function pattern:
- Read
build/GSAE01/asm/main/dll/<unit>.sand list^\.fnsymbols + sizes. That is the canonical function set. - List the src's current function set (
grep -nE "^void|^int|^undefined" src/...). - For each asm symbol missing from src, ADD a new function with that exact name
to the bottom of the src file. The src file's existing
FUN_xxxfunctions can stay - they're dead/unreferenced or referenced only externally. - Read the asm body for the new function and translate to C. Cargo-cult call
patterns from already-matched files:
(*(code *)(*(int *)lbl_XXX + offset))(args)for vtable calls through SDA21 pointer-to-pointer.extern int Obj_IsObjectAlive(void* obj);style — checksrc/main/dll/*.cfor existing extern declarations.(double)lbl_SDA_FLOATfor f1 args to functions that take adouble.*(unsigned char*)(p + off)instead of*(char*)(p + off)when the asm reads withlbz; cmplwi(noextsb.).
- Build, check the report, commit per-function gains. Even a partial fuzzy match (50-90%) is real progress and unblocks the unit from "0 of N matched" purgatory.
For deeper structural fixes (where the asm body of one symbol corresponds to a
FUN_xxx source function, just renamed), see dbbc5ba9 Restructure laser19F:
move the FUN_xxx body into the right symbol with the right signature, delete
the FUN_xxx, drop its .h declaration. Keep FUN_xxx only if other .c
files extern-reference it (e.g. shrine.c calls FUN_801c4b14).
Wins seen in practice: byte-exact matches when the body lines up well, ~50-99%
fuzzy otherwise. The remaining MWCC-vs-target diffs are usually unimportant
scheduling choices (extsb. vs extsb + cmplwi, lis r3 order vs slwi r0,
epilog reordering of lwz r0 vs saved-reg restores) that we can't trivially
force from C source.
The optional joint-matrix analyzer experiment is documented in
docs/foreign/joint_matrices.md: build with python3 tools/dtk_nocfa.py --test,
then configure with --matching --joint-matrices-nocfa. Its local DTK patch trusts
the evidenced outer function extent; it does not establish source-language
provenance or change the assembly policy below. Default builds keep upstream DTK.
Inline asm{} is banned outside src/dolphin/. Inside SDK code, the only
exception is paired-single psq_l / psq_st when MWCC has no intrinsic and a
known-good donor or original binary proves the sequence. Do not trade plausible
C for a higher match score; a clean-C 90% match is better than an asm 100% match.
Narrow evidence-backed exception (2026-09-07), under the active goal's allowance
for compelling evidence: modelBlendMorphTargetChunk and modelReadMorphDelta
in src/main/model.c preserve the retail decoder's private register interface.
Its cursor and multiple results use non-EABI registers, and the parent owns
callee-save preservation across four direct calls. This establishes an assembly
interface, not definitive original-language provenance. Retain the portable C
reference and run both morph probes; see docs/model_morph_targets.md.
This exception does not authorize assembly in other functions.
Narrow hardware-access exception (2026-09-08), under the same compelling-evidence
allowance: setGQR6, setGQR7, and the private inline modelGetGQR7 in
src/main/model.c may use one mtspr or mfspr instruction each. Retail setters
write actual quantization registers consumed by the vertex kernels; a RAM shadow
does not implement that contract. The inline reader lets the existing scalar
reconstructions use live register state without fabricated storage. This does
not authorize replacing their C arithmetic with assembly or claim those kernels
match retail. See docs/model_quantization_registers.md.
Inline pragmas are banned. #pragma of any kind must not appear inline in src/main/ or src/track/
source; pragmas may only be configured at the TU level via configure.py cflags.
User-authorized exception (2026-09-07): subtitleUpdateAndDraw in
src/main/subtitle.c may use optimization_level 2, restoring the TU's level 1
afterward. This is a matching workaround after source-level investigation, not
evidence of the original optimization policy. See docs/subtitle_matching.md.
The exception does not authorize pragmas in other functions or units.
Try these one-line source rewrites first; sometimes they're enough on their own:
& 0xff7f→& ~0x80(often givesrlwinmwhere literal givesandi)*(int*)p != 0→*(void**)p != NULL(signed → unsigned compare)if (v <= K) return v; return K;→if (v > K) v = K; return v;(givesblelrfrom a single value-clamp)- For a constant reused across N consecutive stores, lift to a local:
f32 fz = lbl_xxx; *p1 = fz; *p2 = fz; *p3 = fz;so MWCC CSEs across the stores instead of reloading. - Swap declaration order of two
intlocals whose addresses are passed to an out-param function. The compiler allocates stack offsets in declaration order; if target wants&objectIndexat sp+8 and&objectCountat sp+0xc, declare objectCount first.