feat(phase-15): T2 — dedup_propagate.py (match-once -> propagate-many) proven on wave

- tools/dedup_propagate.py: lift a matched body -> DEFINE_func_<ADDR>() macro in
  src/shared/engine_core.h -> instantiate in place at every onboarded overlay sharing
  that h_exact -> snapshot + per-overlay byte-gate (fail-closed revert) -> register in
  config/dedup.us.yaml. Keyed by addr-int (sig lowercase vs splat func_%08X).
- proof: 4 medium funcs propagated across all 16 onboarded overlays; clean check-all
  18/18 byte-identical; dedup_integrate 8 validated/0 failed; idempotent; negative test
  (corrupt shared body) -> make check FAILS then restores
- Makefile: header-dependency tracking (cpp -MMD -MP + -include ) so editing a
  shared header recompiles dependents — closes a stale-build/false-pass gap the negative
  test exposed; output-neutral (R22 clean 18/18)
- structural self-check: a leftover INCLUDE_ASM stub is byte-identical, so the byte-gate
  can't catch under-application -> assert DEFINE present + stub gone
- FINDING: 577 of ov_SC01_077's 785 matches are h_exact across all 134 overlays
  (2.19 MB collapsible, already matched) -> T5 = bulk-propagate these + harvest the rest
- cookbook §14 (the propagation runbook + gotchas), SETUP tool inventory
- ghidra/ churn NOT staged (R23); build/asm/.run gitignored
This commit is contained in:
Drew T
2026-06-16 19:24:08 -06:00
parent e5a2bd4083
commit 0c7619231d
23 changed files with 618 additions and 88 deletions
+8 -1
View File
@@ -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/<alias>/*.bin and references build/assets/<alias>/*.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
+88
View File
@@ -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 }
+1
View File
@@ -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_<ADDR>()` 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. |
+43
View File
@@ -769,3 +769,46 @@ Recursive `$(MAKE) BINARY=<b>` over `$(BINARIES)` (NOT `foreach` — the OBJS gl
one PASS/FAIL + per-binary `.run/check.<b>.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_<ADDR>` 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 <ov>` (or `--auto-from <ov>` 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_<ADDR>() \`-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_<ADDR>()` — address order preserved; `#include "../shared/engine_core.h"` added once after
`common.h`;
4. **byte-gate** each touched overlay (`make build BINARY=<ov>` == 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_<ADDR>()` 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.
+4 -1
View File
@@ -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.
+5 -4
View File
@@ -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);
+5 -4
View File
@@ -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);
+5 -4
View File
@@ -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);
+5 -4
View File
@@ -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);
+5 -26
View File
@@ -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);
+5 -4
View File
@@ -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);
+5 -4
View File
@@ -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);
+5 -4
View File
@@ -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);
+5 -4
View File
@@ -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);
+5 -4
View File
@@ -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);
+5 -4
View File
@@ -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);
+5 -4
View File
@@ -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);
+5 -4
View File
@@ -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);
+5 -4
View File
@@ -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);
+5 -4
View File
@@ -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);
+5 -4
View File
@@ -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);
+42
View File
@@ -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
+352
View File
@@ -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_<ADDR>` 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_<ADDR>()` macro in src/shared/<header>.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_<ADDR>()` — address order preserved
4. BYTE-GATE every touched overlay (`make build BINARY=<ov>` == 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_<ADDR>() 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: "<type> 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_<ADDR>() \\`-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()