From 516b2db94dfebd827cf2044f395b0cde9697dd8b Mon Sep 17 00:00:00 2001 From: Drew T <50529377+Druthulu@users.noreply.github.com> Date: Wed, 30 Sep 2026 11:07:50 -0600 Subject: [PATCH] T5.1.c2b: pad fields to pad_ (28), prim_layout_class post-relocation invariant, struct-map regen --- docs/struct-map.md | 28 ++++++++++++------------- include/struct_types.h | 32 ++++++++++++++--------------- phase-ends/current/logs/T5.1.c2b.md | 27 ++++++++++++++++++++++++ src/shared/engine_types.h | 22 ++++++++++---------- tools/type_census.py | 29 +++++++++++++++++++++++--- 5 files changed, 94 insertions(+), 44 deletions(-) create mode 100644 phase-ends/current/logs/T5.1.c2b.md diff --git a/docs/struct-map.md b/docs/struct-map.md index aefdafddad..6f913d2d47 100644 --- a/docs/struct-map.md +++ b/docs/struct-map.md @@ -1,8 +1,8 @@ # The struct map — the types the fleet's cast sites need (Phase 37 T1) -> **Generated by `tools/type_census.py` at `6f01332bc` (2026-09-30T06:31:48Z); never typed (R75).** Every raw pointer-cast dereference in `src/` was assigned to a base (a parameter, a local, a global by value or by address, an absolute address) and the bases were clustered by EVIDENCE only — the same global symbol, an argument passed to a callee's parameter, an assignment or return, a pointer field read, a shared body's fleet-wide identity — never by a similar offset signature. Each cluster is one type the tree needs. Names are placeholders (`Unkstruct_`: the lowest global instance's address, else the lowest member function's); fields are placeholders (`unk`) until Phase 38 cites evidence. +> **Generated by `tools/type_census.py` at `0864b69b4` (2026-09-30T17:06:52Z); never typed (R75).** Every raw pointer-cast dereference in `src/` was assigned to a base (a parameter, a local, a global by value or by address, an absolute address) and the bases were clustered by EVIDENCE only — the same global symbol, an argument passed to a callee's parameter, an assignment or return, a pointer field read, a shared body's fleet-wide identity — never by a similar offset signature. Each cluster is one type the tree needs. Names are placeholders (`Unkstruct_`: the lowest global instance's address, else the lowest member function's); fields are placeholders (`unk`) until Phase 38 cites evidence. -**19130 types explain 498866 of 502985 dereference sites (99.2 %); 4119 sites have a base the census could not classify; 856 types carry a width conflict.** Merges by evidence: param 38884, assign 16776, arg 10821, field 9909, ret-of 4320, return 1524, assign& 1184, arg& 270. +**19124 types explain 498868 of 502986 dereference sites (99.2 %); 4118 sites have a base the census could not classify; 857 types carry a width conflict.** Merges by evidence: param 38876, assign 16795, arg 10825, field 9909, ret-of 4331, return 1524, assign& 1184, arg& 270. | # | type | sites | bodies | fields | size ≥ | width conflicts (+sign mixes) | instance globals | pointer globals | |---:|---|---:|---:|---:|---:|---:|---|---| @@ -16,7 +16,7 @@ | 8 | `Unkstruct_8e3dbe51` | 3930 | 131 | 5 | 0x4c | 0 (+1 sign) | | | | 9 | `Unkstruct_8014A1B0` | 3778 | 542 | 13 | 0xfe | 0 (+1 sign) | | | | 10 | `Unkstruct_80128EA8` | 3311 | 632 | 18 | 0x38 | 0 (+13 sign) | | | -| 11 | `Unkstruct_ade2e8a0` | 3271 | 196 | 26 | 0xa4 | 3 (+9 sign) | | | +| 11 | `Unkstruct_57a28935` | 3271 | 196 | 26 | 0xa4 | 3 (+9 sign) | | | | 12 | `Unkstruct_1fa9e810` | 3144 | 131 | 3 | 0x49 | 0 (+0 sign) | | | | 13 | `Unkstruct_30b21035` | 3100 | 124 | 13 | 0x54 | 0 (+0 sign) | | | | 14 | `Unkstruct_80126948` | 2986 | 832 | 34 | 0xa4 | 0 (+10 sign) | D_80126948, D_80126E60 | D_80114EE8, D_801151D4, D_80126948, D_80126E60… | @@ -104,7 +104,7 @@ | 96 | `Unkstruct_80115128` | 667 | 534 | 1 | 0x2 | 0 (+0 sign) | D_80115128 | | | 97 | `Unkstruct_896c95f7` | 665 | 133 | 3 | 0x5e | 0 (+0 sign) | | | | 98 | `Unkstruct_61789347` | 665 | 133 | 0 | 0x0 | 0 (+0 sign) | | | -| 99 | `Unkstruct_44ce00ff` | 665 | 133 | 0 | 0x0 | 0 (+0 sign) | | | +| 99 | `Unkstruct_04834d6a` | 665 | 133 | 0 | 0x0 | 0 (+0 sign) | | | | 100 | `Unkstruct_801345F8` | 665 | 133 | 0 | 0x0 | 0 (+0 sign) | | | | 101 | `Unkstruct_801345F8` | 665 | 133 | 0 | 0x0 | 0 (+0 sign) | | | | 102 | `Unkstruct_801152A0` | 665 | 399 | 1 | 0x2 | 0 (+0 sign) | D_801152A0 | | @@ -130,10 +130,10 @@ | 122 | `Unkstruct_78eaa73c` | 553 | 7 | 15 | 0x10a | 0 (+7 sign) | | | | 123 | `Unkstruct_8011512E` | 532 | 133 | 1 | 0x2 | 0 (+0 sign) | D_8011512E | | | 124 | `Unkstruct_8016BFA8` | 532 | 133 | 4 | 0x24 | 0 (+0 sign) | | | -| 125 | `Unkstruct_30befa8c` | 520 | 130 | 3 | 0xfe | 0 (+0 sign) | | | -| 126 | `Unkstruct_01082f7a` | 520 | 520 | 1 | 0x4 | 0 (+0 sign) | | | -| 127 | `Unkstruct_db0ee441` | 516 | 129 | 4 | 0x38 | 0 (+0 sign) | | | -| 128 | `Unkstruct_7476116e` | 516 | 129 | 1 | 0x4 | 0 (+0 sign) | | | +| 125 | `Unkstruct_7476116e` | 532 | 133 | 1 | 0x4 | 0 (+0 sign) | | | +| 126 | `Unkstruct_30befa8c` | 520 | 130 | 3 | 0xfe | 0 (+0 sign) | | | +| 127 | `Unkstruct_01082f7a` | 520 | 520 | 1 | 0x4 | 0 (+0 sign) | | | +| 128 | `Unkstruct_db0ee441` | 516 | 129 | 4 | 0x38 | 0 (+0 sign) | | | | 129 | `Unkstruct_290ce261` | 504 | 126 | 3 | 0xfe | 0 (+0 sign) | | | | 130 | `Unkstruct_d6100faa` | 504 | 126 | 3 | 0x20 | 0 (+0 sign) | | | | 131 | `Unkstruct_5ebcbddc` | 504 | 126 | 3 | 0xe | 0 (+0 sign) | | | @@ -152,11 +152,11 @@ | 144 | `Unkstruct_8016B5C0` | 399 | 133 | 2 | 0x1c | 0 (+0 sign) | | | | 145 | `Unkstruct_8016BFA8` | 399 | 133 | 3 | 0x6 | 0 (+0 sign) | | | | 146 | `Unkstruct_8011F7F0` | 399 | 133 | 3 | 0x42 | 0 (+0 sign) | D_8011F7F0 | | -| 147 | `Unkstruct_1649b9a7` | 392 | 28 | 8 | 0x38 | 0 (+2 sign) | | | -| 148 | `Unkstruct_ea5ed765` | 390 | 130 | 1 | 0xdb | 0 (+0 sign) | | | -| 149 | `Unkstruct_80135260` | 390 | 130 | 3 | 0x6 | 0 (+0 sign) | | | -| 150 | `Unkstruct_a472a6ba` | 387 | 129 | 3 | 0x1c | 0 (+0 sign) | | | -| 151 | `Unkstruct_630777a3` | 387 | 258 | 2 | 0x5 | 0 (+0 sign) | | | +| 147 | `Unkstruct_630777a3` | 399 | 266 | 2 | 0x5 | 0 (+0 sign) | | | +| 148 | `Unkstruct_1649b9a7` | 392 | 28 | 8 | 0x38 | 0 (+2 sign) | | | +| 149 | `Unkstruct_ea5ed765` | 390 | 130 | 1 | 0xdb | 0 (+0 sign) | | | +| 150 | `Unkstruct_80135260` | 390 | 130 | 3 | 0x6 | 0 (+0 sign) | | | +| 151 | `Unkstruct_a472a6ba` | 387 | 129 | 3 | 0x1c | 0 (+0 sign) | | | | 152 | `Unkstruct_f2440762` | 384 | 128 | 1 | 0x4 | 0 (+0 sign) | | | | 153 | `Unkstruct_80164BDC` | 384 | 128 | 5 | 0x64 | 0 (+1 sign) | | | | 154 | `Unkstruct_8c2ab96e` | 384 | 128 | 3 | 0x8 | 0 (+0 sign) | | | @@ -207,4 +207,4 @@ | 199 | `Unkstruct_76f4299b` | 262 | 131 | 1 | 0x4a | 0 (+0 sign) | | | | 200 | `Unkstruct_3816caab` | 260 | 130 | 2 | 0x1c | 0 (+0 sign) | | | -*19130 types in all; the table shows the 200 largest by site count. The full map with every type's layout (offset, width, sign, site count per field) and conflicts is `.run/P37/census/struct_map.json`.* +*19124 types in all; the table shows the 200 largest by site count. The full map with every type's layout (offset, width, sign, site count per field) and conflicts is `.run/P37/census/struct_map.json`.* diff --git a/include/struct_types.h b/include/struct_types.h index 12665a5e44..bfa7cf7cf7 100644 --- a/include/struct_types.h +++ b/include/struct_types.h @@ -874,7 +874,7 @@ typedef struct { s16 m[3][3]; s32 t[3]; } Mtx32_801A54F0; typedef struct { s16 m[3][3]; s32 t[3]; } Mtx32_801A5698; typedef struct { s32 w[8]; } Mtx32_801A90D8; typedef struct { s16 m[3][3]; s32 t[3]; } Mtx32b; -typedef struct { Blk16X q0; Blk16X q1; s32 pad0, pad1; } Mtx40; +typedef struct { Blk16X q0; Blk16X q1; s32 pad_20, pad_24; } Mtx40; typedef struct { s16 m[3][3]; s32 t[3]; } MtxX; typedef struct { s16 m[9]; @@ -1227,7 +1227,7 @@ typedef struct { s16 x; s16 y; s16 z; s16 pad; } SVec801A5674; typedef struct { SVec801A5674 v[2]; s32 col[2]; s32 tag; s32 pad; } Prim801A5674; typedef struct { s16 m[4]; } SVec801A8E34; typedef struct { s16 vx, vy, vz, pad; } SVecX; -typedef struct { SVecX v[4]; CVecX c[4]; s32 code; s32 pad2; } PrimX; +typedef struct { SVecX v[4]; CVecX c[4]; s32 code; s32 pad_34; } PrimX; typedef struct { s16 vx, vy, vz, pad; } SVec_801A2400; typedef struct { s16 vx, vy, vz, pad; } SVec_801A3180; typedef struct { s16 vx, vy, vz, pad; } SVec_801A39D0; @@ -1661,7 +1661,7 @@ typedef struct { u32 addr : 24; u32 len : 8; u32 code; } DrTpage; typedef struct { u8 pad0[8]; s32 w8; - u8 pad12[10]; + u8 pad_C[10]; u8 b16; u8 pad17[2]; u8 b19; @@ -1766,7 +1766,7 @@ typedef struct { /* 0x18 */ u32 xy2; /* 0x1C */ u8 u2; /* 0x1D */ u8 v2; - /* 0x1E */ u16 pad0; + /* 0x1E */ u16 pad_1E; } FT3_16D78; typedef struct { /* 0x00 */ u32 tag; @@ -1782,11 +1782,11 @@ typedef struct { /* 0x18 */ u32 xy2; /* 0x1C */ u8 u2; /* 0x1D */ u8 v2; - /* 0x1E */ u16 pad0; + /* 0x1E */ u16 pad_1E; /* 0x20 */ u32 xy3; /* 0x24 */ u8 u3; /* 0x25 */ u8 v3; - /* 0x26 */ u16 pad1; + /* 0x26 */ u16 pad_26; } FT4_16F18; typedef struct { s32 unk00; /* 0x00 */ @@ -1907,11 +1907,11 @@ typedef struct { u32 tag; u8 r0, g0, b0, code; s16 x0, y0; - u8 r1, g1, b1, pad1; + u8 r1, g1, b1, pad_F; s16 x1, y1; - u8 r2, g2, b2, pad2; + u8 r2, g2, b2, pad_17; s16 x2, y2; - u8 r3, g3, b3, pad3; + u8 r3, g3, b3, pad_1F; s16 x3, y3; } G4_80017778; typedef struct { @@ -1950,11 +1950,11 @@ typedef struct { u32 rgb2; /* 0x1C */ s16 x2, y2; /* 0x20 */ u8 u2, v2; /* 0x24 */ - u16 pad2; /* 0x26 */ + u16 pad_26; /* 0x26 */ u32 rgb3; /* 0x28 */ s16 x3, y3; /* 0x2C */ u8 u3, v3; /* 0x30 */ - u16 pad3; /* 0x32 */ + u16 pad_32; /* 0x32 */ } GT4_80017930; typedef struct { /* 0x0 */ u32 xy; @@ -2167,9 +2167,9 @@ typedef struct { u32 tag; u8 r0, g0, b0, code; s16 x0, y0; - u8 r1, g1, b1, pad1; + u8 r1, g1, b1, pad_F; s16 x1, y1; - u8 r2, g2, b2, pad2; + u8 r2, g2, b2, pad_17; s16 x2, y2; } PG3; typedef struct { @@ -2257,7 +2257,7 @@ typedef struct { u16 v1; /* 0x18 */ u16 v2; /* 0x1A */ u16 v3; /* 0x1C */ - u16 pad1; /* 0x1E */ + u16 pad_1E; /* 0x1E */ } Prim20; typedef struct { u32 unk0; @@ -2671,7 +2671,7 @@ typedef struct { s16 field_0x34; /* 0x34 */ } Struct80078E78; typedef struct { - u8 pad15[0x15]; + u8 pad_0[0x15]; u8 field_0x15; /* 0x15 */ u8 pad16[0x3]; u8 field_0x19; /* 0x19 */ @@ -2689,7 +2689,7 @@ typedef struct { s32 field_0x28; /* 0x28 */ } Struct800D2FEC; typedef struct { - u8 pad15[0x15]; + u8 pad_0[0x15]; u8 field_0x15; u8 pad16[0x3]; u8 field_0x19; diff --git a/phase-ends/current/logs/T5.1.c2b.md b/phase-ends/current/logs/T5.1.c2b.md new file mode 100644 index 0000000000..cf64ff20ea --- /dev/null +++ b/phase-ends/current/logs/T5.1.c2b.md @@ -0,0 +1,27 @@ +# T5.1.c2b — census relocation batches ov-block/md/main/headers, pad renames, prim_layout_class control + +## Changed +- Batches via `tools/lift_types.py --census .run/P38/census/outside_canon.tsv --batch [--apply] --excluded-out .run/P38/t51/c2_excluded.txt`, census re-run (`--no-cache`) before each dry-run; every dry-run `census rows with no movable def found: 0`, excluded 0. + - c36cb4980 ov-block: 694 groups / 1044 defs (646 -> engine_types.h, 5 -> struct_types.h), 566 files. + - b0b9c4c46 md: 297 groups / 359 defs (12 et, 285 st), 61 files. + - c3ae8d6da main: 223 groups / 236 defs (all st), 9 files. + - headers commit: 47 groups / 49 defs (21 et, 26 st), 21 files. +- Pad renames (T5.c2 precedent `pad_`), 28 fields / 15 canon types, def-only: no member accesses exist (`git grep '(\.|->)pad{0,1,2,3,12,15}\b'` = 0; the 265 `p->pad4` hits are another type whose pad4 sits at 0x4; St_SC03_118_80183{3C4,4DC} only used via `P` macros in ov_SC03_118_o0d.c, no pad access). engine_types.h: Ent_SC07_010_8017AE2C/EntA pad2->pad_E0, PHdr_80189340 pad1->pad_4, St_SC03_118_801833C4/801834DC pad1..4->pad_4/_DE/_E4/_E8. struct_types.h (arrived with md/main/headers batches): Mtx40 pad_20/pad_24, PrimX pad_34, Dst pad_C, FT3_16D78 pad_1E, FT4_16F18 pad_1E/pad_26, G4_80017778 pad_F/_17/_1F, GT4_80017930 pad_26/_32, PG3 pad_F/_17, Prim20 pad_1E, Struct800D2E20/Struct800D3104 pad15->pad_0. Scratch: `.run/P38/t51/c2/padfix.py`. +- `tools/type_census.py` controls() #3 prim_layout_class: + - old: `prim_files > 1000 and top_n == len(prim_defs)` (Prim_8016E7C8 in ~1,192 .c files, two spellings, one layout class). + - new: `len(prim_defs) == 1 and is_canon and size == 64` (post-relocation invariant). Premise was pre-relocation: pre-c2 copy (6869f8f26~1, ov_SC01_000_jr_8016AB6C.c) is byte-identical text to the canon def now in engine_types.h (v[4] SVECTOR + 6 s32 + u8 + pad[7] = 0x40), so no layout loss. + - selftest: `PRIM_FIXTURES` (two spellings, two files) + check "Prim spellings one layout 64" (distinct text_hash, both 64 B, equal layout_hash). Negative control on a scratch copy: mutating spelling 2 to `pad[3]; s32 x;` (same size, other layout) and to `pad[11]` (68 B) each -> 30/31 FAILED ['Prim spellings one layout 64']. + +## Commands / results +- R22 (`make clean && make extract-all JOBS=16 && make check-all JOBS=16`, run.sh --bg/--wait) after each batch and after the pad rename: every run `extract-all: 217 extracted, 0 failed of 217`, `check-all: 218 passed, 0 failed of 218`. +- `.run/P38/t51/c2/hdrchk.sh` rc=0 after every apply. +- Census progression outside_canon/dead/pad_names: start 1684/10/1 -> after ov-block 642/11/11 -> md 284/30/14 -> main 48/35/28 -> headers 0/37/(0, race with pad edit) -> final (census5, tree = this commit) `dup_classes=200 outside_canon=0 dead=37 twins_stale=0 variants=0 pad_names=0 parse_error_decls=1106 controls=4/4`. +- `type_census.py --selftest` -> 31/31 checks OK. +- `restruct.py --audit-types` -> 3417 canonical definitions audited; 0 violations. + +## For the expert +- dead grew 10 -> 37: locally-defined-but-unused defs relocated into canon now count as dead canon names. Not in this task's done-criteria; not touched. +- pad_names grew as batches moved defs into canon (1 -> 28); brief named 1, all 28 renamed to reach pad_names=0. +- Census --no-cache takes ~4.5-5 min: always run.sh --bg + --wait (a foreground call exceeds 285 s). +- dup_classes=200 unchanged (folding names is not this task's). +- docs/struct-map.md regenerated by census5, committed here. diff --git a/src/shared/engine_types.h b/src/shared/engine_types.h index 41f34da9e9..3a9d0dc94d 100644 --- a/src/shared/engine_types.h +++ b/src/shared/engine_types.h @@ -7379,7 +7379,7 @@ typedef struct { s16 f4; s16 f6; } D_80195AF6_t; -struct Ent_SC07_010_8017AE2C { u8 pad[0xDC]; s32 unkDC; u8 pad2[0x1C]; u16 unkFC; }; +struct Ent_SC07_010_8017AE2C { u8 pad[0xDC]; s32 unkDC; u8 pad_E0[0x1C]; u16 unkFC; }; typedef struct { s16 m[3][3]; s32 t[3]; } MTX_CF90; typedef struct { u8 pad_00[0x20]; @@ -7821,7 +7821,7 @@ struct Cnt { struct Cnt_80187DE4 { s32 c; }; -struct EntA { u8 pad[0xDC]; s32 unkDC; u8 pad2[0x1C]; u16 unkFC; }; +struct EntA { u8 pad[0xDC]; s32 unkDC; u8 pad_E0[0x1C]; u16 unkFC; }; typedef struct { u8 pad00[4]; u32 flags; /* 0x04 */ @@ -7998,7 +7998,7 @@ typedef struct { u8 r3, g3, b3, p3; /* 0x1C..0x1F */ s16 x3, y3; /* 0x20, 0x22 */ } PG4_8018C960_80189EA0; -typedef struct { u8 pad0[3]; u8 len; u8 pad1[3]; u8 code; } PHdr_80189340; +typedef struct { u8 pad0[3]; u8 len; u8 pad_4[3]; u8 code; } PHdr_80189340; typedef struct { u32 addr : 24; u32 len : 8; } PTag_8017DD28; typedef struct { u32 addr : 24; u32 len : 8; } PTag_8018C960; typedef struct { u32 addr : 24; u32 len : 8; } PTag_8018C960_801895C8; @@ -8198,26 +8198,26 @@ struct St80180ACC { s16 a[3]; u16 b[3]; }; typedef struct { u8 pad0[2]; union { s16 s; u16 u; } f02; - u8 pad1[0xD8]; + u8 pad_4[0xD8]; union { s16 s; u16 u; } fDC; - u8 pad2[4]; + u8 pad_DE[4]; s16 fE2; - u8 pad3[2]; + u8 pad_E4[2]; s16 fE6; - u8 pad4[4]; + u8 pad_E8[4]; u16 fEC; u16 fEE; } St_SC03_118_801833C4; typedef struct { u8 pad0[2]; union { s16 s; u16 u; } f02; - u8 pad1[0xD8]; + u8 pad_4[0xD8]; union { s16 s; u16 u; } fDC; - u8 pad2[4]; + u8 pad_DE[4]; s16 fE2; - u8 pad3[2]; + u8 pad_E4[2]; s16 fE6; - u8 pad4[4]; + u8 pad_E8[4]; s16 fEC; s16 fEE; } St_SC03_118_801834DC; diff --git a/tools/type_census.py b/tools/type_census.py index 5144f958e4..b6ec96852e 100644 --- a/tools/type_census.py +++ b/tools/type_census.py @@ -1554,14 +1554,17 @@ def controls(defs_all, sites_all, extern_data, by_text, name_layouts): return None m32, m48 = size_of("MATRIX"), size_of("MATRIX_L48") out["matrix_layouts"] = dict(ok=(m32 == 32 and m48 == 48), detail=f"MATRIX={m32} MATRIX_L48={m48}") - # 3. Prim_8016E7C8 (defined in ~1,192 .c files under two spellings): ONE layout class covers every definition + # 3. Prim_8016E7C8: ONE canonical definition, 64 B. Was (pre-T5.1.c2): "defined in ~1,192 .c files under two + # spellings, ONE layout class covers every definition" (prim_files > 1000); the census relocation left one canon + # def, so the multi-spelling case now lives in the selftest ("Prim spellings one layout 64"). prim_defs = [d for d in defs_all if "Prim_8016E7C8" in d["names"] or d["tag"] == "Prim_8016E7C8"] prim_files = len({d["file"] for d in prim_defs}) prim_lh = collections.Counter(d["lhash"] for d in prim_defs) prim_texts = len({d["text_hash"] for d in prim_defs}) top_lh, top_n = (prim_lh.most_common(1)[0] if prim_lh else (None, 0)) - out["prim_layout_class"] = dict(ok=(prim_files > 1000 and top_n == len(prim_defs) and top_lh is not None), - detail=f"defs in {prim_files} files, {prim_texts} spellings, {top_n}/{len(prim_defs)} in one layout ({prim_defs[0]['size'] if prim_defs else None} B)") + out["prim_layout_class"] = dict(ok=(len(prim_defs) == 1 and prim_defs[0]["is_canon"] and prim_defs[0]["size"] == 64), + detail=f"defs in {prim_files} files, {prim_texts} spellings, {top_n}/{len(prim_defs)} in one layout ({prim_defs[0]['size'] if prim_defs else None} B)" + + ("" if all(d["is_canon"] for d in prim_defs) else " (non-canon def present)")) # 4. a TU-CONFLICT ledger row reproduced: D_800A651C is declared with more than one type across TUs types = collections.Counter(e["type"] + e["dims"] for e in extern_data if e["name"] == "D_800A651C") out["tu_conflict_D_800A651C"] = dict(ok=(len(types) > 1), detail=" | ".join(f"{t}:{n}" for t, n in types.most_common(5))) @@ -1690,6 +1693,20 @@ void f(void) { fr.a = 0; } ''' +PRIM_FIXTURES = (r'''typedef struct { s16 vx, vy, vz, pad; } SVECTOR_8016E7C8; +typedef struct { + SVECTOR_8016E7C8 v[4]; /* 0x00 */ + s32 f0, f1, f2, f3, f4, f5; /* 0x20..0x37 */ + u8 f6; /* 0x38 */ + u8 pad[7]; /* -> 0x40 */ +} Prim_8016E7C8; +''', r'''typedef struct { short vx; short vy; short vz; short pad; } SVECTOR_8016E7C8; +typedef struct { + SVECTOR_8016E7C8 v[4]; + s32 f0; s32 f1; s32 f2; s32 f3; s32 f4; s32 f5; + u8 f6; u8 pad[7]; +} Prim_8016E7C8; +''') # the pre-T5.1.c2 live control: two spellings of Prim_8016E7C8 in two files, one layout class, 64 B TWINS_FIXTURE = '''# Struct twins ## Twins ### aaaa:opaque @@ -1735,6 +1752,12 @@ def selftest(): checks.append(("K&R-empty extern", any(e["name"] == "func_80002000" and e["params"] == "" for e in res["extern_fns"]))) checks.append(("typedef alias fnptr", any(a["name"] == "Handler" and a["fnptr"] for a in res["typedef_aliases"]))) checks.append(("params of f1", res["fndefs"][0]["pnames"] == ["a0", "b", "c"])) + prim = [] + for i, txt in enumerate(PRIM_FIXTURES): + pres = walk_file(txt, f"src/ov_TEST/p{i}.c") + pr = Resolver({n: d for d in pres["definitions"] for n in d["names"] + ([d["tag"]] if d["tag"] else [])}).resolve_name("Prim_8016E7C8") + prim.append((pres["definitions"][-1]["text_hash"], pr[0], layout_hash(pr))) + checks.append(("Prim spellings one layout 64", prim[0][0] != prim[1][0] and prim[0][1] == prim[1][1] == 64 and prim[0][2] == prim[1][2])) # T5.1: the --check-structs reading (liveness, allowlists, block frames) cres = walk_file(CANON_FIXTURE, "src/shared/engine_types.h") ures = walk_file(USE_FIXTURE, "src/ov_TEST/u.c")