diff --git a/docs/re/BACKLOG.md b/docs/re/BACKLOG.md index 6705b8ff..dcbc6543 100644 --- a/docs/re/BACKLOG.md +++ b/docs/re/BACKLOG.md @@ -5339,3 +5339,47 @@ with no mesh, and the one gear mesh carries three gear names. `CollisionSet` library (its 18 blobs are byte-identical, so a name join was not attempted); why `Body01` has no mesh; and none of the shader slots was traced to a shipped shader. + +--- + +## ✅ 2026-08-27 — all 277 base-solver rows classified objectively; 53 are data-table schemas + +Item (a). `name_block_bases.py` extended with a per-row data-table test; artefact +**+57/−0**, byte-identical across two runs (~2 min 12 s now — it adds a disc-wide +pak scan). + +🔑 **The test is objective, not by eye:** a row is a *data-table schema* if its +names are **IDXD record/field names on the disc** (13 450 such names disc-wide). +**53 of 277 rows** are ≥50 % disc names with ≥8 names; the other 224 are engine +/ XDK vocabulary, compiled key lists, or noise. + +⚠️ **The two axes are independent.** Against base confidence: `solved` bases +split 34 table / 136 not, `round` bases 16 / 91. So "round base" and "not a +table" are different questions — the earlier round-base warning was about +*where* the base is, this is about *what the names are*. + +✅ The 53 contain every loader already known (the control): unit definitions, +stage settings, `PlayerParams`, hangar, `AIParams`, the weapon datasheet, the +`PG*` roster, the three screen key lists. + +❔ **Five rows in the 53 are unowned — each noun grepped, appearing in no +`docs/re/` file:** +* `sub_823BDAA8` r11 (33) — `rou_e901` + `GN_MainGun_01_MuzC`, `GN_MainGun_02_Muz01…`: + the **S16 boss's muzzle/attach frames**, sitting beside the collision table. +* `sub_823BDAA8` r10 (25) — `Motion_stand`, `Motion_stand_b1`, `Motion_attackA_start`…: + **motion names**, the `EnumMotions` family that `DefTables` declares. +* `sub_82315AE8` r11 (20) — `InitRotation`, `MaxRotationSpeed`, `RotationAccel`, + `MaxVerticalSpeed`: the **`Guardian` record's own fields** — the S16 boss loader. +* `sub_8219E560` r11 (18) — `Detail_Rank`, `MISSIONS`, `Detail_Board_Permanent`: + the **leaderboard screen** keys. +* `sub_825F2CF0` + `sub_825F2F88` r0 (30 each, same base) — `FinalPassBG`, + `FinalPassToneRatio`, `FogMin/MaxDistance`: **post-processing**. + +⚠️ **Four rows that look new are not**, and their disc-overlap says so: 53–70 % +rather than ~100 %, because they mix arsenal fields the corpus owns +(`ConditionToDevelop`, `WeaponDesc`, `SilhouetteModel`) with **literal screen +coordinates as strings** (`757,228`, `903,343`, `1092,457`). + +🟡 Not settled: none of the five was opened — this iteration produced the +shortlist, not the findings. The 224 non-table rows were not partitioned further +(XDK vs game engine vs noise). diff --git a/docs/re/data/name-block-bases.txt b/docs/re/data/name-block-bases.txt index cd91d093..96a33b89 100644 --- a/docs/re/data/name-block-bases.txt +++ b/docs/re/data/name-block-bases.txt @@ -295,6 +295,63 @@ 0x82060000 x1 rows ⚠ the 0x820B0000 cluster is ~60 near-identical functions in 0x8281xxxx-0x8284xxxx that all name the same `rou_e0NN` list. +## Is the row a DATA-TABLE schema? (names that are IDXD record/field names on the disc) + distinct IDXD record+field names disc-wide: 13450 + rows that are >=50% disc names and >=8 names: 53 / 277 + sub_82341A20 r30 0x82088F94 217 names 89% disc + sub_822215D0 r11 0x820A0000 205 names 98% disc + sub_8230D1F8 r29 0x8209FD28 129 names 97% disc + sub_82338EE0 r30 0x8208583C 97 names 94% disc + sub_822F9498 r29 0x8209FD28 90 names 99% disc + sub_822AE628 r11 0x820B0000 81 names 67% disc + sub_823BDAA8 r11 0x820B0000 33 names 88% disc + sub_82561C70 r11 0x820A90D8 30 names 50% disc + sub_825F2CF0 r0 0x8209EB30 30 names 97% disc + sub_825F2F88 r0 0x8209EB30 30 names 97% disc + sub_82297550 r11 0x820B0000 27 names 67% disc + sub_822A2F00 r11 0x820B0000 27 names 70% disc + sub_823BDAA8 r10 0x820B0000 25 names 100% disc + sub_822215D0 r10 0x820A0000 24 names 75% disc + sub_822814D8 r11 0x820B0000 24 names 100% disc + sub_8230D1F8 r28 0x820978AC 24 names 96% disc + sub_8247DFC0 r31 0x8209E690 23 names 96% disc + sub_821A6CF0 r29 0x820A1630 22 names 100% disc + sub_8227A3A0 r11 0x820B0000 21 names 100% disc + sub_82315AE8 r11 0x820B0000 21 names 90% disc + sub_8233C368 r28 0x8208583C 20 names 100% disc + sub_8236B608 r11 0x820B0000 20 names 100% disc + sub_8221F700 r31 0x820A0E9C 19 names 100% disc + sub_82288028 r11 0x820B0000 19 names 53% disc + sub_822A9C18 r11 0x820B0000 19 names 89% disc + sub_8219E560 r11 0x820A0000 18 names 100% disc + sub_82261F70 r31 0x82086C1C 18 names 89% disc + sub_82286BC8 r11 0x820B0000 18 names 100% disc + sub_821AA1B0 r31 0x820A97CC 16 names 94% disc + sub_82804AD0 r11 0x820A792C 16 names 88% disc + sub_82222E70 r0 0x8209E6D8 15 names 100% disc + sub_8227F8E0 r31 0x820A0E9C 15 names 100% disc + sub_82282DB0 r31 0x820A0E8C 15 names 100% disc + sub_823479B8 r28 0x820864F4 15 names 100% disc + sub_82565C80 r11 0x820A5574 15 names 87% disc + sub_825DA790 r26 0x820A3B4C 15 names 93% disc + sub_8220ED30 r31 0x820A0E9C 14 names 100% disc + sub_8221F700 r11 0x820A0000 14 names 86% disc + sub_822F2E58 r31 0x820A97E4 14 names 100% disc + sub_82338EE0 r11 0x820AA368 14 names 100% disc + sub_823B2620 r31 0x8209E710 14 names 93% disc + sub_825635C8 r31 0x8209E230 14 names 100% disc + sub_8261F2C0 r11 0x8208AB54 14 names 86% disc + sub_8229F018 r11 0x820A4818 13 names 100% disc + sub_823785A0 r31 0x8209E710 13 names 92% disc + sub_8237BB78 r31 0x820AFDA0 13 names 100% disc + sub_824E76D0 r10 0x8209FC64 13 names 92% disc + sub_82193648 r11 0x820A8278 12 names 83% disc + sub_82220FA8 r31 0x820A0E8C 12 names 100% disc + sub_822C7480 r31 0x8209E6D4 12 names 100% disc + sub_82320B48 r10 0x820B0000 12 names 100% disc + sub_823276D0 r31 0x820A97EC 12 names 100% disc + sub_826FA758 r11 0x820A81F0 12 names 92% disc + ## The schema each one names, in code order sub_82341A20 (r30, base 0x82088F94) names 217: diff --git a/docs/re/structures/player-tuning-tables.md b/docs/re/structures/player-tuning-tables.md index 4cf3d4c0..a09fb9d0 100644 --- a/docs/re/structures/player-tuning-tables.md +++ b/docs/re/structures/player-tuning-tables.md @@ -204,6 +204,40 @@ HP 42 000 with levels at 25 000 / 10 000 and a lock-on release cycle One record, `Generic`, one field — the **name** `eff_n0071` with an empty value. An effect id and nothing else. Not identified. +## ✅ Classifying all 277 rows objectively — 53 are data-table schemas (2026-08-27) + +The earlier pass classified by eye and by function. This one is **by row** and +uses an objective test: **are the row's names IDXD record/field names on the +disc?** (13 450 of those disc-wide.) `name_block_bases.py` now prints it. + +| | rows | +|---|---:| +| **≥50 % disc names, ≥8 names — a data-table schema** | **53** | +| everything else (engine/XDK vocabulary, key lists, noise) | 224 | + +Split against the base-confidence axis: `solved` bases (non-zero low half) split +34 / 136, `round` bases 16 / 91 — so **a round base is not the same question as a +non-table row**, and both axes are needed. + +✅ The 53 include every loader the corpus already knows, which is the control. +❔ **What they also include, still unopened** (each grepped: the noun appears in +no `docs/re/` file): + +| row | names | what it looks like | +|---|---:|---| +| `sub_823BDAA8` r11 | 33 | `rou_e901` + `GN_MainGun_01_MuzC`, `GN_MainGun_02_Muz01…` — the **S16 boss's muzzle/attach frames**, next to the collision table above | +| `sub_823BDAA8` r10 | 25 | `Motion_stand`, `Motion_stand_b1`, `Motion_attackA_start`… — **motion names**, the `EnumMotions` family `DefTables` declares | +| `sub_82315AE8` r11 | 20 | `InitRotation`, `MaxRotationSpeed`, `RotationAccel`, `MaxVerticalSpeed` — the **`Guardian` record's own fields**, i.e. the S16 boss loader | +| `sub_8219E560` r11 | 18 | `Detail_Rank`, `MISSIONS`, `DETAIL_TITLE`, `Detail_Board_Permanent` — the **leaderboard screen** keys | +| `sub_825F2CF0` / `sub_825F2F88` r0 | 30 each | `FinalPassBG`, `FinalPassToneRatio`, `FogMin/MaxDistance` — **post-processing**, two functions with the same base | + +⚠️ Four rows that *look* new are not: `sub_82297550`, `sub_822A2F00`, +`sub_822A9C18`, `sub_82288028` mix arsenal fields the corpus owns +(`ConditionToDevelop`, `WeaponDesc` → [[arsenal-develop-economy]]; +`SilhouetteModel` → [[arsenal-item-weapon-chain]]) with **literal screen +coordinates as strings** — `757,228`, `903,343`, `1092,457` — which is why their +disc-overlap sits at 53–70 % rather than ~100 %. + ## ✅ Mining the 277: what the base-solver's index actually contains (2026-08-27) 277 rows over **190 distinct functions** (a function can read more than one diff --git a/tools/re-capture/name_block_bases.py b/tools/re-capture/name_block_bases.py index 0b04afc4..57d49d19 100644 --- a/tools/re-capture/name_block_bases.py +++ b/tools/re-capture/name_block_bases.py @@ -13,7 +13,7 @@ start. Control: it must recover `r30 = 0x82088F94` for the unit loader Regenerates docs/re/data/name-block-bases.txt. """ -import sys, collections, bisect +import sys, os, glob, collections, bisect DB = '/work/xenia-rs/sylpheed.db' MIN_GROUP = 8 # displacements needed before a group is worth solving @@ -99,6 +99,47 @@ def main(): print(" ⚠ the 0x820B0000 cluster is ~60 near-identical functions in" " 0x8281xxxx-0x8284xxxx that all name the same `rou_e0NN` list.") + # Is a row a DATA-TABLE schema or engine/XDK vocabulary? The objective test + # is whether its names are IDXD record/field names on the disc. + sys.path.insert(0, HERE) if False else None + from unit_substructures import pak_entries + import unitgroup as _U + disc = set() + for pk in sorted(glob.glob('/work/sylph_extract/**/*.pak', recursive=True)): + for _h, b in pak_entries(pk): + if b[:4] != b'IDXD': + continue + try: + rs = _U.parse(b) + except Exception: + continue + for r in rs: + disc.add(r['squadron']) + for _t, n, _v in r['fields']: + if n: + disc.add(n) + print("\n## Is the row a DATA-TABLE schema? (names that are IDXD record/field" + " names on the disc)") + print(" distinct IDXD record+field names disc-wide: %d" % len(disc)) + scored = [] + for name, reg, B, tot, n in rows: + nm, seen = [], set() + for a, d in order[name][reg]: + t = B + d + if t in txt and t not in seen: + seen.add(t) + nm.append(txt[t]) + if not nm: + continue + hit = sum(1 for x in nm if x in disc) + scored.append((hit / len(nm), hit, len(nm), name, reg, B)) + tab = [r for r in scored if r[0] >= 0.5 and r[2] >= 8] + print(" rows that are >=50%% disc names and >=8 names: %d / %d" + % (len(tab), len(scored))) + for r in sorted(tab, key=lambda r: (-r[2], r[3])): + print(" %-14s %-4s 0x%08X %4d names %3.0f%% disc" + % (r[3], r[4], r[5], r[2], 100 * r[0])) + print("\n## The schema each one names, in code order") for name, reg, B, tot, n in rows: seen, names = set(), []