diff --git a/docs/re/BACKLOG.md b/docs/re/BACKLOG.md index fd5235b4..d42adbd6 100644 --- a/docs/re/BACKLOG.md +++ b/docs/re/BACKLOG.md @@ -5166,3 +5166,35 @@ the field set above and nothing else. 🟡 Not settled: which of the 23 objects belongs to which stage (only `AIParams_S02.tbl` is named by the corpus; the disc-wide harvest was not re-run against the other 22), and what consumes an `AIID` at runtime. + +--- + +## ✅ 2026-08-27 — every `AIParams` object mapped to its stage, zero residual + +Item (a). `aiparams_census.py` extended with the stage mapping; artefact +**+7/−0**, byte-identical across two runs. + +✅ **`StageResource.EnumerateAIParams` names the table, and every name resolves.** + +| | | +|---|---:| +| `StageResource` records | 29 | +| carrying `EnumerateAIParams` | **28** (the one without it is the `_Test` template) | +| distinct table names declared | **23** | +| names that hash to an `AIParams` object key | **23 / 23**, prefix `stage\` | +| objects left unnamed | **0** | +| declared names with no object | **0** | + +`AIParams_S01…S16` (16) + `AIParams_S24…S29` (6) + **`AIParams_Tutorial.tbl` +shared by six** (`UnitGroup_S18…S23`, the tutorials) = 23 tables over +`16 + 6 + 6 = 28` records. **The arithmetic closes both ways** and matches the +23-object count found last iteration. + +🔑 Same sharing shape as the settings family (`stage-settings-table.md`: 24 +objects, `StageParameter_Tutorial` shared by six tutorials). Two independent +families agree on how the tutorials are handled — n=2, a pattern rather than a +rule, but a consistent one. + +🟡 Not settled: what consumes an `AIID` at runtime (unchanged — the link from +`UnitGroup.AIID` to a profile record is documented, the code that acts on the +20 parameters is not). diff --git a/docs/re/data/aiparams-census.txt b/docs/re/data/aiparams-census.txt index 8c421c64..d0816820 100644 --- a/docs/re/data/aiparams-census.txt +++ b/docs/re/data/aiparams-census.txt @@ -15,6 +15,13 @@ declared names with no record in their own object: 0 profile records total: 782 (= 23 objects x 34) +## Which object is which stage + StageResource records: 29 carrying EnumerateAIParams: 28 + distinct table names declared: 23 AIParams objects: 23 + names that hash to an object key: 23 / 23 (prefix `stage\`, name_hash is case-insensitive) + objects left unnamed: 0 declared names with no object: 0 + shared: AIParams_Tutorial.tbl by 6 -> ['UnitGroup_S18.tbl', 'UnitGroup_S19.tbl', 'UnitGroup_S20.tbl', 'UnitGroup_S21.tbl', 'UnitGroup_S22.tbl', 'UnitGroup_S23.tbl'] + ## Does `Type` predict the field count? Fleet 6 fields x253 Squad 6 fields x46 diff --git a/docs/re/structures/stage-mission-tables.md b/docs/re/structures/stage-mission-tables.md index e1e36f68..f30e1b60 100644 --- a/docs/re/structures/stage-mission-tables.md +++ b/docs/re/structures/stage-mission-tables.md @@ -152,6 +152,28 @@ profiles and only the values move.** 🔑 The roster is declared the same way `DefTables` names its tables: an **`Enumerate_AIs` record whose field names are the profile names** (see [[archive-naming]] route 2). +### ✅ Which object is which stage — all 23 named, zero residual + +`StageResource.EnumerateAIParams` names the table, and the names resolve: + +| | | +|---|---:| +| `StageResource` records | 29 | +| carrying `EnumerateAIParams` | **28** (the one without it is the `_Test` template) | +| distinct table names declared | **23** | +| names that hash to an `AIParams` object key | **23 / 23** | +| objects left unnamed | **0** | +| declared names with no object | **0** | + +The names are `AIParams_S01…S16` (16), `AIParams_S24…S29` (6) and +**`AIParams_Tutorial.tbl`, shared by six records** — `UnitGroup_S18…S23`, the six +tutorials. `16 + 6 + 1 = 23` tables over `16 + 6 + 6 = 28` records; the arithmetic +closes both ways. + +🔑 That is the **same sharing shape as the settings family** +([[stage-settings-table]]: 24 objects, `StageParameter_Tutorial` shared by six). +The prefix is `stage\` — `name_hash` is case-insensitive, so its case is free. + ### ⚠️ `Type` predicts the field count — with exactly two exceptions | | | diff --git a/tools/re-capture/aiparams_census.py b/tools/re-capture/aiparams_census.py index 8d344c49..09f06cc9 100644 --- a/tools/re-capture/aiparams_census.py +++ b/tools/re-capture/aiparams_census.py @@ -58,6 +58,47 @@ def main(): % (sum(len(p) for _h, _d, p in objs), len(objs), len(objs[0][2]) if objs else 0)) + # Which object is which stage? `StageResource.EnumerateAIParams` names it. + stageres = [] + for h, b in pak_entries(paks[0]): + if b[:4] != b'IDXD': + continue + try: + recs = U.parse(b) + except Exception: + continue + for r in recs: + if r['squadron'] == 'StageResource': + stageres.append(dict((n, v) for _t, n, v in r['fields'] if n)) + named = sorted({d['EnumerateAIParams'] for d in stageres + if d.get('EnumerateAIParams')}) + keys = {h for h, _d, _p in objs} + resolved = {} + for n in named: + for pre in ('', 'stage\\', 'table\\'): + k = U.name_hash(pre + n) + if k in keys: + resolved[n] = (pre + n, k) + break + print("\n## Which object is which stage") + print(" StageResource records: %d carrying EnumerateAIParams: %d" + % (len(stageres), sum(1 for d in stageres if d.get('EnumerateAIParams')))) + print(" distinct table names declared: %d AIParams objects: %d" + % (len(named), len(objs))) + print(" names that hash to an object key: %d / %d (prefix `stage\\`," + " name_hash is case-insensitive)" % (len(resolved), len(named))) + print(" objects left unnamed: %d declared names with no object: %d" + % (len(keys - {v[1] for v in resolved.values()}), + len([n for n in named if n not in resolved]))) + share = collections.defaultdict(list) + for d in stageres: + v = d.get('EnumerateAIParams') + if v: + share[v].append(d.get('EnumerateSquadron', '?')) + for v, who in sorted(share.items()): + if len(who) > 1: + print(" shared: %-24s by %d -> %s" % (v, len(who), sorted(who))) + ct = collections.Counter() byname = collections.defaultdict(set) for _h, _d, p in objs: