From a6e8eca4367b3eea69ef24e326ab85353c68a004 Mon Sep 17 00:00:00 2001 From: Sylpheed RE agent Date: Fri, 28 Aug 2026 01:32:52 +0000 Subject: [PATCH] re: the asteroid-field tables (AsteroidGroup_00N) decoded MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New doc docs/re/structures/asteroid-fields.md, indexed. Half the chain was already in the corpus: stage-definition-table and stage-mission-tables record the per-phase AsteroidDefinition naming a .tbl, and unit-datasheet-static partitions Generic and finds 10 per pack whose only field is {EnumAsteroidGroup}, "an asteroid-group file". Neither says what is inside them. The table: 10 objects per pack x 6 packs = 60, 384 AsteroidGroup_* records, 4 or 7 groups per object. Every record carries exactly three fields, all 384, no partials - AsteroidModelName (28 distinct; mob_n050..n056 x24 each, mob_n040..n043 x18), AsteroidFrameName (64 distinct, Frame_S[_pN]_ Asteroid_NN, the same names regn-map-grid reports the Route tables showing), and EnumAsteroid (37 distinct, 1..588, 6633 per pack summed). Enum is a COUNT; Enumerate names a TABLE. EnumAsteroidGroup equals its object's group count in 54 of 60 and EnumAsteroid is a plain integer - the opposite of the Enumerate* join rule. The -erate is the difference. Count-style fields still are not safe: the 6 failures are ONE object repeated per language pack, declaring 4 while holding 7. The tenth object is unreferenced and it is the one that miscounts. Joining by frame prefix, nine objects match the nine AsteroidDefinition values one to one; the tenth carries Frame_Alpha_S01_Asteroid, Frame_Alpha_S01_Haze and Frame_TAlpha_S01_Asteroid and is named by nothing. S28 has an asteroid VOLUME but no asteroid DEFINITION: mcol-collision lists nine _AsteroidVolume_wp meshes (…, S28_p1); the nine AsteroidDefinition values are those eight plus test_s8p1. Eight shared, one different each way. Not settled: what EnumAsteroid counts of; why S28_p1 has a volume and no definition. The name->object join did not reproduce by hash under name_hash, mod 0x00FFF9D7, or a 24-bit mask - the join used is by frame prefix. All fifteen artefacts byte-identical. --- docs/re/BACKLOG.md | 49 +++++++++++++++ docs/re/INDEX.md | 1 + docs/re/structures/asteroid-fields.md | 86 +++++++++++++++++++++++++++ 3 files changed, 136 insertions(+) create mode 100644 docs/re/structures/asteroid-fields.md diff --git a/docs/re/BACKLOG.md b/docs/re/BACKLOG.md index da485c3e..d7d28d20 100644 --- a/docs/re/BACKLOG.md +++ b/docs/re/BACKLOG.md @@ -5703,3 +5703,52 @@ S7–S16 + S24–27,29 and for S28, and the roster omits both while keeping corpus already decodes"; what it does not say is that there are only **46 distinct** values disc-wide. `AsteroidGroup_00N` / `AsteroidModelName` are new to the corpus. Written up in `docs/re/structures/player-tuning-tables.md`. + +--- + +## ✅ 2026-08-28 — the asteroid-field tables (`AsteroidGroup_00N`) decoded + +Item (a), the new nouns from the roster census. New doc +`docs/re/structures/asteroid-fields.md`, indexed. + +The chain was already half in the corpus: [[stage-definition-table]] / +[[stage-mission-tables]] record the per-phase **`AsteroidDefinition`** naming a +`.tbl`, and [[unit-datasheet-static]] partitions `Generic` and finds **10 per pack +whose only field is `{EnumAsteroidGroup}` — "an asteroid-group file"**. What +neither says is what is inside them. + +✅ **The table.** 10 objects per pack × 6 packs = 60, **384 `AsteroidGroup_*` +records**, 4 or 7 groups per object (12 objects with 4, 48 with 7). Every record +carries exactly **three** fields, all 384, no partials: `AsteroidModelName` (28 +distinct — `mob_n050`…`mob_n056` ×24 each, `mob_n040`…`mob_n043` ×18), +`AsteroidFrameName` (64 distinct, `Frame_S[_pN]_Asteroid_NN` — the same names +[[regn-map-grid]] reports the `Route` tables showing), and `EnumAsteroid` +(37 distinct, **1…588**, 6 633 per pack summed). + +✅ **`Enum` is a COUNT; `Enumerate` names a TABLE.** +`EnumAsteroidGroup` equals its object's group count in **54 of 60**, and +`EnumAsteroid` is a plain integer — the opposite of the `Enumerate*` join rule. +**The `-erate` is the difference.** ⚠️ And `Count`-style fields still are not +safe: the 6 failures are **one object repeated per language pack**, declaring +`4` while holding `7`. + +🔑 **The tenth object is unreferenced, and it is the one that miscounts.** +Joining by frame prefix: nine objects match the nine `AsteroidDefinition` values +one-to-one (S01 4 groups, S04/S05/S13/S14_p1/S14_p2/S08_p1/S08_p2 7 each, Test 4), +and the tenth carries **`Frame_Alpha_S01_Asteroid`, `Frame_Alpha_S01_Haze`, +`Frame_TAlpha_S01_Asteroid`** and is named by nothing — an `Alpha` Stage-01 +variant with its own haze. An abandoned table is exactly what a wrong declared +count looks like. + +🔑 **S28 has an asteroid *volume* but no asteroid *definition*.** +[[mcol-collision]] lists nine `_AsteroidVolume_wp` meshes — S01, S04, S05, +S08_p1, S08_p2, S13, S14_p1, S14_p2, **S28_p1**. The nine `AsteroidDefinition` +values are those eight plus **`test_s8p1`**. Eight shared, one different each way. + +🟡 Not settled: what `EnumAsteroid` counts *of* (instances per frame fits the +1…588 range, untested); why S28_p1 has a volume and no definition. 🔴 **The +name→object join did not reproduce by hash** — `AsteroidDefinition` values do not +map to their objects' pak entry keys under `name_hash`, nor under that key +reduced `mod 0x00FFF9D7` or masked to 24 bits. The join above is by frame prefix, +which is direct evidence; either those keys are not name hashes or +[[archive-naming]]'s resolver differs from `unitgroup.name_hash`. Not chased. diff --git a/docs/re/INDEX.md b/docs/re/INDEX.md index afb62775..31b64a87 100644 --- a/docs/re/INDEX.md +++ b/docs/re/INDEX.md @@ -119,6 +119,7 @@ files, which is how the same ground got covered twice. | [`structures/isl-message-dialogue-link.md`](structures/isl-message-dialogue-link.md) | Mission scripts as dialogue — built-in 64 -> message id -> caption text | ✅ CONFIRMED total, 2 683/2 683 call sites across all 28 stages resolve, no residue | | [`structures/mission-objective-counter.md`](structures/mission-objective-counter.md) | `REMAINING OB` — the mission's own objective counter, in RAM | ✅ CONFIRMED for one Stage 02 run: a big-endian u32 whose value | | [`structures/movie-subtitles.md`](structures/movie-subtitles.md) | Movie subtitles & the movie ↔ mission ↔ text chain | — | +| [`structures/asteroid-fields.md`](structures/asteroid-fields.md) | `AsteroidGroup_00N` — the asteroid-field tables a `Phase_N.AsteroidDefinition` names | ✅ 10 objects/pack, 384 group records, 3 fields each (`AsteroidModelName`, `AsteroidFrameName`, `EnumAsteroid` = a count 1…588); `Enum` counts while `Enumerate` names a table (54/60); 🔑 a tenth `Frame_Alpha_S01_*` object nothing references — and it is the one that miscounts; 🔑 **S28_p1 has an asteroid volume but no definition** | | [`structures/mcol-collision.md`](structures/mcol-collision.md) | `MCOL` — same container and map parameters as `REGN` | 🟡 OPENED — `POF0` at `data_size+16` 11/11, bbox pad words 11/11, `extent == max−min` 11/11, and bbox + cell-size distributions identical to `REGN` (2/6/3 and 2/9); everything past `0x40` ❔ | | [`structures/regn-map-grid.md`](structures/regn-map-grid.md) | `REGN` — a per-map spatial grid (and `MCOL` beside it) | ✅ CONFIRMED for the header, which self-checks on all 11 objects on | | [`structures/savegame-format.md`](structures/savegame-format.md) | Save file (`savedata`) — container ✅ exact, 3 fields named ✅, rest ❔ (2026-08-11) | ✅ CONFIRMED for the container and the chunk layout — parsed off the | diff --git a/docs/re/structures/asteroid-fields.md b/docs/re/structures/asteroid-fields.md new file mode 100644 index 00000000..348a119f --- /dev/null +++ b/docs/re/structures/asteroid-fields.md @@ -0,0 +1,86 @@ +# The asteroid-field tables (`AsteroidGroup_00N`) + +**Status: ✅ decoded statically, 2026-08-28.** Every number below is an exact +disc census over all 41 `.pak` archives using the corpus reader. + +## Where it sits in the chain + +[[stage-definition-table]] and [[stage-mission-tables]] already record that a +stage's `Phase_N` block carries an **`AsteroidDefinition`** field naming a `.tbl` +(`test_s8p1_asteroid.tbl` in the `_Test` template, `""` in most phases), and +[[unit-datasheet-static]] already partitions the `Generic` records and finds +**10 per pack whose only field is `{EnumAsteroidGroup}` — "an asteroid-group +file"**. What neither says is what is *inside* those ten files. + +``` +Stage_SNN . Phase_N . AsteroidDefinition = "S04_asteroid.tbl" + └── that object: Generic . EnumAsteroidGroup = "7" + AsteroidGroup_000 … AsteroidGroup_006 + AsteroidModelName = mob_n050 + AsteroidFrameName = Frame_S04_Asteroid_01 + EnumAsteroid = 39 +``` + +## ✅ The table + +**10 objects per pack × 6 `GP_MAIN_GAME_` packs = 60**, holding **384 +`AsteroidGroup_*` records** in total. Each object has **4 or 7** groups (12 +objects with 4, 48 with 7). Every group record carries exactly **three** fields, +present on all 384 with no partials: + +| field | distinct values | what it is | +|---|---:|---| +| `AsteroidModelName` | 28 | the mesh — `mob_n050`…`mob_n056` (24 uses each) and `mob_n040`…`mob_n043` (18 each) | +| `AsteroidFrameName` | 64 | `Frame_S[_pN]_Asteroid_NN` — the placement frame | +| `EnumAsteroid` | 37 | a **count**, 1 … 588; 6 633 asteroids per pack summed | + +`Frame_S_Asteroid` is the same name [[regn-map-grid]] reports the mission +`Route` tables showing independently — the frame is the join to the map data. + +## ✅ `Enum` is a count; `Enumerate` names a table + +`EnumAsteroidGroup` equals the number of `AsteroidGroup_*` records in its own +object in **54 of 60** objects, and `EnumAsteroid` is a plain integer. That is +the opposite of the `Enumerate*` convention, where the value is a child table's +file name. **The `-erate` is the difference**, and it is worth checking the +spelling before assuming which kind a field is. + +⚠️ `Count`-style fields still are not safe: the **6 failures are one object, +repeated once per language pack** — it declares `4` and contains `7`. + +## 🔑 The tenth object is unreferenced, and it is the one that miscounts + +Joining the ten objects to the nine `AsteroidDefinition` values by frame prefix: + +| object's frame prefix | groups | declared | named by | +|---|---:|---:|---| +| `Frame_S01_Asteroid` | 4 | 4 | `S01_asteroid.tbl` | +| `Frame_S04/S05/S13/S14_p1/S14_p2/S08_p1/S08_p2_Asteroid` | 7 | 7 | the matching `SNN[_pN]_asteroid.tbl` | +| `Frame_Test_Asteroid` | 4 | 4 | `test_s8p1_asteroid.tbl` | +| **`Frame_Alpha_S01_Asteroid` + `Frame_Alpha_S01_Haze` + `Frame_TAlpha_S01_Asteroid`** | **7** | **4** | **nothing** | + +Nine `AsteroidDefinition` names, nine matching objects, and a **tenth that no +phase names** — an `Alpha` Stage-01 variant with its own `Haze` frames and a +`TAlpha` set. It is also the only object whose declared count is wrong, which is +what an abandoned table looks like. + +## 🔑 S28 has an asteroid *volume* but no asteroid *definition* + +[[mcol-collision]] lists **nine** `_AsteroidVolume_wp` collision meshes: S01, +S04, S05, S08_p1, S08_p2, S13, S14_p1, S14_p2 and **S28_p1**. The nine +`AsteroidDefinition` values are the same eight stages plus **`test_s8p1`**. So +the two sets share eight and differ by one each: **`S28_p1` has the collision +volume for an asteroid field but no table saying which rocks to scatter in it**, +while `test_s8p1` has a table with no volume of its own. + +## 🟡 Not settled + +* What `EnumAsteroid` counts *of* — instances per frame is the obvious reading + and the 1…588 range fits, but it is untested. +* Why `S28_p1` has a volume and no definition (S28 is the challenge mission that + alone flies `rou_f004`, see [[stage-numbering-and-player-craft]]). +* 🔴 **The name→object join did not reproduce by hash.** `AsteroidDefinition` + values do not map to their objects' pak entry keys under `name_hash`, nor under + that key reduced `mod 0x00FFF9D7` or masked to 24 bits — the join above is by + frame prefix, which is direct. Either these keys are not name hashes or + [[archive-naming]]'s resolver differs from `unitgroup.name_hash`; not chased.