diff --git a/Makefile b/Makefile index 30b0c6265..f04c48ae1 100644 --- a/Makefile +++ b/Makefile @@ -366,6 +366,13 @@ ASM_SRCS := $(shell find $(ASM_DIR) -name '*.s' -not -path '$(ASM_DIR)/nonmatchi C_SRCS := $(shell find $(SRC_DIR) -name '*.c' $(SRC_PRUNE) 2>/dev/null) OBJS := $(ASM_SRCS:%.s=build/%.o) $(C_SRCS:%.c=build/%.o) +# Header-dependency tracking (Phase 15): now that shared headers (src/shared/*.h, common.h) +# are build inputs, the cpp stage emits a .d per C object (-MMD, below) so editing a #included +# header triggers a recompile — incremental `make check` stays trustworthy (R22). .d files live +# under build/ (gitignored); -include ignores them on the first build. No effect on output bytes. +C_DEPS := $(C_SRCS:%.c=build/%.d) +-include $(C_DEPS) + # splat `bin` subsegs (raw byte regions — e.g. an overlay's trailing non-word-aligned bytes that # spimdisasm's data path drops, since it won't emit a <4-byte partial word). splat extracts them to # assets//*.bin and references build/assets//*.o in the .ld; wrap each raw .bin into a @@ -420,7 +427,7 @@ CC1FLAGS := -quiet -O2 -G0 -mips1 -mcpu=3000 -mgas -msoft-float -fgnu-linker build/src/%.o: src/%.c @mkdir -p $(dir $@) @echo " CC $@" - @set -o pipefail; $(CPP) $(CPPFLAGS) $< | $(CC1_PSX) $(CC1FLAGS) | $(VENV_PY) $(MASPSX) --aspsx-version=$(ASPSX_VERSION) $(MASPSX_FLAGS) | $(AS) $(ASFLAGS) -o $@ + @set -o pipefail; $(CPP) $(CPPFLAGS) -MMD -MP -MT $@ -MF $(@:.o=.d) $< | $(CC1_PSX) $(CC1FLAGS) | $(VENV_PY) $(MASPSX) --aspsx-version=$(ASPSX_VERSION) $(MASPSX_FLAGS) | $(AS) $(ASFLAGS) -o $@ # Per-module optimization override (SETUP §5.5 — per-module compiler mixing). The boot/ # main/game-mode-dispatch module (src/boot.c, vram 0x80010000-0x800123F0) was compiled at diff --git a/config/dedup.us.yaml b/config/dedup.us.yaml index 0b58c06b4..7c2248411 100644 --- a/config/dedup.us.yaml +++ b/config/dedup.us.yaml @@ -89,3 +89,91 @@ groups: members: - { binary: ov_SC01_005, vram: 0x8012E27C, name: func_8012E27C } - { binary: ov_SC01_006, vram: 0x8012E27C, name: func_8012E27C } + - id: E_func_80128EA8 + tier: h_exact + hash: 2905b55f46d5c22ad234e800654eafc93b791693 + source: src/shared/engine_core.h + func: DEFINE_func_80128EA8 + members: + - { binary: ov_SC01_077, vram: 0x80128EA8, name: func_80128EA8 } + - { binary: ov_SC01_005, vram: 0x80128EA8, name: func_80128EA8 } + - { binary: ov_SC01_006, vram: 0x80128EA8, name: func_80128EA8 } + - { binary: ov_SC03_001, vram: 0x80128EA8, name: func_80128EA8 } + - { binary: ov_SC01_000, vram: 0x80128EA8, name: func_80128EA8 } + - { binary: ov_SC01_001, vram: 0x80128EA8, name: func_80128EA8 } + - { binary: ov_SC02_000, vram: 0x80128EA8, name: func_80128EA8 } + - { binary: ov_SC02_003, vram: 0x80128EA8, name: func_80128EA8 } + - { binary: ov_SC03_002, vram: 0x80128EA8, name: func_80128EA8 } + - { binary: ov_SC03_006, vram: 0x80128EA8, name: func_80128EA8 } + - { binary: ov_SC04_000, vram: 0x80128EA8, name: func_80128EA8 } + - { binary: ov_SC04_018, vram: 0x80128EA8, name: func_80128EA8 } + - { binary: ov_SC04_019, vram: 0x80128EA8, name: func_80128EA8 } + - { binary: ov_SC05_000, vram: 0x80128EA8, name: func_80128EA8 } + - { binary: ov_SC06_000, vram: 0x80128EA8, name: func_80128EA8 } + - { binary: ov_SC07_000, vram: 0x80128EA8, name: func_80128EA8 } + - id: E_func_8012A568 + tier: h_exact + hash: bdb48359cd3a9c1e714bbc0347d88dc35e705ffe + source: src/shared/engine_core.h + func: DEFINE_func_8012A568 + members: + - { binary: ov_SC01_077, vram: 0x8012A568, name: func_8012A568 } + - { binary: ov_SC01_005, vram: 0x8012A568, name: func_8012A568 } + - { binary: ov_SC01_006, vram: 0x8012A568, name: func_8012A568 } + - { binary: ov_SC03_001, vram: 0x8012A568, name: func_8012A568 } + - { binary: ov_SC01_000, vram: 0x8012A568, name: func_8012A568 } + - { binary: ov_SC01_001, vram: 0x8012A568, name: func_8012A568 } + - { binary: ov_SC02_000, vram: 0x8012A568, name: func_8012A568 } + - { binary: ov_SC02_003, vram: 0x8012A568, name: func_8012A568 } + - { binary: ov_SC03_002, vram: 0x8012A568, name: func_8012A568 } + - { binary: ov_SC03_006, vram: 0x8012A568, name: func_8012A568 } + - { binary: ov_SC04_000, vram: 0x8012A568, name: func_8012A568 } + - { binary: ov_SC04_018, vram: 0x8012A568, name: func_8012A568 } + - { binary: ov_SC04_019, vram: 0x8012A568, name: func_8012A568 } + - { binary: ov_SC05_000, vram: 0x8012A568, name: func_8012A568 } + - { binary: ov_SC06_000, vram: 0x8012A568, name: func_8012A568 } + - { binary: ov_SC07_000, vram: 0x8012A568, name: func_8012A568 } + - id: E_func_80132EC4 + tier: h_exact + hash: 5d8b76ebf9470cc559a263f93949edfc95c9821e + source: src/shared/engine_core.h + func: DEFINE_func_80132EC4 + members: + - { binary: ov_SC01_077, vram: 0x80132EC4, name: func_80132EC4 } + - { binary: ov_SC01_005, vram: 0x80132EC4, name: func_80132EC4 } + - { binary: ov_SC01_006, vram: 0x80132EC4, name: func_80132EC4 } + - { binary: ov_SC03_001, vram: 0x80132EC4, name: func_80132EC4 } + - { binary: ov_SC01_000, vram: 0x80132EC4, name: func_80132EC4 } + - { binary: ov_SC01_001, vram: 0x80132EC4, name: func_80132EC4 } + - { binary: ov_SC02_000, vram: 0x80132EC4, name: func_80132EC4 } + - { binary: ov_SC02_003, vram: 0x80132EC4, name: func_80132EC4 } + - { binary: ov_SC03_002, vram: 0x80132EC4, name: func_80132EC4 } + - { binary: ov_SC03_006, vram: 0x80132EC4, name: func_80132EC4 } + - { binary: ov_SC04_000, vram: 0x80132EC4, name: func_80132EC4 } + - { binary: ov_SC04_018, vram: 0x80132EC4, name: func_80132EC4 } + - { binary: ov_SC04_019, vram: 0x80132EC4, name: func_80132EC4 } + - { binary: ov_SC05_000, vram: 0x80132EC4, name: func_80132EC4 } + - { binary: ov_SC06_000, vram: 0x80132EC4, name: func_80132EC4 } + - { binary: ov_SC07_000, vram: 0x80132EC4, name: func_80132EC4 } + - id: E_func_80138C30 + tier: h_exact + hash: 9bda7673e95d98766414bc8cc2133f95663ee3a0 + source: src/shared/engine_core.h + func: DEFINE_func_80138C30 + members: + - { binary: ov_SC01_077, vram: 0x80138C30, name: func_80138C30 } + - { binary: ov_SC01_005, vram: 0x80138C30, name: func_80138C30 } + - { binary: ov_SC01_006, vram: 0x80138C30, name: func_80138C30 } + - { binary: ov_SC03_001, vram: 0x80138C30, name: func_80138C30 } + - { binary: ov_SC01_000, vram: 0x80138C30, name: func_80138C30 } + - { binary: ov_SC01_001, vram: 0x80138C30, name: func_80138C30 } + - { binary: ov_SC02_000, vram: 0x80138C30, name: func_80138C30 } + - { binary: ov_SC02_003, vram: 0x80138C30, name: func_80138C30 } + - { binary: ov_SC03_002, vram: 0x80138C30, name: func_80138C30 } + - { binary: ov_SC03_006, vram: 0x80138C30, name: func_80138C30 } + - { binary: ov_SC04_000, vram: 0x80138C30, name: func_80138C30 } + - { binary: ov_SC04_018, vram: 0x80138C30, name: func_80138C30 } + - { binary: ov_SC04_019, vram: 0x80138C30, name: func_80138C30 } + - { binary: ov_SC05_000, vram: 0x80138C30, name: func_80138C30 } + - { binary: ov_SC06_000, vram: 0x80138C30, name: func_80138C30 } + - { binary: ov_SC07_000, vram: 0x80138C30, name: func_80138C30 } diff --git a/docs/SETUP.md b/docs/SETUP.md index 8480f47f8..6250766e1 100644 --- a/docs/SETUP.md +++ b/docs/SETUP.md @@ -654,6 +654,7 @@ Every script under `tools/` (plus the two report make-targets), grouped by purpo | | `tools/dup_report.py` | Duplicate-function report; `--cross` (Phase 11) buckets all binaries → `docs/duplicates.cross.md`. | | **Cross-binary dedup** (Phase 11, cookbook §11) | `tools/sig_image.py` | **Ghidra-FREE** per-function signer for a flat image (overlay/resident); `h_exact` byte-matches the Ghidra dumper, self-consistent `h_norm`; linear-partition + `detect_code_end` boundaries. | | | `tools/dedup_integrate.py` | Byte-honesty validator for `config/dedup.us.yaml` code-shares (`--check`; fail-closed on sig-hash drift). | +| | `tools/dedup_propagate.py` | **(Phase 15, cookbook §14)** Match-once → propagate-many: lift a matched body, author a `DEFINE_func_()` macro in `src/shared/engine_core.h`, instantiate it at every onboarded overlay sharing that `h_exact`, byte-gate each (fail-closed), register in `dedup.us.yaml`. `--addr`/`--auto-from`/`--check-only`. | | | `config/dedup.us.yaml` / `src/shared/*.h` | The code-share registry + the shared bodies (one macro → N sites, byte-gated). | | | `make report` / `make sig-refresh` / `make sig-overlays` | Convenience targets: reports (+`--cross`) / Ghidra signature-dump / Ghidra-free sign all 134 overlays. | diff --git a/docs/matching-cookbook.md b/docs/matching-cookbook.md index 83e9a0d54..085512b4b 100644 --- a/docs/matching-cookbook.md +++ b/docs/matching-cookbook.md @@ -769,3 +769,46 @@ Recursive `$(MAKE) BINARY=` over `$(BINARIES)` (NOT `foreach` — the OBJS gl one PASS/FAIL + per-binary `.run/check..log`. Serial across binaries (shared `build/asm|src/**` outputs make binary-level `-j` racy). Day-to-day incremental; milestone fleet proof = a CLEAN run (R22): `make clean && for b in $(BINARIES); do make extract BINARY=$$b; done && make check-all`. + +## §14 Propagate a matched function across the fleet — `tools/dedup_propagate.py` (Phase 15) + +**The economics that drives Phase 15.** Overlays are position-locked at `0x80128158`, so a shared engine +function has the SAME vaddr (hence the SAME `func_` symbol) and a BYTE-IDENTICAL body in every overlay +that contains it. Measured on the fleet: **577 of `ov_SC01_077`'s 785 matched functions are `h_exact`-identical +across ALL 134 overlays** (~2.19 MB collapsible) — already matched, just needing propagation. So the rule is +**match once → propagate, do NOT re-harvest each overlay.** (`tools/dedup_propagate.py --auto-from ov_SC01_077` +enumerates exactly this set: matched-as-an-inline-def in the source AND `h_exact`-shared across ≥`--min-reach`.) + +**The tool.** `tools/dedup_propagate.py --addr 0x..[,..] --source-overlay ` (or `--auto-from ` for the +whole shared set; `--check-only` for a dry-run plan): +1. extract the matched body (preceding `extern`s + the def, brace-matched) from the source overlay's `.c`; +2. author it ONCE as a tool-generated `#define DEFINE_func_() \`-continued macro in `src/shared/engine_core.h` + (idempotent; **refuses `//` line comments** — they break line-splicing; block `/* */` is fine); +3. at every onboarded overlay whose sig shows that `h_exact` (lead with `h_exact` — guaranteed byte-identity), + replace that function's `INCLUDE_ASM` stub (or, in the source overlay, its inline def) **in place** with + `DEFINE_func_()` — address order preserved; `#include "../shared/engine_core.h"` added once after + `common.h`; +4. **byte-gate** each touched overlay (`make build BINARY=` == its `check.sha`); on ANY miss, restore EVERY + file from an in-memory snapshot and abort (fail-closed; nothing wrong lands); +5. register the group in `config/dedup.us.yaml`, validated by `dedup_integrate --check`. + +**Key gotchas (each cost a real bug or false pass during the Phase-15 proof):** +- **Key by addr-int, never the string.** `sig_image` writes lowercase hex (`0x80144b9c`); splat's symbol is + uppercase (`func_80144B9C`). Compare `int(addr,16)`; render the symbol as `func_%08X`. +- **Accumulate edits from the on-disk text, not a snapshot cache.** When a batch propagates several functions into + the same overlay, re-read the file before each edit (the snapshot dict holds the ORIGINAL for restore, not the + running state) — else each function's edit clobbers the previous and only the last lands. +- **The byte-gate can't catch under-application.** A leftover `INCLUDE_ASM` stub is itself byte-identical (it just + uses the asm), so `make check` passes even if a function wasn't actually converted. Add a STRUCTURAL self-check: + after editing, assert each member's `.c` contains `DEFINE_func_()` and NO leftover stub line. +- **Header-dependency tracking is mandatory once shared headers are build inputs (R22).** The Makefile C rule + originally made `build/src/%.o` depend only on the `.c`, so editing `engine_core.h`/`common.h` did NOT trigger a + recompile → an incremental `make check` after a header-only edit was STALE (a wrong shared body falsely passed). + Fixed: the `cpp` stage now emits a `.d` (`-MMD -MP -MT $@ -MF $(@:.o=.d)`) and the Makefile `-include`s + `$(C_SRCS:%.c=build/%.d)`. Side-effect only — output bytes unchanged. With it, the negative test (corrupt a + macro body → `make check` rebuilds via the `.d` → SHA mismatch → fail) behaves correctly. +- **Idempotent + resumable.** A propagated source function becomes a `DEFINE_…` macro (no longer an inline def), so + `--auto-from` re-runs skip the done ones; `--addr` re-runs no-op (source is now a macro, plan is empty). +- **Scale note (deferred until it bites):** `dedup.us.yaml` members are listed verbose (`{binary,vram,name}`). + For the full 134-overlay × hundreds-of-functions bulk, switch to a `vram + binaries:[...]` shorthand (expanded by + `dedup_integrate`/`progress.py`) before it becomes a 5-figure-line file. diff --git a/phase-ends/CURRENT_PHASE.md b/phase-ends/CURRENT_PHASE.md index 34236ea92..5b56e97af 100644 --- a/phase-ends/CURRENT_PHASE.md +++ b/phase-ends/CURRENT_PHASE.md @@ -16,7 +16,8 @@ Overlays are position-locked at vram 0x80128158 and share an enormous engine cor ## Tasks (harness IDs in brackets; one at a time, P4) - [x] **T0 [#1] — DONE** (commit commit:0101) — Phase-14 deferral recorded in `docs/gen2-roadmap.md`. - [x] **T1 [#2] — DONE** — onboarded diverse wave-1 (12 overlays, all 7 SC areas + 2 dup pairs); `check-all` **18/18 byte-identical**, 0 anomalies. -- [ ] **T2 [#3] — IN PROGRESS** — build `tools/dedup_propagate.py` + `src/shared/engine_core.h`; prove on the wave (Tier 1 / Max). +- [x] **T2 [#3] — DONE** — `tools/dedup_propagate.py` + `src/shared/engine_core.h` built & proven: 4 funcs propagated across all 16 onboarded overlays, clean `check-all` **18/18**, dedup_integrate green, idempotent, negative-test fails-closed. Also added Makefile `-MMD` header-dep tracking (closed a stale-build gap the negative test exposed). +- [ ] **T3 [#4] — IN PROGRESS** — onboard remaining ~118 overlays (full fleet → 136 binaries); clean `check-all` 136/136. - [ ] T3 [#4] — onboard remaining ~118 overlays (full fleet); 136/136 `check-all` from clean (R22). - [ ] T4 [#5] — `progress.py --fleet` → `docs/progress.fleet.md`; wire into `make report`. **(P6 rules check after T4.)** - [ ] T5 [#6] — Ultracode shared-core harvest Tier-1 (small/high-count) + propagate fleet-wide. **(R27 toggle prompt.)** @@ -37,3 +38,5 @@ Overlays are position-locked at vram 0x80128158 and share an enormous engine cor - 2026-06-16 — Session start: read load order (PROJECT_CONTEXT + all 14 PhaseEnds, no prior CURRENT_PHASE). Drew: skip Phase 14, do Phase 15, Max effort, plan mode. Verified ground truth (overlay count, dup pairs, cross-report, per-overlay counts) directly, correcting several sub-agent overclaims (R14). Plan approved (gate 1). Task list created (R28). Starting T0. - 2026-06-16 — **T0 done** (commit:0101): Phase-14 deferral propagated through gen2-roadmap (additive, H5). - 2026-06-16 — **T1 done**: onboarded wave-1 = ov_{SC01_000, SC01_001, SC02_000, SC02_003, SC03_002, SC03_006, SC04_000, SC04_018, SC04_019, SC05_000, SC06_000, SC07_000} (all byte-identical at 100% INCLUDE_ASM in ~50s; dup pairs SC02_000≡003=5ece4bca, SC04_018≡019=fe9b413f). Fleet `make check-all` = **18/18 passed**. Position-lock risk retired across all 7 areas — no anomalies. Next: T2 (dedup_propagate.py). +- 2026-06-16 — **KEY FINDING (reshapes T5):** of `ov_SC01_077`'s 785 matched signed functions, **577 are h_exact-identical across ALL 134 overlays** (2.19 MB collapsible) — already matched, just need propagating (free credit, no harvest). 204 are 077-unique. So T5 has two parts: (a) bulk-propagate these 577 already-matched shared funcs fleet-wide (mechanical, after T3); (b) Ultracode-harvest the still-UNMATCHED shared core (the big ex0–ex50 giants/mediums, mostly unmatched even in 077). +- 2026-06-16 — **T2 done**: built `tools/dedup_propagate.py` (extract body → DEFINE_func macro in src/shared/engine_core.h → in-place stub/def replace at every h_exact member → snapshot+byte-gate fail-closed → register dedup.us.yaml). Proven on 4 medium funcs (0x80128EA8/8012A568/80132EC4/80138C30) across all 16 onboarded: clean `check-all` 18/18, dedup_integrate 8 validated/0 failed, idempotent re-run no-op, negative test (corrupt body) → `make check` FAILS then restores. Fixed 2 bugs found by self-verification: (i) edit-accumulation read from snapshot not disk (only last fn landed); (ii) **Makefile had no header-dep tracking** → header-only edits were stale/false-pass → added `cpp -MMD -MP` + `-include $(C_DEPS)` (output-neutral; R22-clean 18/18). Cookbook §14, SETUP inventory updated. Next: T3. diff --git a/src/ov_SC01_000/ov_SC01_000.c b/src/ov_SC01_000/ov_SC01_000.c index af590df24..a7e62acca 100644 --- a/src/ov_SC01_000/ov_SC01_000.c +++ b/src/ov_SC01_000/ov_SC01_000.c @@ -1,4 +1,5 @@ #include "common.h" +#include "../shared/engine_core.h" INCLUDE_ASM("asm/ov_SC01_000/nonmatchings/ov_SC01_000", func_80128158); @@ -72,7 +73,7 @@ INCLUDE_ASM("asm/ov_SC01_000/nonmatchings/ov_SC01_000", func_80128D60); INCLUDE_ASM("asm/ov_SC01_000/nonmatchings/ov_SC01_000", func_80128DB4); -INCLUDE_ASM("asm/ov_SC01_000/nonmatchings/ov_SC01_000", func_80128EA8); +DEFINE_func_80128EA8() /* dedup: shared engine-core @0x80128EA8 (src/shared) */ INCLUDE_ASM("asm/ov_SC01_000/nonmatchings/ov_SC01_000", func_80128ED8); @@ -148,7 +149,7 @@ INCLUDE_ASM("asm/ov_SC01_000/nonmatchings/ov_SC01_000", func_8012A464); INCLUDE_ASM("asm/ov_SC01_000/nonmatchings/ov_SC01_000", func_8012A4BC); -INCLUDE_ASM("asm/ov_SC01_000/nonmatchings/ov_SC01_000", func_8012A568); +DEFINE_func_8012A568() /* dedup: shared engine-core @0x8012A568 (src/shared) */ INCLUDE_ASM("asm/ov_SC01_000/nonmatchings/ov_SC01_000", func_8012A598); @@ -607,7 +608,7 @@ INCLUDE_ASM("asm/ov_SC01_000/nonmatchings/ov_SC01_000", func_80132DC4); INCLUDE_ASM("asm/ov_SC01_000/nonmatchings/ov_SC01_000", func_80132E6C); -INCLUDE_ASM("asm/ov_SC01_000/nonmatchings/ov_SC01_000", func_80132EC4); +DEFINE_func_80132EC4() /* dedup: shared engine-core @0x80132EC4 (src/shared) */ INCLUDE_ASM("asm/ov_SC01_000/nonmatchings/ov_SC01_000", func_80132EF4); @@ -760,7 +761,7 @@ INCLUDE_ASM("asm/ov_SC01_000/nonmatchings/ov_SC01_000", func_80138B88); INCLUDE_ASM("asm/ov_SC01_000/nonmatchings/ov_SC01_000", func_80138BE0); -INCLUDE_ASM("asm/ov_SC01_000/nonmatchings/ov_SC01_000", func_80138C30); +DEFINE_func_80138C30() /* dedup: shared engine-core @0x80138C30 (src/shared) */ INCLUDE_ASM("asm/ov_SC01_000/nonmatchings/ov_SC01_000", func_80138C60); diff --git a/src/ov_SC01_001/ov_SC01_001.c b/src/ov_SC01_001/ov_SC01_001.c index 8430c96a7..8eea88874 100644 --- a/src/ov_SC01_001/ov_SC01_001.c +++ b/src/ov_SC01_001/ov_SC01_001.c @@ -1,4 +1,5 @@ #include "common.h" +#include "../shared/engine_core.h" INCLUDE_ASM("asm/ov_SC01_001/nonmatchings/ov_SC01_001", func_80128158); @@ -72,7 +73,7 @@ INCLUDE_ASM("asm/ov_SC01_001/nonmatchings/ov_SC01_001", func_80128D60); INCLUDE_ASM("asm/ov_SC01_001/nonmatchings/ov_SC01_001", func_80128DB4); -INCLUDE_ASM("asm/ov_SC01_001/nonmatchings/ov_SC01_001", func_80128EA8); +DEFINE_func_80128EA8() /* dedup: shared engine-core @0x80128EA8 (src/shared) */ INCLUDE_ASM("asm/ov_SC01_001/nonmatchings/ov_SC01_001", func_80128ED8); @@ -148,7 +149,7 @@ INCLUDE_ASM("asm/ov_SC01_001/nonmatchings/ov_SC01_001", func_8012A464); INCLUDE_ASM("asm/ov_SC01_001/nonmatchings/ov_SC01_001", func_8012A4BC); -INCLUDE_ASM("asm/ov_SC01_001/nonmatchings/ov_SC01_001", func_8012A568); +DEFINE_func_8012A568() /* dedup: shared engine-core @0x8012A568 (src/shared) */ INCLUDE_ASM("asm/ov_SC01_001/nonmatchings/ov_SC01_001", func_8012A598); @@ -607,7 +608,7 @@ INCLUDE_ASM("asm/ov_SC01_001/nonmatchings/ov_SC01_001", func_80132DC4); INCLUDE_ASM("asm/ov_SC01_001/nonmatchings/ov_SC01_001", func_80132E6C); -INCLUDE_ASM("asm/ov_SC01_001/nonmatchings/ov_SC01_001", func_80132EC4); +DEFINE_func_80132EC4() /* dedup: shared engine-core @0x80132EC4 (src/shared) */ INCLUDE_ASM("asm/ov_SC01_001/nonmatchings/ov_SC01_001", func_80132EF4); @@ -760,7 +761,7 @@ INCLUDE_ASM("asm/ov_SC01_001/nonmatchings/ov_SC01_001", func_80138B88); INCLUDE_ASM("asm/ov_SC01_001/nonmatchings/ov_SC01_001", func_80138BE0); -INCLUDE_ASM("asm/ov_SC01_001/nonmatchings/ov_SC01_001", func_80138C30); +DEFINE_func_80138C30() /* dedup: shared engine-core @0x80138C30 (src/shared) */ INCLUDE_ASM("asm/ov_SC01_001/nonmatchings/ov_SC01_001", func_80138C60); diff --git a/src/ov_SC01_005/ov_SC01_005.c b/src/ov_SC01_005/ov_SC01_005.c index d3252e6ac..ac02bb57c 100644 --- a/src/ov_SC01_005/ov_SC01_005.c +++ b/src/ov_SC01_005/ov_SC01_005.c @@ -1,4 +1,5 @@ #include "common.h" +#include "../shared/engine_core.h" #include "../shared/ov_setters.h" /* dedup share across the SC01/005 ≡ 006 pair (cookbook §11) */ INCLUDE_ASM("asm/ov_SC01_005/nonmatchings/ov_SC01_005", func_80128158); @@ -73,7 +74,7 @@ INCLUDE_ASM("asm/ov_SC01_005/nonmatchings/ov_SC01_005", func_80128D60); INCLUDE_ASM("asm/ov_SC01_005/nonmatchings/ov_SC01_005", func_80128DB4); -INCLUDE_ASM("asm/ov_SC01_005/nonmatchings/ov_SC01_005", func_80128EA8); +DEFINE_func_80128EA8() /* dedup: shared engine-core @0x80128EA8 (src/shared) */ INCLUDE_ASM("asm/ov_SC01_005/nonmatchings/ov_SC01_005", func_80128ED8); @@ -149,7 +150,7 @@ INCLUDE_ASM("asm/ov_SC01_005/nonmatchings/ov_SC01_005", func_8012A464); INCLUDE_ASM("asm/ov_SC01_005/nonmatchings/ov_SC01_005", func_8012A4BC); -INCLUDE_ASM("asm/ov_SC01_005/nonmatchings/ov_SC01_005", func_8012A568); +DEFINE_func_8012A568() /* dedup: shared engine-core @0x8012A568 (src/shared) */ INCLUDE_ASM("asm/ov_SC01_005/nonmatchings/ov_SC01_005", func_8012A598); @@ -608,7 +609,7 @@ INCLUDE_ASM("asm/ov_SC01_005/nonmatchings/ov_SC01_005", func_80132DC4); INCLUDE_ASM("asm/ov_SC01_005/nonmatchings/ov_SC01_005", func_80132E6C); -INCLUDE_ASM("asm/ov_SC01_005/nonmatchings/ov_SC01_005", func_80132EC4); +DEFINE_func_80132EC4() /* dedup: shared engine-core @0x80132EC4 (src/shared) */ INCLUDE_ASM("asm/ov_SC01_005/nonmatchings/ov_SC01_005", func_80132EF4); @@ -761,7 +762,7 @@ INCLUDE_ASM("asm/ov_SC01_005/nonmatchings/ov_SC01_005", func_80138B88); INCLUDE_ASM("asm/ov_SC01_005/nonmatchings/ov_SC01_005", func_80138BE0); -INCLUDE_ASM("asm/ov_SC01_005/nonmatchings/ov_SC01_005", func_80138C30); +DEFINE_func_80138C30() /* dedup: shared engine-core @0x80138C30 (src/shared) */ INCLUDE_ASM("asm/ov_SC01_005/nonmatchings/ov_SC01_005", func_80138C60); diff --git a/src/ov_SC01_006/ov_SC01_006.c b/src/ov_SC01_006/ov_SC01_006.c index eaa38a881..b466e69e3 100644 --- a/src/ov_SC01_006/ov_SC01_006.c +++ b/src/ov_SC01_006/ov_SC01_006.c @@ -1,4 +1,5 @@ #include "common.h" +#include "../shared/engine_core.h" #include "../shared/ov_setters.h" /* dedup share across the SC01/005 ≡ 006 pair (cookbook §11) */ INCLUDE_ASM("asm/ov_SC01_006/nonmatchings/ov_SC01_006", func_80128158); @@ -73,7 +74,7 @@ INCLUDE_ASM("asm/ov_SC01_006/nonmatchings/ov_SC01_006", func_80128D60); INCLUDE_ASM("asm/ov_SC01_006/nonmatchings/ov_SC01_006", func_80128DB4); -INCLUDE_ASM("asm/ov_SC01_006/nonmatchings/ov_SC01_006", func_80128EA8); +DEFINE_func_80128EA8() /* dedup: shared engine-core @0x80128EA8 (src/shared) */ INCLUDE_ASM("asm/ov_SC01_006/nonmatchings/ov_SC01_006", func_80128ED8); @@ -149,7 +150,7 @@ INCLUDE_ASM("asm/ov_SC01_006/nonmatchings/ov_SC01_006", func_8012A464); INCLUDE_ASM("asm/ov_SC01_006/nonmatchings/ov_SC01_006", func_8012A4BC); -INCLUDE_ASM("asm/ov_SC01_006/nonmatchings/ov_SC01_006", func_8012A568); +DEFINE_func_8012A568() /* dedup: shared engine-core @0x8012A568 (src/shared) */ INCLUDE_ASM("asm/ov_SC01_006/nonmatchings/ov_SC01_006", func_8012A598); @@ -608,7 +609,7 @@ INCLUDE_ASM("asm/ov_SC01_006/nonmatchings/ov_SC01_006", func_80132DC4); INCLUDE_ASM("asm/ov_SC01_006/nonmatchings/ov_SC01_006", func_80132E6C); -INCLUDE_ASM("asm/ov_SC01_006/nonmatchings/ov_SC01_006", func_80132EC4); +DEFINE_func_80132EC4() /* dedup: shared engine-core @0x80132EC4 (src/shared) */ INCLUDE_ASM("asm/ov_SC01_006/nonmatchings/ov_SC01_006", func_80132EF4); @@ -761,7 +762,7 @@ INCLUDE_ASM("asm/ov_SC01_006/nonmatchings/ov_SC01_006", func_80138B88); INCLUDE_ASM("asm/ov_SC01_006/nonmatchings/ov_SC01_006", func_80138BE0); -INCLUDE_ASM("asm/ov_SC01_006/nonmatchings/ov_SC01_006", func_80138C30); +DEFINE_func_80138C30() /* dedup: shared engine-core @0x80138C30 (src/shared) */ INCLUDE_ASM("asm/ov_SC01_006/nonmatchings/ov_SC01_006", func_80138C60); diff --git a/src/ov_SC01_077/ov_SC01_077.c b/src/ov_SC01_077/ov_SC01_077.c index 75ccc523a..b1e43eeba 100644 --- a/src/ov_SC01_077/ov_SC01_077.c +++ b/src/ov_SC01_077/ov_SC01_077.c @@ -1,4 +1,5 @@ #include "common.h" +#include "../shared/engine_core.h" extern void func_80128288(void); @@ -163,13 +164,7 @@ INCLUDE_ASM("asm/ov_SC01_077/nonmatchings/ov_SC01_077", func_80128D60); INCLUDE_ASM("asm/ov_SC01_077/nonmatchings/ov_SC01_077", func_80128DB4); -void func_80128EA8(s32 a0, s32 a1, s32 a2) { - *(s32 *)(a1 + 0x0) = a2; /* sw a2, 0x0(a1) */ - *(s16 *)(a1 + 0x4) = 0; /* sh zero, 0x4(a1) */ - *(s16 *)(a1 + 0x6) = (*(s32 *)(a2 + 0x4)) & 0x3F; /* lw; andi 0x3F; sh 0x6(a1) */ - *(s16 *)(a0 + 0x28) = *(u16 *)(a2 + 0x0); /* lhu 0x0(a2); sh 0x28(a0) */ - *(s16 *)(a0 + 0x2A) = *(u16 *)(a2 + 0x2); /* lhu 0x2(a2); sh 0x2A(a0) */ -} +DEFINE_func_80128EA8() /* dedup: shared engine-core @0x80128EA8 (src/shared) */ INCLUDE_ASM("asm/ov_SC01_077/nonmatchings/ov_SC01_077", func_80128ED8); @@ -324,12 +319,7 @@ INCLUDE_ASM("asm/ov_SC01_077/nonmatchings/ov_SC01_077", func_8012A464); INCLUDE_ASM("asm/ov_SC01_077/nonmatchings/ov_SC01_077", func_8012A4BC); -extern void func_8012A598(void *a0); - -void func_8012A568(void (*a0)(void)) { - func_8012A598(a0); - a0(); -} +DEFINE_func_8012A568() /* dedup: shared engine-core @0x8012A568 (src/shared) */ INCLUDE_ASM("asm/ov_SC01_077/nonmatchings/ov_SC01_077", func_8012A598); @@ -1012,12 +1002,7 @@ INCLUDE_ASM("asm/ov_SC01_077/nonmatchings/ov_SC01_077", func_80132DC4); INCLUDE_ASM("asm/ov_SC01_077/nonmatchings/ov_SC01_077", func_80132E6C); -void func_80132EC4(void *a0, s16 a1) { - while (*(s32 *)a0 != 0) { - *(s16 *)((s32)a0 + 4) = a1; - a0 = (void *)((s32)a0 + 8); - } -} +DEFINE_func_80132EC4() /* dedup: shared engine-core @0x80132EC4 (src/shared) */ INCLUDE_ASM("asm/ov_SC01_077/nonmatchings/ov_SC01_077", func_80132EF4); @@ -1214,13 +1199,7 @@ INCLUDE_ASM("asm/ov_SC01_077/nonmatchings/ov_SC01_077", func_80138B88); INCLUDE_ASM("asm/ov_SC01_077/nonmatchings/ov_SC01_077", func_80138BE0); -void func_80138C30(void *a0) { - if (*(s16 *)((s32)a0 + 4) == 4) { - *(s16 *)((s32)a0 + 4) = 2; - } else { - *(u32 *)((s32)a0 + 8) |= 0x800; - } -} +DEFINE_func_80138C30() /* dedup: shared engine-core @0x80138C30 (src/shared) */ INCLUDE_ASM("asm/ov_SC01_077/nonmatchings/ov_SC01_077", func_80138C60); diff --git a/src/ov_SC02_000/ov_SC02_000.c b/src/ov_SC02_000/ov_SC02_000.c index 1328c72c8..ed80f972e 100644 --- a/src/ov_SC02_000/ov_SC02_000.c +++ b/src/ov_SC02_000/ov_SC02_000.c @@ -1,4 +1,5 @@ #include "common.h" +#include "../shared/engine_core.h" INCLUDE_ASM("asm/ov_SC02_000/nonmatchings/ov_SC02_000", func_80128158); @@ -72,7 +73,7 @@ INCLUDE_ASM("asm/ov_SC02_000/nonmatchings/ov_SC02_000", func_80128D60); INCLUDE_ASM("asm/ov_SC02_000/nonmatchings/ov_SC02_000", func_80128DB4); -INCLUDE_ASM("asm/ov_SC02_000/nonmatchings/ov_SC02_000", func_80128EA8); +DEFINE_func_80128EA8() /* dedup: shared engine-core @0x80128EA8 (src/shared) */ INCLUDE_ASM("asm/ov_SC02_000/nonmatchings/ov_SC02_000", func_80128ED8); @@ -148,7 +149,7 @@ INCLUDE_ASM("asm/ov_SC02_000/nonmatchings/ov_SC02_000", func_8012A464); INCLUDE_ASM("asm/ov_SC02_000/nonmatchings/ov_SC02_000", func_8012A4BC); -INCLUDE_ASM("asm/ov_SC02_000/nonmatchings/ov_SC02_000", func_8012A568); +DEFINE_func_8012A568() /* dedup: shared engine-core @0x8012A568 (src/shared) */ INCLUDE_ASM("asm/ov_SC02_000/nonmatchings/ov_SC02_000", func_8012A598); @@ -607,7 +608,7 @@ INCLUDE_ASM("asm/ov_SC02_000/nonmatchings/ov_SC02_000", func_80132DC4); INCLUDE_ASM("asm/ov_SC02_000/nonmatchings/ov_SC02_000", func_80132E6C); -INCLUDE_ASM("asm/ov_SC02_000/nonmatchings/ov_SC02_000", func_80132EC4); +DEFINE_func_80132EC4() /* dedup: shared engine-core @0x80132EC4 (src/shared) */ INCLUDE_ASM("asm/ov_SC02_000/nonmatchings/ov_SC02_000", func_80132EF4); @@ -760,7 +761,7 @@ INCLUDE_ASM("asm/ov_SC02_000/nonmatchings/ov_SC02_000", func_80138B88); INCLUDE_ASM("asm/ov_SC02_000/nonmatchings/ov_SC02_000", func_80138BE0); -INCLUDE_ASM("asm/ov_SC02_000/nonmatchings/ov_SC02_000", func_80138C30); +DEFINE_func_80138C30() /* dedup: shared engine-core @0x80138C30 (src/shared) */ INCLUDE_ASM("asm/ov_SC02_000/nonmatchings/ov_SC02_000", func_80138C60); diff --git a/src/ov_SC02_003/ov_SC02_003.c b/src/ov_SC02_003/ov_SC02_003.c index 601601e01..e431f07d8 100644 --- a/src/ov_SC02_003/ov_SC02_003.c +++ b/src/ov_SC02_003/ov_SC02_003.c @@ -1,4 +1,5 @@ #include "common.h" +#include "../shared/engine_core.h" INCLUDE_ASM("asm/ov_SC02_003/nonmatchings/ov_SC02_003", func_80128158); @@ -72,7 +73,7 @@ INCLUDE_ASM("asm/ov_SC02_003/nonmatchings/ov_SC02_003", func_80128D60); INCLUDE_ASM("asm/ov_SC02_003/nonmatchings/ov_SC02_003", func_80128DB4); -INCLUDE_ASM("asm/ov_SC02_003/nonmatchings/ov_SC02_003", func_80128EA8); +DEFINE_func_80128EA8() /* dedup: shared engine-core @0x80128EA8 (src/shared) */ INCLUDE_ASM("asm/ov_SC02_003/nonmatchings/ov_SC02_003", func_80128ED8); @@ -148,7 +149,7 @@ INCLUDE_ASM("asm/ov_SC02_003/nonmatchings/ov_SC02_003", func_8012A464); INCLUDE_ASM("asm/ov_SC02_003/nonmatchings/ov_SC02_003", func_8012A4BC); -INCLUDE_ASM("asm/ov_SC02_003/nonmatchings/ov_SC02_003", func_8012A568); +DEFINE_func_8012A568() /* dedup: shared engine-core @0x8012A568 (src/shared) */ INCLUDE_ASM("asm/ov_SC02_003/nonmatchings/ov_SC02_003", func_8012A598); @@ -607,7 +608,7 @@ INCLUDE_ASM("asm/ov_SC02_003/nonmatchings/ov_SC02_003", func_80132DC4); INCLUDE_ASM("asm/ov_SC02_003/nonmatchings/ov_SC02_003", func_80132E6C); -INCLUDE_ASM("asm/ov_SC02_003/nonmatchings/ov_SC02_003", func_80132EC4); +DEFINE_func_80132EC4() /* dedup: shared engine-core @0x80132EC4 (src/shared) */ INCLUDE_ASM("asm/ov_SC02_003/nonmatchings/ov_SC02_003", func_80132EF4); @@ -760,7 +761,7 @@ INCLUDE_ASM("asm/ov_SC02_003/nonmatchings/ov_SC02_003", func_80138B88); INCLUDE_ASM("asm/ov_SC02_003/nonmatchings/ov_SC02_003", func_80138BE0); -INCLUDE_ASM("asm/ov_SC02_003/nonmatchings/ov_SC02_003", func_80138C30); +DEFINE_func_80138C30() /* dedup: shared engine-core @0x80138C30 (src/shared) */ INCLUDE_ASM("asm/ov_SC02_003/nonmatchings/ov_SC02_003", func_80138C60); diff --git a/src/ov_SC03_001/ov_SC03_001.c b/src/ov_SC03_001/ov_SC03_001.c index b7fb1fbe7..8a3f41b71 100644 --- a/src/ov_SC03_001/ov_SC03_001.c +++ b/src/ov_SC03_001/ov_SC03_001.c @@ -1,4 +1,5 @@ #include "common.h" +#include "../shared/engine_core.h" INCLUDE_ASM("asm/ov_SC03_001/nonmatchings/ov_SC03_001", func_80128158); @@ -72,7 +73,7 @@ INCLUDE_ASM("asm/ov_SC03_001/nonmatchings/ov_SC03_001", func_80128D60); INCLUDE_ASM("asm/ov_SC03_001/nonmatchings/ov_SC03_001", func_80128DB4); -INCLUDE_ASM("asm/ov_SC03_001/nonmatchings/ov_SC03_001", func_80128EA8); +DEFINE_func_80128EA8() /* dedup: shared engine-core @0x80128EA8 (src/shared) */ INCLUDE_ASM("asm/ov_SC03_001/nonmatchings/ov_SC03_001", func_80128ED8); @@ -148,7 +149,7 @@ INCLUDE_ASM("asm/ov_SC03_001/nonmatchings/ov_SC03_001", func_8012A464); INCLUDE_ASM("asm/ov_SC03_001/nonmatchings/ov_SC03_001", func_8012A4BC); -INCLUDE_ASM("asm/ov_SC03_001/nonmatchings/ov_SC03_001", func_8012A568); +DEFINE_func_8012A568() /* dedup: shared engine-core @0x8012A568 (src/shared) */ INCLUDE_ASM("asm/ov_SC03_001/nonmatchings/ov_SC03_001", func_8012A598); @@ -607,7 +608,7 @@ INCLUDE_ASM("asm/ov_SC03_001/nonmatchings/ov_SC03_001", func_80132DC4); INCLUDE_ASM("asm/ov_SC03_001/nonmatchings/ov_SC03_001", func_80132E6C); -INCLUDE_ASM("asm/ov_SC03_001/nonmatchings/ov_SC03_001", func_80132EC4); +DEFINE_func_80132EC4() /* dedup: shared engine-core @0x80132EC4 (src/shared) */ INCLUDE_ASM("asm/ov_SC03_001/nonmatchings/ov_SC03_001", func_80132EF4); @@ -760,7 +761,7 @@ INCLUDE_ASM("asm/ov_SC03_001/nonmatchings/ov_SC03_001", func_80138B88); INCLUDE_ASM("asm/ov_SC03_001/nonmatchings/ov_SC03_001", func_80138BE0); -INCLUDE_ASM("asm/ov_SC03_001/nonmatchings/ov_SC03_001", func_80138C30); +DEFINE_func_80138C30() /* dedup: shared engine-core @0x80138C30 (src/shared) */ INCLUDE_ASM("asm/ov_SC03_001/nonmatchings/ov_SC03_001", func_80138C60); diff --git a/src/ov_SC03_002/ov_SC03_002.c b/src/ov_SC03_002/ov_SC03_002.c index ef75f0d22..5f5a273d3 100644 --- a/src/ov_SC03_002/ov_SC03_002.c +++ b/src/ov_SC03_002/ov_SC03_002.c @@ -1,4 +1,5 @@ #include "common.h" +#include "../shared/engine_core.h" INCLUDE_ASM("asm/ov_SC03_002/nonmatchings/ov_SC03_002", func_80128158); @@ -72,7 +73,7 @@ INCLUDE_ASM("asm/ov_SC03_002/nonmatchings/ov_SC03_002", func_80128D60); INCLUDE_ASM("asm/ov_SC03_002/nonmatchings/ov_SC03_002", func_80128DB4); -INCLUDE_ASM("asm/ov_SC03_002/nonmatchings/ov_SC03_002", func_80128EA8); +DEFINE_func_80128EA8() /* dedup: shared engine-core @0x80128EA8 (src/shared) */ INCLUDE_ASM("asm/ov_SC03_002/nonmatchings/ov_SC03_002", func_80128ED8); @@ -148,7 +149,7 @@ INCLUDE_ASM("asm/ov_SC03_002/nonmatchings/ov_SC03_002", func_8012A464); INCLUDE_ASM("asm/ov_SC03_002/nonmatchings/ov_SC03_002", func_8012A4BC); -INCLUDE_ASM("asm/ov_SC03_002/nonmatchings/ov_SC03_002", func_8012A568); +DEFINE_func_8012A568() /* dedup: shared engine-core @0x8012A568 (src/shared) */ INCLUDE_ASM("asm/ov_SC03_002/nonmatchings/ov_SC03_002", func_8012A598); @@ -607,7 +608,7 @@ INCLUDE_ASM("asm/ov_SC03_002/nonmatchings/ov_SC03_002", func_80132DC4); INCLUDE_ASM("asm/ov_SC03_002/nonmatchings/ov_SC03_002", func_80132E6C); -INCLUDE_ASM("asm/ov_SC03_002/nonmatchings/ov_SC03_002", func_80132EC4); +DEFINE_func_80132EC4() /* dedup: shared engine-core @0x80132EC4 (src/shared) */ INCLUDE_ASM("asm/ov_SC03_002/nonmatchings/ov_SC03_002", func_80132EF4); @@ -760,7 +761,7 @@ INCLUDE_ASM("asm/ov_SC03_002/nonmatchings/ov_SC03_002", func_80138B88); INCLUDE_ASM("asm/ov_SC03_002/nonmatchings/ov_SC03_002", func_80138BE0); -INCLUDE_ASM("asm/ov_SC03_002/nonmatchings/ov_SC03_002", func_80138C30); +DEFINE_func_80138C30() /* dedup: shared engine-core @0x80138C30 (src/shared) */ INCLUDE_ASM("asm/ov_SC03_002/nonmatchings/ov_SC03_002", func_80138C60); diff --git a/src/ov_SC03_006/ov_SC03_006.c b/src/ov_SC03_006/ov_SC03_006.c index 900c67739..00780fa58 100644 --- a/src/ov_SC03_006/ov_SC03_006.c +++ b/src/ov_SC03_006/ov_SC03_006.c @@ -1,4 +1,5 @@ #include "common.h" +#include "../shared/engine_core.h" INCLUDE_ASM("asm/ov_SC03_006/nonmatchings/ov_SC03_006", func_80128158); @@ -72,7 +73,7 @@ INCLUDE_ASM("asm/ov_SC03_006/nonmatchings/ov_SC03_006", func_80128D60); INCLUDE_ASM("asm/ov_SC03_006/nonmatchings/ov_SC03_006", func_80128DB4); -INCLUDE_ASM("asm/ov_SC03_006/nonmatchings/ov_SC03_006", func_80128EA8); +DEFINE_func_80128EA8() /* dedup: shared engine-core @0x80128EA8 (src/shared) */ INCLUDE_ASM("asm/ov_SC03_006/nonmatchings/ov_SC03_006", func_80128ED8); @@ -148,7 +149,7 @@ INCLUDE_ASM("asm/ov_SC03_006/nonmatchings/ov_SC03_006", func_8012A464); INCLUDE_ASM("asm/ov_SC03_006/nonmatchings/ov_SC03_006", func_8012A4BC); -INCLUDE_ASM("asm/ov_SC03_006/nonmatchings/ov_SC03_006", func_8012A568); +DEFINE_func_8012A568() /* dedup: shared engine-core @0x8012A568 (src/shared) */ INCLUDE_ASM("asm/ov_SC03_006/nonmatchings/ov_SC03_006", func_8012A598); @@ -607,7 +608,7 @@ INCLUDE_ASM("asm/ov_SC03_006/nonmatchings/ov_SC03_006", func_80132DC4); INCLUDE_ASM("asm/ov_SC03_006/nonmatchings/ov_SC03_006", func_80132E6C); -INCLUDE_ASM("asm/ov_SC03_006/nonmatchings/ov_SC03_006", func_80132EC4); +DEFINE_func_80132EC4() /* dedup: shared engine-core @0x80132EC4 (src/shared) */ INCLUDE_ASM("asm/ov_SC03_006/nonmatchings/ov_SC03_006", func_80132EF4); @@ -760,7 +761,7 @@ INCLUDE_ASM("asm/ov_SC03_006/nonmatchings/ov_SC03_006", func_80138B88); INCLUDE_ASM("asm/ov_SC03_006/nonmatchings/ov_SC03_006", func_80138BE0); -INCLUDE_ASM("asm/ov_SC03_006/nonmatchings/ov_SC03_006", func_80138C30); +DEFINE_func_80138C30() /* dedup: shared engine-core @0x80138C30 (src/shared) */ INCLUDE_ASM("asm/ov_SC03_006/nonmatchings/ov_SC03_006", func_80138C60); diff --git a/src/ov_SC04_000/ov_SC04_000.c b/src/ov_SC04_000/ov_SC04_000.c index ba108d0a7..a85470b7f 100644 --- a/src/ov_SC04_000/ov_SC04_000.c +++ b/src/ov_SC04_000/ov_SC04_000.c @@ -1,4 +1,5 @@ #include "common.h" +#include "../shared/engine_core.h" INCLUDE_ASM("asm/ov_SC04_000/nonmatchings/ov_SC04_000", func_80128158); @@ -72,7 +73,7 @@ INCLUDE_ASM("asm/ov_SC04_000/nonmatchings/ov_SC04_000", func_80128D60); INCLUDE_ASM("asm/ov_SC04_000/nonmatchings/ov_SC04_000", func_80128DB4); -INCLUDE_ASM("asm/ov_SC04_000/nonmatchings/ov_SC04_000", func_80128EA8); +DEFINE_func_80128EA8() /* dedup: shared engine-core @0x80128EA8 (src/shared) */ INCLUDE_ASM("asm/ov_SC04_000/nonmatchings/ov_SC04_000", func_80128ED8); @@ -148,7 +149,7 @@ INCLUDE_ASM("asm/ov_SC04_000/nonmatchings/ov_SC04_000", func_8012A464); INCLUDE_ASM("asm/ov_SC04_000/nonmatchings/ov_SC04_000", func_8012A4BC); -INCLUDE_ASM("asm/ov_SC04_000/nonmatchings/ov_SC04_000", func_8012A568); +DEFINE_func_8012A568() /* dedup: shared engine-core @0x8012A568 (src/shared) */ INCLUDE_ASM("asm/ov_SC04_000/nonmatchings/ov_SC04_000", func_8012A598); @@ -607,7 +608,7 @@ INCLUDE_ASM("asm/ov_SC04_000/nonmatchings/ov_SC04_000", func_80132DC4); INCLUDE_ASM("asm/ov_SC04_000/nonmatchings/ov_SC04_000", func_80132E6C); -INCLUDE_ASM("asm/ov_SC04_000/nonmatchings/ov_SC04_000", func_80132EC4); +DEFINE_func_80132EC4() /* dedup: shared engine-core @0x80132EC4 (src/shared) */ INCLUDE_ASM("asm/ov_SC04_000/nonmatchings/ov_SC04_000", func_80132EF4); @@ -760,7 +761,7 @@ INCLUDE_ASM("asm/ov_SC04_000/nonmatchings/ov_SC04_000", func_80138B88); INCLUDE_ASM("asm/ov_SC04_000/nonmatchings/ov_SC04_000", func_80138BE0); -INCLUDE_ASM("asm/ov_SC04_000/nonmatchings/ov_SC04_000", func_80138C30); +DEFINE_func_80138C30() /* dedup: shared engine-core @0x80138C30 (src/shared) */ INCLUDE_ASM("asm/ov_SC04_000/nonmatchings/ov_SC04_000", func_80138C60); diff --git a/src/ov_SC04_018/ov_SC04_018.c b/src/ov_SC04_018/ov_SC04_018.c index 0cf36ad05..6796deeec 100644 --- a/src/ov_SC04_018/ov_SC04_018.c +++ b/src/ov_SC04_018/ov_SC04_018.c @@ -1,4 +1,5 @@ #include "common.h" +#include "../shared/engine_core.h" INCLUDE_ASM("asm/ov_SC04_018/nonmatchings/ov_SC04_018", func_80128158); @@ -72,7 +73,7 @@ INCLUDE_ASM("asm/ov_SC04_018/nonmatchings/ov_SC04_018", func_80128D60); INCLUDE_ASM("asm/ov_SC04_018/nonmatchings/ov_SC04_018", func_80128DB4); -INCLUDE_ASM("asm/ov_SC04_018/nonmatchings/ov_SC04_018", func_80128EA8); +DEFINE_func_80128EA8() /* dedup: shared engine-core @0x80128EA8 (src/shared) */ INCLUDE_ASM("asm/ov_SC04_018/nonmatchings/ov_SC04_018", func_80128ED8); @@ -148,7 +149,7 @@ INCLUDE_ASM("asm/ov_SC04_018/nonmatchings/ov_SC04_018", func_8012A464); INCLUDE_ASM("asm/ov_SC04_018/nonmatchings/ov_SC04_018", func_8012A4BC); -INCLUDE_ASM("asm/ov_SC04_018/nonmatchings/ov_SC04_018", func_8012A568); +DEFINE_func_8012A568() /* dedup: shared engine-core @0x8012A568 (src/shared) */ INCLUDE_ASM("asm/ov_SC04_018/nonmatchings/ov_SC04_018", func_8012A598); @@ -607,7 +608,7 @@ INCLUDE_ASM("asm/ov_SC04_018/nonmatchings/ov_SC04_018", func_80132DC4); INCLUDE_ASM("asm/ov_SC04_018/nonmatchings/ov_SC04_018", func_80132E6C); -INCLUDE_ASM("asm/ov_SC04_018/nonmatchings/ov_SC04_018", func_80132EC4); +DEFINE_func_80132EC4() /* dedup: shared engine-core @0x80132EC4 (src/shared) */ INCLUDE_ASM("asm/ov_SC04_018/nonmatchings/ov_SC04_018", func_80132EF4); @@ -760,7 +761,7 @@ INCLUDE_ASM("asm/ov_SC04_018/nonmatchings/ov_SC04_018", func_80138B88); INCLUDE_ASM("asm/ov_SC04_018/nonmatchings/ov_SC04_018", func_80138BE0); -INCLUDE_ASM("asm/ov_SC04_018/nonmatchings/ov_SC04_018", func_80138C30); +DEFINE_func_80138C30() /* dedup: shared engine-core @0x80138C30 (src/shared) */ INCLUDE_ASM("asm/ov_SC04_018/nonmatchings/ov_SC04_018", func_80138C60); diff --git a/src/ov_SC04_019/ov_SC04_019.c b/src/ov_SC04_019/ov_SC04_019.c index 6aa1cf228..833f21564 100644 --- a/src/ov_SC04_019/ov_SC04_019.c +++ b/src/ov_SC04_019/ov_SC04_019.c @@ -1,4 +1,5 @@ #include "common.h" +#include "../shared/engine_core.h" INCLUDE_ASM("asm/ov_SC04_019/nonmatchings/ov_SC04_019", func_80128158); @@ -72,7 +73,7 @@ INCLUDE_ASM("asm/ov_SC04_019/nonmatchings/ov_SC04_019", func_80128D60); INCLUDE_ASM("asm/ov_SC04_019/nonmatchings/ov_SC04_019", func_80128DB4); -INCLUDE_ASM("asm/ov_SC04_019/nonmatchings/ov_SC04_019", func_80128EA8); +DEFINE_func_80128EA8() /* dedup: shared engine-core @0x80128EA8 (src/shared) */ INCLUDE_ASM("asm/ov_SC04_019/nonmatchings/ov_SC04_019", func_80128ED8); @@ -148,7 +149,7 @@ INCLUDE_ASM("asm/ov_SC04_019/nonmatchings/ov_SC04_019", func_8012A464); INCLUDE_ASM("asm/ov_SC04_019/nonmatchings/ov_SC04_019", func_8012A4BC); -INCLUDE_ASM("asm/ov_SC04_019/nonmatchings/ov_SC04_019", func_8012A568); +DEFINE_func_8012A568() /* dedup: shared engine-core @0x8012A568 (src/shared) */ INCLUDE_ASM("asm/ov_SC04_019/nonmatchings/ov_SC04_019", func_8012A598); @@ -607,7 +608,7 @@ INCLUDE_ASM("asm/ov_SC04_019/nonmatchings/ov_SC04_019", func_80132DC4); INCLUDE_ASM("asm/ov_SC04_019/nonmatchings/ov_SC04_019", func_80132E6C); -INCLUDE_ASM("asm/ov_SC04_019/nonmatchings/ov_SC04_019", func_80132EC4); +DEFINE_func_80132EC4() /* dedup: shared engine-core @0x80132EC4 (src/shared) */ INCLUDE_ASM("asm/ov_SC04_019/nonmatchings/ov_SC04_019", func_80132EF4); @@ -760,7 +761,7 @@ INCLUDE_ASM("asm/ov_SC04_019/nonmatchings/ov_SC04_019", func_80138B88); INCLUDE_ASM("asm/ov_SC04_019/nonmatchings/ov_SC04_019", func_80138BE0); -INCLUDE_ASM("asm/ov_SC04_019/nonmatchings/ov_SC04_019", func_80138C30); +DEFINE_func_80138C30() /* dedup: shared engine-core @0x80138C30 (src/shared) */ INCLUDE_ASM("asm/ov_SC04_019/nonmatchings/ov_SC04_019", func_80138C60); diff --git a/src/ov_SC05_000/ov_SC05_000.c b/src/ov_SC05_000/ov_SC05_000.c index ee86458f7..8a6a436d4 100644 --- a/src/ov_SC05_000/ov_SC05_000.c +++ b/src/ov_SC05_000/ov_SC05_000.c @@ -1,4 +1,5 @@ #include "common.h" +#include "../shared/engine_core.h" INCLUDE_ASM("asm/ov_SC05_000/nonmatchings/ov_SC05_000", func_80128158); @@ -72,7 +73,7 @@ INCLUDE_ASM("asm/ov_SC05_000/nonmatchings/ov_SC05_000", func_80128D60); INCLUDE_ASM("asm/ov_SC05_000/nonmatchings/ov_SC05_000", func_80128DB4); -INCLUDE_ASM("asm/ov_SC05_000/nonmatchings/ov_SC05_000", func_80128EA8); +DEFINE_func_80128EA8() /* dedup: shared engine-core @0x80128EA8 (src/shared) */ INCLUDE_ASM("asm/ov_SC05_000/nonmatchings/ov_SC05_000", func_80128ED8); @@ -148,7 +149,7 @@ INCLUDE_ASM("asm/ov_SC05_000/nonmatchings/ov_SC05_000", func_8012A464); INCLUDE_ASM("asm/ov_SC05_000/nonmatchings/ov_SC05_000", func_8012A4BC); -INCLUDE_ASM("asm/ov_SC05_000/nonmatchings/ov_SC05_000", func_8012A568); +DEFINE_func_8012A568() /* dedup: shared engine-core @0x8012A568 (src/shared) */ INCLUDE_ASM("asm/ov_SC05_000/nonmatchings/ov_SC05_000", func_8012A598); @@ -607,7 +608,7 @@ INCLUDE_ASM("asm/ov_SC05_000/nonmatchings/ov_SC05_000", func_80132DC4); INCLUDE_ASM("asm/ov_SC05_000/nonmatchings/ov_SC05_000", func_80132E6C); -INCLUDE_ASM("asm/ov_SC05_000/nonmatchings/ov_SC05_000", func_80132EC4); +DEFINE_func_80132EC4() /* dedup: shared engine-core @0x80132EC4 (src/shared) */ INCLUDE_ASM("asm/ov_SC05_000/nonmatchings/ov_SC05_000", func_80132EF4); @@ -760,7 +761,7 @@ INCLUDE_ASM("asm/ov_SC05_000/nonmatchings/ov_SC05_000", func_80138B88); INCLUDE_ASM("asm/ov_SC05_000/nonmatchings/ov_SC05_000", func_80138BE0); -INCLUDE_ASM("asm/ov_SC05_000/nonmatchings/ov_SC05_000", func_80138C30); +DEFINE_func_80138C30() /* dedup: shared engine-core @0x80138C30 (src/shared) */ INCLUDE_ASM("asm/ov_SC05_000/nonmatchings/ov_SC05_000", func_80138C60); diff --git a/src/ov_SC06_000/ov_SC06_000.c b/src/ov_SC06_000/ov_SC06_000.c index 384fcd1e3..df1fffc42 100644 --- a/src/ov_SC06_000/ov_SC06_000.c +++ b/src/ov_SC06_000/ov_SC06_000.c @@ -1,4 +1,5 @@ #include "common.h" +#include "../shared/engine_core.h" INCLUDE_ASM("asm/ov_SC06_000/nonmatchings/ov_SC06_000", func_80128158); @@ -72,7 +73,7 @@ INCLUDE_ASM("asm/ov_SC06_000/nonmatchings/ov_SC06_000", func_80128D60); INCLUDE_ASM("asm/ov_SC06_000/nonmatchings/ov_SC06_000", func_80128DB4); -INCLUDE_ASM("asm/ov_SC06_000/nonmatchings/ov_SC06_000", func_80128EA8); +DEFINE_func_80128EA8() /* dedup: shared engine-core @0x80128EA8 (src/shared) */ INCLUDE_ASM("asm/ov_SC06_000/nonmatchings/ov_SC06_000", func_80128ED8); @@ -148,7 +149,7 @@ INCLUDE_ASM("asm/ov_SC06_000/nonmatchings/ov_SC06_000", func_8012A464); INCLUDE_ASM("asm/ov_SC06_000/nonmatchings/ov_SC06_000", func_8012A4BC); -INCLUDE_ASM("asm/ov_SC06_000/nonmatchings/ov_SC06_000", func_8012A568); +DEFINE_func_8012A568() /* dedup: shared engine-core @0x8012A568 (src/shared) */ INCLUDE_ASM("asm/ov_SC06_000/nonmatchings/ov_SC06_000", func_8012A598); @@ -607,7 +608,7 @@ INCLUDE_ASM("asm/ov_SC06_000/nonmatchings/ov_SC06_000", func_80132DC4); INCLUDE_ASM("asm/ov_SC06_000/nonmatchings/ov_SC06_000", func_80132E6C); -INCLUDE_ASM("asm/ov_SC06_000/nonmatchings/ov_SC06_000", func_80132EC4); +DEFINE_func_80132EC4() /* dedup: shared engine-core @0x80132EC4 (src/shared) */ INCLUDE_ASM("asm/ov_SC06_000/nonmatchings/ov_SC06_000", func_80132EF4); @@ -760,7 +761,7 @@ INCLUDE_ASM("asm/ov_SC06_000/nonmatchings/ov_SC06_000", func_80138B88); INCLUDE_ASM("asm/ov_SC06_000/nonmatchings/ov_SC06_000", func_80138BE0); -INCLUDE_ASM("asm/ov_SC06_000/nonmatchings/ov_SC06_000", func_80138C30); +DEFINE_func_80138C30() /* dedup: shared engine-core @0x80138C30 (src/shared) */ INCLUDE_ASM("asm/ov_SC06_000/nonmatchings/ov_SC06_000", func_80138C60); diff --git a/src/ov_SC07_000/ov_SC07_000.c b/src/ov_SC07_000/ov_SC07_000.c index 4536b95b5..7302bc78c 100644 --- a/src/ov_SC07_000/ov_SC07_000.c +++ b/src/ov_SC07_000/ov_SC07_000.c @@ -1,4 +1,5 @@ #include "common.h" +#include "../shared/engine_core.h" INCLUDE_ASM("asm/ov_SC07_000/nonmatchings/ov_SC07_000", func_80128158); @@ -72,7 +73,7 @@ INCLUDE_ASM("asm/ov_SC07_000/nonmatchings/ov_SC07_000", func_80128D60); INCLUDE_ASM("asm/ov_SC07_000/nonmatchings/ov_SC07_000", func_80128DB4); -INCLUDE_ASM("asm/ov_SC07_000/nonmatchings/ov_SC07_000", func_80128EA8); +DEFINE_func_80128EA8() /* dedup: shared engine-core @0x80128EA8 (src/shared) */ INCLUDE_ASM("asm/ov_SC07_000/nonmatchings/ov_SC07_000", func_80128ED8); @@ -148,7 +149,7 @@ INCLUDE_ASM("asm/ov_SC07_000/nonmatchings/ov_SC07_000", func_8012A464); INCLUDE_ASM("asm/ov_SC07_000/nonmatchings/ov_SC07_000", func_8012A4BC); -INCLUDE_ASM("asm/ov_SC07_000/nonmatchings/ov_SC07_000", func_8012A568); +DEFINE_func_8012A568() /* dedup: shared engine-core @0x8012A568 (src/shared) */ INCLUDE_ASM("asm/ov_SC07_000/nonmatchings/ov_SC07_000", func_8012A598); @@ -607,7 +608,7 @@ INCLUDE_ASM("asm/ov_SC07_000/nonmatchings/ov_SC07_000", func_80132DC4); INCLUDE_ASM("asm/ov_SC07_000/nonmatchings/ov_SC07_000", func_80132E6C); -INCLUDE_ASM("asm/ov_SC07_000/nonmatchings/ov_SC07_000", func_80132EC4); +DEFINE_func_80132EC4() /* dedup: shared engine-core @0x80132EC4 (src/shared) */ INCLUDE_ASM("asm/ov_SC07_000/nonmatchings/ov_SC07_000", func_80132EF4); @@ -760,7 +761,7 @@ INCLUDE_ASM("asm/ov_SC07_000/nonmatchings/ov_SC07_000", func_80138B88); INCLUDE_ASM("asm/ov_SC07_000/nonmatchings/ov_SC07_000", func_80138BE0); -INCLUDE_ASM("asm/ov_SC07_000/nonmatchings/ov_SC07_000", func_80138C30); +DEFINE_func_80138C30() /* dedup: shared engine-core @0x80138C30 (src/shared) */ INCLUDE_ASM("asm/ov_SC07_000/nonmatchings/ov_SC07_000", func_80138C60); diff --git a/src/shared/engine_core.h b/src/shared/engine_core.h new file mode 100644 index 000000000..1ef56936d --- /dev/null +++ b/src/shared/engine_core.h @@ -0,0 +1,42 @@ +/* src/shared/engine_core.h — Phase 15 shared engine-core bodies (cross-overlay dedup, §14). + * Each DEFINE_func_XXXX() expands to the WHOLE matched body once; instantiated in place at the + * func_XXXX site in every overlay that shares it (address order preserved). Registry + + * byte-honesty: config/dedup.us.yaml + tools/dedup_integrate.py. Tool-generated; do not hand-edit. */ +#ifndef SHARED_ENGINE_CORE_H +#define SHARED_ENGINE_CORE_H +#include "common.h" + +#define DEFINE_func_80128EA8() \ + void func_80128EA8(s32 a0, s32 a1, s32 a2) { \ + *(s32 *)(a1 + 0x0) = a2; /* sw a2, 0x0(a1) */ \ + *(s16 *)(a1 + 0x4) = 0; /* sh zero, 0x4(a1) */ \ + *(s16 *)(a1 + 0x6) = (*(s32 *)(a2 + 0x4)) & 0x3F; /* lw; andi 0x3F; sh 0x6(a1) */ \ + *(s16 *)(a0 + 0x28) = *(u16 *)(a2 + 0x0); /* lhu 0x0(a2); sh 0x28(a0) */ \ + *(s16 *)(a0 + 0x2A) = *(u16 *)(a2 + 0x2); /* lhu 0x2(a2); sh 0x2A(a0) */ \ + } + +#define DEFINE_func_8012A568() \ + extern void func_8012A598(void *a0); \ + void func_8012A568(void (*a0)(void)) { \ + func_8012A598(a0); \ + a0(); \ + } + +#define DEFINE_func_80132EC4() \ + void func_80132EC4(void *a0, s16 a1) { \ + while (*(s32 *)a0 != 0) { \ + *(s16 *)((s32)a0 + 4) = a1; \ + a0 = (void *)((s32)a0 + 8); \ + } \ + } + +#define DEFINE_func_80138C30() \ + void func_80138C30(void *a0) { \ + if (*(s16 *)((s32)a0 + 4) == 4) { \ + *(s16 *)((s32)a0 + 4) = 2; \ + } else { \ + *(u32 *)((s32)a0 + 8) |= 0x800; \ + } \ + } + +#endif diff --git a/tools/dedup_propagate.py b/tools/dedup_propagate.py new file mode 100644 index 000000000..468dde13a --- /dev/null +++ b/tools/dedup_propagate.py @@ -0,0 +1,352 @@ +#!/usr/bin/env python3 +"""dedup_propagate.py — match once, share many across the overlay fleet (Phase 15, cookbook §14). + +The overlay fleet is position-locked at vram 0x80128158, so a shared engine function has the +SAME vaddr (hence the same `func_` symbol) and BYTE-IDENTICAL body in every overlay that +contains it. This tool takes a function already matched in ONE overlay and propagates that single +matched C body to every onboarded overlay whose signature shows the same `h_exact` at that addr: + + 1. extract the matched body from the source overlay's .c (externs + the function def) + 2. author it ONCE as a `DEFINE_func_()` macro in src/shared/
.h (idempotent) + 3. at each member overlay, replace that function's INCLUDE_ASM stub (or, in the source overlay, + its inline def) in place with `DEFINE_func_()` — address order preserved + 4. BYTE-GATE every touched overlay (`make build BINARY=` == its check.sha); on ANY miss, + restore every file from an in-memory snapshot and abort (fail-closed; nothing wrong can land) + 5. register the group in config/dedup.us.yaml (the byte-honesty registry, validated by + tools/dedup_integrate.py --check) + +Everything is keyed by the integer address: the sig uses lowercase hex ("0x80144b9c"), splat emits +the uppercase symbol ("func_80144B9C") — never compare the strings, always the int. + +Lead with h_exact (guaranteed byte-identity). The per-overlay `make check` is the sole arbiter +(G3/P9): a wrong propagation cannot pass it. + +Usage: + tools/dedup_propagate.py --addr 0x8013xxxx[,0x...] [--source-overlay ov_SC01_077] + tools/dedup_propagate.py --auto-from ov_SC01_077 [--min-reach 2] [--limit N] + tools/dedup_propagate.py ... --check-only # dry run: print the plan, touch nothing +Options: + --header src/shared/engine_core.h target cluster header (created/appended) + --tier h_exact|h_norm default h_exact (h_norm requires the byte-gate to pass on ALL) + --binaries a,b,c restrict members to these onboarded overlays (default: all that share) + --no-gate skip the per-overlay build gate (CI / batch re-gate later) +""" +import argparse, json, pathlib, re, subprocess, sys + +ROOT = pathlib.Path(__file__).resolve().parent.parent + + +# ---------------------------------------------------------------- fleet / sig helpers +def onboarded_overlays(): + mk = (ROOT / "config/overlays.mk").read_text() + m = re.search(r"^OVERLAY_BINARIES\s*:=\s*(.*)$", mk, re.M) + return m.group(1).split() if m else [] + + +def sig_path(ov): + return ROOT / f".run/sig.{ov}.jsonl" + + +def load_sig(ov): + """addr(int) -> {h_exact, h_norm, nins, ...}; {} if absent.""" + p = sig_path(ov) + if not p.exists(): + return {} + out = {} + for ln in p.read_text().splitlines(): + ln = ln.strip() + if ln: + r = json.loads(ln) + out[int(r["addr"], 16)] = r + return out + + +def sym(addr): + return f"func_{addr:08X}" # splat convention: uppercase 8-hex + + +def c_path(ov): + return ROOT / f"src/{ov}/{ov}.c" + + +def stub_line(ov, addr): + return f'INCLUDE_ASM("asm/{ov}/nonmatchings/{ov}", {sym(addr)});' + + +# ---------------------------------------------------------------- body extraction +def find_site(text, ov, addr): + """Locate this function in ov's .c. Returns (kind, start, end, body_lines): + kind 'stub' -> the INCLUDE_ASM line (start==end, body_lines None) + kind 'def' -> an inline definition block (preceding contiguous externs .. closing brace) + kind 'macro' -> already a DEFINE_func_() instantiation (already propagated) + None -> not present / matched in some other form.""" + lines = text.splitlines() + s = sym(addr) + stub = stub_line(ov, addr) + for i, l in enumerate(lines): + if l.strip() == stub: + return ("stub", i, i, None) + for i, l in enumerate(lines): + if l.strip() == f"DEFINE_{s}()" or l.strip().startswith(f"DEFINE_{s}()"): + return ("macro", i, i, None) + # inline definition: " func_XXXX(...) {" at column 0, brace on the same line + defre = re.compile(rf"^[A-Za-z_][\w \*]*\b{s}\s*\([^;]*\)\s*\{{") + for i, l in enumerate(lines): + if defre.match(l): + # brace-match forward to the closing '}' + depth = 0 + end = None + for j in range(i, len(lines)): + depth += lines[j].count("{") - lines[j].count("}") + if depth <= 0: + end = j + break + if end is None: + return None + # collect contiguous preceding extern declarations (skip blank lines) + start = i + k = i - 1 + while k >= 0 and lines[k].strip() == "": + k -= 1 + while k >= 0 and re.match(r"^\s*extern\b.*;\s*$", lines[k]): + start = k + k -= 1 + while k >= 0 and lines[k].strip() == "": + k -= 1 + body = [ln for ln in lines[start:end + 1] if ln.strip() != ""] + return ("def", start, end, body) + return None + + +def make_macro(addr, body_lines): + """Turn an extracted body (externs + def) into a `#define DEFINE_func_() \\`-continued macro. + Fails loud on constructs that don't survive line-continuation (// comments, trailing backslash).""" + s = sym(addr) + for ln in body_lines: + if "//" in ln: + raise SystemExit(f"[refuse] {s}: body has a // comment — not macro-safe (handle manually)") + if ln.rstrip().endswith("\\"): + raise SystemExit(f"[refuse] {s}: body line ends with backslash — not macro-safe") + out = [f"#define DEFINE_{s}() \\"] + for idx, ln in enumerate(body_lines): + cont = " \\" if idx < len(body_lines) - 1 else "" + out.append(" " + ln.rstrip() + cont) + return "\n".join(out) + "\n" + + +# ---------------------------------------------------------------- file edits (in memory) +INCLUDE_RE = re.compile(r'^#include\s+"\.\./shared/') + + +def ensure_include(text, header): + rel = f'#include "../shared/{pathlib.Path(header).name}"' + if rel in text: + return text + lines = text.splitlines(keepends=True) + for i, l in enumerate(lines): + if l.strip() == '#include "common.h"': + lines.insert(i + 1, rel + "\n") + return "".join(lines) + return rel + "\n" + text # fallback: prepend + + +def replace_site(text, ov, addr, kind, start, end): + lines = text.splitlines(keepends=True) + repl = f"DEFINE_{sym(addr)}() /* dedup: shared engine-core @0x{addr:08X} (src/shared) */\n" + lines[start:end + 1] = [repl] + return "".join(lines) + + +# ---------------------------------------------------------------- dedup.us.yaml registration +def append_groups(groups): + """Append new groups textually (preserve the file's header comments). Idempotent by id.""" + path = ROOT / "config/dedup.us.yaml" + text = path.read_text() + blocks = [] + for g in groups: + if re.search(rf"^\s*-\s*id:\s*{re.escape(g['id'])}\s*$", text, re.M): + continue # already present + mem = "\n".join( + f" - {{ binary: {m['binary']}, vram: 0x{m['vram']:08X}, name: {m['name']} }}" + for m in g["members"]) + blocks.append( + f" - id: {g['id']}\n" + f" tier: {g['tier']}\n" + f" hash: {g['hash']}\n" + f" source: {g['source']}\n" + f" func: DEFINE_{sym(g['addr'])}\n" + f" members:\n{mem}") + if not blocks: + return 0 + if not text.endswith("\n"): + text += "\n" + path.write_text(text + "\n".join(blocks) + "\n") + return len(blocks) + + +# ---------------------------------------------------------------- byte gate +def byte_gate(ov): + r = subprocess.run(["make", "build", f"BINARY={ov}"], cwd=ROOT, + capture_output=True, text=True) + return r.returncode == 0, r.stdout + r.stderr + + +# ---------------------------------------------------------------- main +def main(): + ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) + ap.add_argument("--addr", help="comma-separated target vaddrs (hex)") + ap.add_argument("--auto-from", help="propagate every matched+shared function of this overlay") + ap.add_argument("--source-overlay", help="overlay to extract the matched body from (default: auto)") + ap.add_argument("--min-reach", type=int, default=2, help="min #overlays sharing (auto mode; default 2)") + ap.add_argument("--limit", type=int, default=0, help="cap #functions (auto mode; 0 = no cap)") + ap.add_argument("--header", default="src/shared/engine_core.h") + ap.add_argument("--tier", default="h_exact", choices=("h_exact", "h_norm")) + ap.add_argument("--binaries", help="restrict members to these onboarded overlays (comma list)") + ap.add_argument("--check-only", action="store_true", help="dry run: print the plan, touch nothing") + ap.add_argument("--no-gate", action="store_true") + a = ap.parse_args() + + onb = onboarded_overlays() + restrict = set(a.binaries.split(",")) if a.binaries else None + pool = [ov for ov in onb if (restrict is None or ov in restrict)] + sigs = {ov: load_sig(ov) for ov in pool} + + # ---- choose target addresses + source overlay per target + targets = [] # list of (addr, source_overlay) + if a.auto_from: + src = a.auto_from + ssig = load_sig(src) + ctext = c_path(src).read_text() + for addr in sorted(ssig): + site = find_site(ctext, src, addr) + if not site or site[0] != "def": # only functions matched (inline def) in the source + continue + h = ssig[addr].get(a.tier) + reach = sum(1 for ov in pool if sigs[ov].get(addr, {}).get(a.tier) == h) + if reach >= a.min_reach: + targets.append((addr, src)) + targets.sort(key=lambda t: ssig[t[0]]["nins"]) + if a.limit: + targets = targets[:a.limit] + elif a.addr: + for tok in a.addr.split(","): + addr = int(tok, 16) + src = a.source_overlay + if not src: # auto: an onboarded overlay where it is an inline def + for ov in pool: + site = find_site(c_path(ov).read_text(), ov, addr) + if site and site[0] == "def": + src = ov + break + if not src: + sys.exit(f"[error] 0x{addr:08X}: no source overlay has it matched — give --source-overlay") + targets.append((addr, src)) + else: + sys.exit("give --addr or --auto-from") + + # ---- build the per-target plan (members + body + hash) + plan = [] + for addr, src in targets: + ssig = load_sig(src) + if addr not in ssig: + print(f"[skip] 0x{addr:08X}: not signed in {src}"); continue + h = ssig[addr].get(a.tier) + site = find_site(c_path(src).read_text(), src, addr) + if not site or site[0] != "def": + print(f"[skip] 0x{addr:08X}: not an inline def in source {src}"); continue + body = site[3] + members = [ov for ov in pool if sigs[ov].get(addr, {}).get(a.tier) == h] + if len(members) < 2: + print(f"[skip] 0x{addr:08X}: reach {len(members)} < 2"); continue + plan.append(dict(addr=addr, src=src, hash=h, body=body, members=members)) + + if not plan: + sys.exit("[error] nothing to propagate") + + print(f"== plan: {len(plan)} function(s), tier={a.tier}, header={a.header} ==") + for p in plan: + print(f" 0x{p['addr']:08X} body={len(p['body'])}L members={len(p['members'])} " + f"[{','.join(m.replace('ov_','') for m in p['members'][:6])}{'…' if len(p['members'])>6 else ''}]") + if a.check_only: + print("(--check-only: no files touched)") + return + + # ---- apply (in-memory snapshot of every file we touch, for fail-closed restore) + header_path = ROOT / a.header + touched = {} # path -> original text (None if newly created) + + def edit(path, newtext): + if path not in touched: + touched[path] = path.read_text() if path.exists() else None + path.write_text(newtext) + + # author macros + htext = header_path.read_text() if header_path.exists() else \ + ("/* src/shared/engine_core.h — Phase 15 shared engine-core bodies (cross-overlay dedup, §14).\n" + " * Each DEFINE_func_XXXX() expands to the WHOLE matched body once; instantiated in place at the\n" + " * func_XXXX site in every overlay that shares it (address order preserved). Registry +\n" + " * byte-honesty: config/dedup.us.yaml + tools/dedup_integrate.py. Tool-generated; do not hand-edit. */\n" + "#ifndef SHARED_ENGINE_CORE_H\n#define SHARED_ENGINE_CORE_H\n#include \"common.h\"\n\n#endif\n") + for p in plan: + if f"DEFINE_{sym(p['addr'])}()" not in htext: + macro = make_macro(p["addr"], p["body"]) + htext = htext.replace("\n#endif\n", "\n" + macro + "\n#endif\n") + edit(header_path, htext) + + # edit each member overlay + for p in plan: + for ov in p["members"]: + cp = c_path(ov) + t = cp.read_text() # accumulated on-disk text (edit() preserves the original for restore) + t = ensure_include(t, a.header) + site = find_site(t, ov, p["addr"]) + if not site: + print(f"[warn] {ov}: 0x{p['addr']:08X} site not found — skipping in this overlay") + continue + if site[0] == "macro": + edit(cp, t) # already instantiated (idempotent) + continue + t = replace_site(t, ov, p["addr"], *site[:3]) + edit(cp, t) + + # ---- byte-gate every touched overlay + def restore(): + for path, orig in touched.items(): + if orig is None: + path.unlink(missing_ok=True) + else: + path.write_text(orig) + + # structural check the byte-gate CANNOT catch: a leftover INCLUDE_ASM stub is itself + # byte-identical, so `make check` would pass even if a function wasn't actually converted. + for p in plan: + for ov in p["members"]: + t = c_path(ov).read_text() + if f"DEFINE_{sym(p['addr'])}()" not in t: + restore(); sys.exit(f"[FAIL] {ov}: 0x{p['addr']:08X} not instantiated after edit — REVERTED") + if stub_line(ov, p["addr"]) in t: + restore(); sys.exit(f"[FAIL] {ov}: 0x{p['addr']:08X} stub still present — REVERTED") + + gate_ovs = sorted({ov for p in plan for ov in p["members"]}) + if not a.no_gate: + for ov in gate_ovs: + ok, log = byte_gate(ov) + if not ok: + tail = "\n".join(log.splitlines()[-12:]) + print(f"[FAIL] {ov} not byte-identical after propagation — REVERTING ALL.\n{tail}") + restore() + sys.exit(1) + print(f"[ OK ] {ov} byte-identical") + + # ---- register groups + groups = [dict(id=f"E_{sym(p['addr'])}", tier=a.tier, hash=p["hash"], + source=a.header, addr=p["addr"], + members=[dict(binary=ov, vram=p["addr"], name=sym(p["addr"])) for ov in p["members"]]) + for p in plan] + n = append_groups(groups) + print(f"== propagated {len(plan)} function(s) across {len(gate_ovs)} overlays; " + f"registered {n} new group(s) in config/dedup.us.yaml ==") + + +if __name__ == "__main__": + main()