Commit Graph

780 Commits

Author SHA1 Message Date
Sylpheed RE agent
35a35f753f re: the EX_ selection is shown - a mode word at +184 tested against 3
result-screens.md left this explicitly open: "the selection has not been shown".
It is shown now. Both screens that reference EX_ names carry the same
five-instruction idiom immediately before the choice - sub_822814D8 at 0x822815b4
and sub_8227A3A0 at 0x8227a408:

  lwz    r11, 4(rX)              the owning object
  lwz    r11, 184(r11)           a mode word at +184
  addi   r11, r11, -3
  cntlzw r11, r11
  rlwinm r11, r11, 27, 31, 31    r11 = 1 iff [+184] == 3
  cmpli  cr6, 0, r11, 0x0
  bc     12, 4*cr6+eq, <plain>   not 3 -> STAGE_RESULT / OVERVIEW
                                 else  -> EX_STAGE_RESULT / EX_OVERVIEW

cntlzw of zero is 32, and rotating left 27 lifts bit 5 into bit 31, so the
sequence is a branchless equals-3 test. The equal-to-zero branch takes the plain
name, so EX_ is chosen exactly when the word is 3.

A partition over the three key-list loaders the corpus already names:

                        loads +184   does not
  references EX_ names       2           0
  does not                   0           1

sub_822814D8 and sub_8227A3A0 both load it; sub_82286BC8, which references no EX_
name, has no +184 access at all. Both off-diagonal cells are empty.

3 is the game's own EXTRA. challenge-mission-gate already established the
vocabulary for this constant at a three-way switch: == 3 is EXTRA, == 5 or 6 is
CHALLENGE, otherwise FILE. So the prefix reads literally - EX_ is the EXTRA
variant.

That REFINES the doc's tentative reading rather than confirming it. The section
proposed "EX_ = the challenge-mission debriefing" by analogy with _EXn on asset
names; the measured constant is EXTRA, which in the game's own three-way
vocabulary is a DIFFERENT kind from CHALLENGE (5/6).

Boundary of what was measured: the EXTRA = 3 constant is established at +144 of
the stage-loader class, while the word tested here is at +184 of [object + 4].
Same constant and the same EX/EXTRA naming, but two different offsets in what may
be two different objects - that the two fields are the same field is NOT shown,
only that both compare a mode word against 3.

All seventeen artefacts byte-identical.
2026-08-28 10:51:16 +00:00
Sylpheed RE agent
33671c5086 re: wep_85 is the ONLY declared-but-unshipped asset; the package gaps are unused numbers
The obvious follow-up to last commit's wep_85 finding: the weapon packages number
00 to 84 with 26 holes, so are those holes cut weapons too? Partitioning every
number 00-85 by package-present x named-anywhere-in-the-parsed-pak-data:

                     named in data   not named
   package present        59             0
   package absent          1            26

59 + 0 + 1 + 26 = 86, the numbers 00 through 85.

The 26 gaps are simply unused numbers, not unshipped weapons - nothing in the
data names them at all. The one occupied cell is the one already known: wep_85,
named 16 times, with no package. It is an isolated cut, not a pattern, which
refutes the natural expectation that finding one cut weapon means finding a
family of them.

The other zero is worth as much: 0 shipped packages are never named. Every
rou_f001_wep_NN.xpr on the disc is referenced by the data, so the package set and
the weapon vocabulary agree in both directions but for that single entry.

Running the identical test on the craft family gives a clean negative. Every
rou_f### number appearing inside an .xpr against every number named in the parsed
data: 16 in both, 0 named-but-unshipped, 0 shipped-but-unnamed. The 16 are
f001-f004, f101, f102, f104-f106, f201, f202, f301-f303, f401, f402 - the
hundreds digit groups them into five families with holes inside each (no f103, no
f203). No craft was cut.

So across both families the disc ships exactly what it declares, with the single
exception of that one weapon.

All seventeen artefacts byte-identical.
2026-08-28 10:33:41 +00:00
Sylpheed RE agent
7ba415fbae re: the last 9 effects are genuinely unshipped; wep_85 accounts for two
EF_IDX_ proved that prefixes exist, so the residual deserved the same treatment
across every package rather than one. Censusing prefixes over all 36
effect-carrying .xpr gives seven in use:

  EF_IDX_       223 names
  mdl_           45
  EF_IDX_mdl_    45
  VolumeLine_    10
  GN_            10
  GN__            6
  bare           the rest

Testing all nine unlocated names against all seven prefixes: 0 of 9 resolve.

That zero has force where the earlier disc-wide zero did not, and for the stated
reason - the control shows each prefix genuinely carries names the same search
reads (223, 45, 45, 10, 10, 6), so the instrument demonstrably works on the
population it is being asked about. The nine are bound by a datasheet field and
shipped in no package.

Two of them turn out to belong to one cut asset. eff_m010_wep_85 and
eff_m011_wep_85 name a weapon, and the weapon is real in the data:
Weapon_DSaber_P_wep_85_Beam with 24 uses, its Shell_, WeaponCannonName_ and
WeaponShellName_ siblings, and GameModel_eff_m010_wep_85 / _m011_ declaring the
two effects. But the weapon packages stop at 84 - hidden/resource3d/ holds 59
rou_f001_wep_NN.xpr files numbering 00 to 84 with gaps, and no wep_85. So wep_85
is a declared-but-unshipped weapon and its two effects go missing with it.

This does not contradict "every weapon is placed - 131 = 105+22+0+4". That
partition is declared x MOUNTED IN A LOADOUT, which is a different question from
whether a package ships.

Seven remain with no account: eff_e0044, eff_f0002, eff_f0002_barn, eff_h308,
eff_j002_e01, eff_j002_e02, eff_n0071. The .xpr route is now exhausted for them
under every prefix the disc uses; a different container or a runtime generator is
what is left.

All seventeen artefacts byte-identical.
2026-08-28 10:17:20 +00:00
Sylpheed RE agent
23a6cfd979 re: the EF_IDX_ prefix - ptc_pack has 727 names, and the map reaches 128 of 137
Censusing ptc_pack's own naming vocabulary turned up a third variant of the
prefix trap, and this one had been corrupting a number the corpus carried.

268 of ptc_pack's names do not start with eff_ at all. They start with EF_IDX_,
as in EF_IDX_eff_d001_f. A regex anchored at eff_ chops that prefix off and
merges distinct names, which is exactly where the earlier figure of 532 came
from. Enumerating maximal [A-Za-z0-9_] runs gives 727. The two earlier traps were
a STORED name being longer (rot_n001_break) and a BOUND name being a prefix
(eff_f0002 inside eff_f0002_barnhaze); this is the third - a prefix the pattern
cannot see at all, because its anchor sits in the middle of the real name.

Looking each bound name up bare AND under EF_IDX_ resolves 25 of the 34 that were
unlocated. The map is now 128 of 137, and the residual is 9, small enough to
print: eff_e0044, eff_f0002, eff_f0002_barn, eff_h308, eff_j002_e01,
eff_j002_e02, eff_m010_wep_85, eff_m011_wep_85, eff_n0071. All 17 eff_l### are
among the recovered.

This withdraws my own previous correction. I had recorded Base.xpr (53) as
holding more bound effects than ptc_pack (46), and struck out "ptc_pack is the
effect library". With the prefixed keys counted ptc_pack holds 71 - it IS the
larger library, and the 46 was an undercount from the same truncating pattern.
Two shared libraries remains right; which one is bigger does not.

The suffix vocabulary: 106 distinct tokens over the 727 names - IDX 223 (the
prefix above), _f 137, _e 119, _root 87, _col 54, _mdl 45, _break 43, _ring 38,
_ALL 17, _haze 14, _thunder 10. That census counts ALL tokens rather than
trailing ones, which is precisely how the EF_IDX_ PREFIX surfaced inside what I
had first labelled a suffix list - the mislabel found the bug.

Testing the structural candidates the way _hangar was tested, does the suffixed
name have a bare parent:

  _ALL      17 names    17 of 17
  _root     87          64 of 87
  _break    30          15 of 30
  _e        74           0 of 74
  _f        61           0 of 61

_root is strictly terminal - 87 of 87, and it never appears mid-name. The
compound shapes put it outermost: _e_root 19, _f_root 18, _break_root 13, bare
_root 30. So the order is <stem>_[<faction>|<break>]_root and _root reads as a
hierarchy marker rather than a variant - though 64 of 87 having a bare parent
means it is not simply the parent of an existing node, and _break at 15 of 30 is
likewise not a plain destroyed-twin-of-everything.

_e/_f never have a bare parent, 0 of 135. That is independent asset-side
confirmation of the faction law: an effect is authored per faction and there is
no faction-neutral original for either side to derive from.

effect-homes.txt changes 5/30 and every line pairs: five values changed (103->128,
34->9, ptc_pack 46->71 and its sort position, the residual header, 3-digit
80->105 of 110) plus 25 pure deletions, exactly the 25 recovered names. All are
3-digit, so the 4-digit line is unchanged at 23 of 27. The other sixteen
artefacts are byte-identical.
2026-08-28 10:01:53 +00:00
Sylpheed RE agent
aa478d9444 re: the faction law generalises - 564 of 564, four fields, all six paks
The previous commit measured _e/_f on one pak and only through the effect
binders. Widening the sweep to EVERY string field of every unit object in ALL
SIX GP_MAIN_GAME_* paks:

                value _e   value _f
  UN_e###          198          0
  UN_f###            0        366

564 of 564 agree and the mismatch residual is empty. The law is not confined to
one field either - it holds separately, at 100%, in each of four:

  LowerHPFxModel            252 of 252
  ShieldHitEffectName       210 of 210
  ShieldRecoverEffectName    84 of 84
  ExplosionFxModel           18 of 18

The two shield fields were not in the earlier measurement at all, so the law
reaches further than the *FxModel family that suggested it.

Scope stated exactly, because "general" would overclaim: this is a law about
EFFECTS, not about assets in general. The sweep covered every field, and every
_e/_f-suffixed value a unit binds turns out to live in those four effect fields.
No model, motion or SE value carries the suffix at all, so the faction pairing is
NOT shown for those kinds - there was simply nothing to test.

UN_n### (TTRL) binds no _e/_f value in any of the six paks: 12 objects, = 2
users, with nothing on either side. That confirms over the whole population what
was only a single-pak observation before.

Also corrects the ID grammar and reconciles a count. The earlier section reported
42 + 26 + 2 = 70 unit objects using a regex that required UN_<letter>###_<FACTION>_;
the looser UN_<letter>###_ finds 71. The extra one is UN_e910_core_ADAN_GeneratorCore,
which inserts a sub-kind token BEFORE the faction tag. So the grammar is
UN_<letter>###_[<subkind>_]<FACTION>_<name>, and both counts were right for their
own pattern.

All seventeen artefacts byte-identical.
2026-08-28 09:44:20 +00:00
Sylpheed RE agent
1265512880 re: _e/_f on an effect name is the binding unit's FACTION (94 of 94)
Chasing the 17 unlocated eff_l### turned up their shape first: they come in
_e/_f PAIRS - eff_l101_e + eff_l101_f, and the same for l102, l104, l105, l106,
l201, plus _e-only l010/l011/l107/l108 and _f-only l002.

Partitioning every eff_<letter><digits>_<e|f> binding by the ID letter of the
OWNING unit (one GP_MAIN_GAME_* pak = one user):

                effect _e   effect _f
  UN_e###           33          0
  UN_f###            0         61

94 of 94 agree and both off-diagonal cells are empty. The control reads the
factions straight off the IDs: UN_e### -> ADAN (42 objects), UN_f### -> TCAF
(26), UN_n### -> TTRL (2, tutorial, binding neither). So an effect ending _e
belongs to an ADAN ship and one ending _f to a TCAF ship - the same visual is
authored twice, once per faction, which is exactly why eff_l### arrives in pairs.

What the 34 unlocated ARE is now also clear, even though where they live is not.
They are one job, not a scatter: Generic binds 32 of the 34, Explosion 19,
Shell 9, Level_0 and Weapon 2 each. The binder fields rank LowerHPFxModel 252,
HitFxModel 144, then JetFxModel_00N and AfterBurnerFxModel_00N. They sit in the
six GP_MAIN_GAME_* paks at 130 bindings each plus 32 in DefTables.pak. Since
LowerHPFxModel is the damaged-ship effect, the residual is largely the
per-faction battle-damage and hit visuals. None of the 34 is a record name and
only one is a field name, so they are asset references.

Stated plainly: they remain unlocated AS ASSETS. Knowing the family and its
naming law does not say where the geometry lives - the .xpr route is exhausted
for them and the parsed pak payloads hold references, not meshes.

Also fixes a defect in the artefact shipped last commit. effect-homes.txt came
back with two equal-count lines swapped: Counter.most_common() breaks ties by
insertion order, so the package listing was not deterministic. Now sorted by
(-count, name) and verified to regenerate byte-identical twice running. This is
the corpus's own rule - any map built by iterating a set or Counter needs
sorted() - and the new tool had violated it.

The other sixteen artefacts are byte-identical; effect-homes.txt changes only in
the tie-break ordering of the five 1-count rows, with every line pairing.
2026-08-28 09:28:39 +00:00
Sylpheed RE agent
e72f0f14be re: the effect->package map, 103 of 137 - and eff_f0002 was a substring artefact
Enumerating eff_* names per .xpr across all 166 packages and matching the bound
names EXACTLY gives a real home for 103 of the 137, up from the 71 ptc_pack
alone accounted for. Only 36 packages carry an effect name at all, and two
dominate:

  Base.xpr                  53
  ptc_pack.xpr              46
  Stage_S28.xpr              2
  five rou_f001_wep_NN.xpr   1 each

So there are TWO shared effect libraries, not one - and ptc_pack.xpr is the only
*_pack bundle on the disc, so no third shared library is hiding. By digit-width:
3-digit 80 resolved of 110, 4-digit 23 of 27. The previous iteration's split
survives and sharpens - the four-digit series really does live outside ptc_pack
(that zero stands), and now we can say where: Base.xpr.

Correction to the previous commit. It reported eff_f0002 and eff_f0002_barn as
present in Base.xpr. Both were SUBSTRING artefacts: what the file actually holds
is eff_f0002_barnhaze, one longer resource name that grep -l eff_f0002 and
grep -l eff_f0002_barn each match inside. Neither bound name is there. This is
the corpus's own paid-for prefix lesson arriving from the other direction - last
time it was rot_n001 vs rot_n001_break with the stored name longer; here the
BOUND name was the prefix. The new map is exact-keyed and does not have this
failure mode, so the earlier positive is withdrawn.

34 names remain unlocated, dominated by a family the last pass did not single
out: eff_l### with 17 of the 34, then h 4, s 4, j 2, m 2, t 1, and four
four-digit names - eff_e0044, eff_f0002, eff_f0002_barn, eff_n0071.

Scope note worth keeping: the j 22 / t 14 clustering reported last time was the
residual against ptc_pack ALONE; against all packages those families are largely
accounted for and l is what is left. Both numbers are right for their own
population, which is exactly why a residual has to say what it was measured
against.

New artefact with its regenerator: tools/re-capture/effect_homes.py ->
docs/re/data/effect-homes.txt, which lists all 34 by name. All sixteen existing
artefacts byte-identical.
2026-08-28 09:12:15 +00:00
Sylpheed RE agent
7e9d1437c0 re: effects split into two families by digit-width; ptc_pack.xpr holds one
The open question was how an effect mesh is reached at all, after last
iteration's .xpr byte search was refuted by its own control. The corpus already
held the pointer: xbg7-mesh names ptc_pack.xpr, a 20 MB shared particle package
in hidden/resource3d/. It lists 532 distinct eff_* resources - 527 three-digit,
3 four-digit, 2 unnumbered.

Joining it against the 137 effect names the datasheets actually bind:

                    in ptc_pack   not
  3-digit                71        39
  4-digit                 0        27

Zero of the 27 four-digit names resolve in ptc_pack, and that series is a closed
three-letter set: e (10), f (12), n (5). Since eff_e0033 was found in Base.xpr,
the reading is two effect families - a shared three-digit particle library in
ptc_pack.xpr, and a four-digit series that lives in the per-model and base
packages instead.

The instrument passes its own control this time, which is the difference from
last iteration. The same kind of byte search demonstrably reads names out of
this file - 532 of them - so a zero WITHIN ptc_pack is meaningful in a way the
earlier disc-wide zero was not.

Among the 39 three-digit misses the letters cluster hard - j 22, t 14, m 2, h 1 -
and ptc_pack contains just one j name against 149 m and 81 s, so eff_j### is a
third grouping that is almost entirely elsewhere.

Not closed: 66 of the 137 bound effects still have no located home, eff_n0071
among them. But the route is now real and has a number on it, and the next step
is the eff_j### family and the four-digit series rather than another disc-wide
grep.

All sixteen artefacts byte-identical.
2026-08-28 08:55:41 +00:00
Sylpheed RE agent
dd542c23a4 re: the unit family is the only schema on the disc; an .xpr search cannot prove absence
Two exhaustive probes agree on the same 15 records. The six Shift-JIS type words
occur in the six GP_MAIN_GAME_* paks and nowhere else, and a disc-wide sweep of
every parsed record name for a wildcard shape (???, *, ###, NNN, <...>) returns
7 distinct names - exactly the seven already in the schema: Turret_???,
Hatch_???, Bridge_???, Thruster_???, ShieldGenerator_???, Versatile_???, NS_*,
each x6. So the weapon datasheet, the arsenal item and StageResource ship NO
schema; the unit datasheet is the only structure the disc describes to itself.
Wildcard field names are confined to the schema records too - NozzleSpec_???,
NozzleFrame_???, CannonFrame_???, MuzzleFrame_???.

No untyped gaps either. The full residual is 30 slots and every one holds a
sample value rather than a missing type: 28 booleans spelled Yes, plus
Generic.ID = Ship_ and Generic.Type = Vessel. The booleans follow one pattern -
the six destructible part types each carry the same four-boolean core
(IsDestructible, IsInvolved, IsRadarVisible, IsShielded), Turret_??? adds IsAuto,
Generic carries only IsDestructible, and NS_* has its own pair AttenuationAlpha /
AttenuationVolume. 28 + 2 = 30; 7 fully-typed records + 8 with examples = 15.

The third result is a refutation of my own instrument. Testing the four
genuinely-undeclared effects against the 166 .xpr packages put eff_f0002 and
eff_f0002_barn in Base.xpr and found nothing for eff_e0044 or eff_h308 - but the
control kills the negative: eff_n0071, which we measured LIVE as an Explosion
record's ExplosionFxModel, also returns nothing from the same search. A
known-live name the test cannot find means the test has no power here. Only the
positive half counts: eff_f0002/_barn do ship. Nothing follows about eff_e0044
or eff_h308, and the earlier "no mesh" remarks about eff_e0058/_e0059/_e0060 are
weaker than written - absent from a byte search over .xpr is not absent from the
disc. How an effect mesh is actually reached is now the open question, since
eff_n0071 is not a plain name string in any of the 166 packages.

All sixteen artefacts byte-identical.
2026-08-28 08:38:26 +00:00
Sylpheed RE agent
fa8a538c33 re: the disc ships the unit datasheet's own SCHEMA (and 文字列 was not a placeholder)
This corrects the previous commit. 文字列 is not a developer's leftover: it is
one member of a six-word Shift-JIS TYPE vocabulary, and the records carrying it
are a machine-readable schema for the unit datasheet.

The whole non-ASCII population on the disc is 6 distinct values out of 99328 -
0 of 3496 record names and 0 of 12173 field names - and all six are type words:

  文字列              string                366 uses
  NS_"文字列"         NS_ string             12
  整数 / 整数値        integer            66 / 6
  浮動小数値           floating-point value  504
  浮動小数値[0〜1]     float in [0,1]         36

990 type-valued fields. So the reader defect noted last time is real but bounded
to these six strings, and name_hash re-encodes Latin-1 byte-for-byte, so hashing
was never affected.

They sit in 15 records x 6 GP_MAIN_GAME_* paks = 90 instances, i.e. 15 records
with ONE user. The names are exactly the unit substructure family, and six carry
a literal wildcard: Turret_???, Hatch_???, Bridge_???, Thruster_???,
ShieldGenerator_???, Versatile_???, and NS_*. ??? is the numeric-suffix wildcard
at record AND field level - Turret_??? is the schema for Turret_000..00N, and
inside it CannonFrame_??? / MuzzleFrame_??? stand for the numbered slots.

Where a field's type is an enumeration the schema holds an EXAMPLE value instead
of a type name: Yes for the five booleans, Vessel for Generic.Type (the 43
Craft + 71 Vessel split), Ship_ for the ID prefix convention. Maneuver is the
one fully-typed record, 34 of 34. Every ResistanceTo* and every Color_* channel
is declared FLOAT[0..1] - normalised by declaration, matching the sampled values
in unit-datasheet-static. Generic.NozzleSpec_??? has its own type NS_"文字列"
and NS_* is a record, so the nozzle spec is a nested sub-schema.

Control separates schema from data cleanly: the _??? records and NS_* exist ONLY
as schema, 6 of 6 instances typed, while the eight real substructure names are
typed in 6 instances and untyped in the rest - Generic 6 of 3651, the others
6 of 684 each.

Turret_??? carries the game's own typo NomalModel beside DamagedModel.

This gives the port an authoritative field-type table: types the disc declares,
rather than types inferred from sampled values.

New artefact with its regenerator: tools/re-capture/datasheet_schema.py ->
docs/re/data/datasheet-schema.txt. All fifteen existing artefacts byte-identical.
2026-08-28 08:22:36 +00:00
Sylpheed RE agent
44bdbad2d6 re: every bound effect vs every declared effect; a Shift-JIS dev placeholder
The Explosion substructure's field list was already in the corpus, so the open
part was whether the effect names RESOLVE. Declared x used, sweeping 34 binder
field names (*FxModel*, *EffectName, Effect_*, ShellModel, CoverModel,
SilhouetteModel) against every name declared by a LOD_Effect_<n> or
GameModel_<n> field anywhere on the disc:

                 declared   not declared
  used              172          58
  not used          318           -

490 declared, 230 used. Top binders: Effect_Paralyze 2874, ShellModel 996,
HitFxModel 738, JetFxModel_000 408, MuzzleFlashFxModel_Loop 384.

The 58-cell is almost one field. 53 of the 58 are bound by SilhouetteModel
alone and are all rou_f###_wep* names - the arsenal item silhouettes already
documented in arsenal-item-weapon-chain. They are undeclared because they are
the wrong KIND: each resolves as its own standalone package, rou_f001_wep_01.xpr
and friends, never as a LOD-table entry. Nothing is missing; the sweep was
reading an asset-file name as though it were an effect name. That leaves four
genuinely undeclared effects - eff_e0044, eff_f0002, eff_f0002_barn, eff_h308 -
none of which resolves as a record or field name either.

One "effect name" in that cell is not a name at all. Bound by Effect_Explosion
and Effect_Flare, 72 occurrences = 12 users, its bytes are 95 B6 8E 9A 97 F1 -
Shift-JIS for the word "character string". A developer's placeholder. This
exposes a reader defect worth fixing before these strings reach a port:
unitgroup.py hands the value back as Latin-1 mojibake, so an IDXD string field
can carry Shift-JIS and our decode does not know it.

Following the silhouettes into the ISO tree turned up a convention that IS real:
59 of the 166 .xpr packages end _hangar.xpr, and 59 of 59 have a bare twin of
the same stem. The direct contrast to yesterday's refutation, where _all/_child
was 1 of 166 with a single stem. Suffix conventions in this corpus are worth
testing precisely because they are not all real.

Controls reproduced from the previous iteration: eff_n0071 is declared and used
6x (= one user under the per-pak-copy rule); eff_e0033 is declared and used 0x,
sitting in the 318-cell. That cell is expected rather than alarming - the
EnumLODSet/EnumGameModel family is overwhelmingly equipment, which no unit
datasheet binds.

All fifteen artefacts byte-identical.
2026-08-28 08:05:28 +00:00
Sylpheed RE agent
25b5f60ea1 re: _all/_child is not a convention (1 of 166); eff_n0071 is a live explosion
Refuted, and it was my own suggestion from last turn. Across all 166 .xpr
packages the _child suffix occurs in ONE file, Stage_S24.xpr, with exactly one
stem, stg24_04, 20 occurrences. Across the 105394 distinct parsed pak names -
record names, field names and string values - names ending _all or _child occur
ZERO times. So the pair is mesh-internal to a single model and never reaches the
data tables; the tables only ever name the bare stg24_04. n = 1 is not a
convention, and the multi-part mesh mechanism the corpus actually owns is
xbg7-mesh's grouped index/vertex pools, which is a different thing.

The orphan tables' five unique effects split three ways, chased through PARSED
names rather than .xpr bytes:

  eff_n0071                  LIVE - Generic.GameResourceID in DefTables.pak and
                             an Explosion record's ExplosionFxModel in all six
                             GP_MAIN_GAME_* archives (x6 = one user)
  eff_e0033                  a mesh in Base.xpr, but no data reference
  eff_e0058/_e0059/_e0060    neither a mesh nor any reference

So one "abandoned" entry is not abandoned: the orphan LOD tables list an effect
the shipped _test table omits while the game still binds it elsewhere.

Control recorded so the negative has force: effect names bind through a whole
family of fields - Effect_Paralyze (2874 values), HitFxModel (738), ShellModel
(516), JetFxModel_000/001/002, MuzzleFlashFxModel_Loop - so a value that
resolves through none of them really is unreferenced, not merely missed by a
narrow search.

All fifteen artefacts byte-identical.
2026-08-28 07:47:06 +00:00
Sylpheed RE agent
da41f95db1 re: the challenge LOD extras are meshes in that stage's own .xpr - 11 of 11
Perfect diagonal. Each of stg24_01..04, stg26_01..03, stg27_01..02 and
stg29_01..02 appears in Stage_S<NN>.xpr and in no other stage package. So a
challenge stage's bespoke LOD/model table exists to declare the handful of
scenery meshes packaged with that stage, consistent with xbg7-mesh's account of
a stage .xpr as a bag of XBG7 resources.

stg24_04 is a composite: 22 occurrences resolve as 1 bare + 1 _all + 20 _child.
Every other stg* name occurs exactly once.

rou_f004 is NOT in Stage_S28.xpr - it is in DeltaSaber_A.xpr. S28's LOD table
declares the craft but the mesh ships in the player-craft package, confirming
from the ISO side that _A is the f004 variant: the stage package carries
scenery, the craft comes from its own file. rot_n001_break resolves too, in
Tutorial.xpr and Stage_S28.xpr, matching exactly the tables that list it.

The orphan tables' unique effects only partly resolve: eff_e0033 is present in
Base.xpr, but eff_e0058 and eff_n0071 appear in NO .xpr on the disc. So the six
abandoned LOD tables name at least two effects that were never packaged - the
same "declared, never shipped" shape as S14's asteroid meshes, on a much smaller
scale.

Method caveats recorded in the doc: these are byte searches over .xpr files, not
parsed resource-table reads. They establish presence, not an enumeration, and
are acceptable only because the names are long and distinctive and the match was
expanded to the full trailing token. Also grep -c counts LINES, not occurrences;
the counts above come from grep -o | wc -l after that was caught.

All fifteen artefacts byte-identical.
2026-08-28 07:30:37 +00:00
Sylpheed RE agent
d08e464897 re: challenge LOD tables are _test + set dressing; orphan count corrected 8 -> 6
Diffed the field sets, the same move that cracked EnumWeapon_EX5.

The five bespoke challenge tables are EXACT SUPERSETS of _test - 10 of 10 (five
stages x the EnumLODSet and EnumGameModel families) with zero removals. Extras
run +2 to +9 and name the stage's own scenery: stg24_01..04, stg26_01..03,
stg27_01..02, stg29_01..02, plus rot_n001_break shared by S24, S26, S27, S28 and
Tutorial.

S28's nine extras independently confirm the player-craft result:
LOD_Frame_rou_f004, four rou_f004_mnv*/turn180 poses and three eff_j004_*
effects - the LOD table for the one stage that flies rou_f004. Two unrelated
tables agreeing on S28's oddity is a real cross-check.

This does NOT revive "rot_n001 is on the disc": that refutation concerned the
bare name, which still occurs 0 times exactly; only the _break form is real,
exactly as recorded.

Self-correction: 6 orphan tables, not 8. name_hash is case-INSENSITIVE, so _test
and _Test are the same pak entry - EnumLODSet hashes to 3485720498 and
EnumGameModel to 4020329537 for both spellings. The family therefore has 10
distinct keys, not 11 tags, and the unreferenced set is S01, S02, S16 x 2 = 6.

And the six are not stale copies of _test. They are far smaller (120, 129 and
178 EnumLODSet fields against _test's 676) yet each carries 4-5 entries _test
does NOT have: LOD_Effect_eff_e0033, _e0058, _e0059, _e0060, _n0071, and S16
adds five rou_e106_* boss parts matching stage16-boss. So the live table is not
a superset of the abandoned ones. Tutorial has the same shape: 66 fields, 3
unique.

All fifteen artefacts byte-identical.
2026-08-28 07:15:28 +00:00
Sylpheed RE agent
0536e287b5 re: EnumLODSet_test.tbl is the story campaign's live LOD table; S25 premise withdrawn
The question was why S25 is absent from the DefTables +-path families. The
premise is withdrawn: it was an artifact of listing DISTINCT values of
LodEnumTable. S25 is not missing anything - it shares EnumLODSet_test.tbl with
the sixteen story stages, and test is in the list. Counting distinct values
hides who uses them; join per user.

The per-stage join: EnumLODSet_test.tbl / EnumGameModel_test.tbl serve 17
stages - all of S01-S16 plus S25. The five challenge stages S24, S26, S27, S28,
S29 have bespoke tables. Hangar.xpr has no pair at all. 17 + 5 + 1 = 23 objects
carrying a StageResource.

So the test-named table is the live LOD and model table for the whole story
campaign - the strongest case yet of "a test_-named table can be live", serving
17 of the 22 missions.

Eight shipped tables that nothing references: the family covers only 11 tags
(S01, S02, S16, S24, S26-S29, Tutorial, test, Test) and is NOT per stage -
S03-S15, S17-S23 and S25 have none. Seven tags are referenced; S01, S02, S16 and
Test are present in DefTables.pak with zero references disc-wide, i.e. 8 orphan
tables. "Early per-stage LOD sets, abandoned when the campaign was pointed at
the shared test table" is a reading, not a measurement.

Scope note recorded so the family is not mistaken for a stage one:
EnumLODSet/EnumGameModel is overwhelmingly an EQUIPMENT family - 116 of the 130
referenced values are bare Equip_EnumGameModel_wep_NN.tbl names reached from
PlayerLOD, WingmanLOD, PlayerMotion and WingmanMotion (58 distinct each); only
the 14 stage-tagged values use the + path form.

All fifteen artefacts byte-identical.
2026-08-28 07:00:34 +00:00
Sylpheed RE agent
9be52cb2e9 re: the archive.pak+entry dictionary is 20 keys and misses the 2D blocker entirely
A path that names its own archive is a free name->key fact, since a pak TOC holds
only hashes. Swept them disc-wide and aimed the result at the 2D /
GP_READY_ROOM blocker.

Seven fields carry a game: value - BackGroundPackage 18, StageResourcePackage 23,
TBL_3D 6, LodEnumTable 7, MotionEnumTable 7, one PATH, one anonymous - giving 63
distinct paths in two forms: 42 file-form (41 present on the extracted ISO, the
single miss being the bare directory game:\hidden\Resource3D\, which is not a
file) and 21 archive+entry (20 of 20 resolve inside the archive they name; the
21st is sound.pak+ with an empty entry).

The blocker test is a measured zero. The six 2D paks and GP_READY_ROOM hold 1817
distinct keys between them and NONE is named by a + path. The dictionary's total
reach is 20 of 16630 keys, 0.12%, and every one lands in DefTables or
GP_HANGAR_ARSENAL - archives that are already partly named.

The route is exhausted rather than unlucky: these paths exist only in the seven
fields above, and none of those fields ever addresses a 2D or ready-room asset.
The blocker keeps its earlier structural reason (six identical 711-key TOCs, 0 of
711 shared with any other archive).

The full dictionary is recorded: 6 x <lang>\GP_HANGAR_ARSENAL_3D.tbl in
GP_HANGAR_ARSENAL.pak, and EnumGameModel_* plus EnumLODSet_* for S24, S26, S27,
S28, S29, Tutorial and test in DefTables.pak.

Left unexplained: both DefTables families are seven names with S25 absent and no
story stage present at all.

All fifteen artefacts byte-identical.
2026-08-28 06:43:52 +00:00
Sylpheed RE agent
af8292c940 re: the rest of StageResource - two more value kinds, one convention refuted
Swept all 19 StageResource fields by value kind.

Refuted: <X>ID + <X>Package is not a convention. The record has exactly one *ID
field (BackGroundID) and exactly one pairing stem. StageResourcePackage has no
StageResourceID; WingmanIconID_0..3 have no package. n = 1 is not a rule, so
last pass's generalisation is withdrawn.

Fourth kind - the game:\ external path. BackGroundPackage (18 values) and
StageResourcePackage (23) are neither records nor pak keys because they are ISO
file paths. Mapping game:\ to the extract root and \ to /, case-folded, 41 of 41
exist on the extracted disc (hidden/resource3d/BG_Acheron.xpr, Stage_S01.xpr,
...; 166 .xpr files in that directory).

Fifth form - game:\hidden\<archive>.pak+<entry>. LodEnumTable, MotionEnumTable
and TBL_3D name an archive AND an entry inside it, e.g.
game:\hidden\DefTables.pak+EnumLODSet_S24.tbl. Splitting on + and hashing the
right half against THAT archive's keys resolves 20 of 21; the one failure is
sound.pak+ with an empty entry name.

Some of those entry names carry an explicit <lang>\ prefix
(deu\GP_HANGAR_ARSENAL_3D.tbl, eng\, esp\), each present in 0 other archives, so
the language-prefix form is genuinely used by the engine. This does NOT unblock
the 2D naming problem: archive-naming refuted <lang>\ there structurally, since
the six 2D paks share one identical 711-key TOC and cannot carry per-language
names. Both facts stand.

The 4 never-shipped _Test tables - the residual of the 344-name census - are now
named: EnumCharacter_Test.tbl, EnumLocalString_Test.tbl, EnumSquadron_Test.tbl,
StageMessageSet_test.tbl (lower-case test on the last).

WingmanIconID_1..3 hold PGHUD_WING2/WING3/WING4, HUD element names from the PG*
roster; WingmanIconID_0 is never valued.

All fifteen artefacts byte-identical.
2026-08-28 06:28:14 +00:00
Sylpheed RE agent
3da617d04b re: BackGroundID is an identity, not a file name; its sibling names the asset
The one value class that resolved to neither a record nor a file resolves to
neither because it is neither. BackGroundID lives on StageResource (174 records
carry it) directly beside BackGroundPackage, and that sibling holds the real
asset: game:\hidden\Resource3D\BG_<something>.xpr.

My earlier suffix sweep had a real bug: it tried BG_ and .xpr as alternatives,
never as a pair, so BG_Acheron.xpr was never in the search space. A
one-transform-at-a-time sweep cannot find a two-transform name.

But the obvious follow-on hypothesis - "the package is BG_<id>.xpr" - is also
refuted: it holds in only 12 of the 24 StageResource records carrying both
fields. Hargenteen spans six packages (BG_Hargenteen{,_near,_far,_dead}.xpr plus
BG_Acheron.xpr and BG_Stg26.xpr), and BG_Acheron.xpr serves both the Acheron id
(S09) and the Hargenteen id (S25). The relation is many-to-many in both
directions, roughly 18 distinct packages across 10 ids.

So the id is a logical place - Acheron, Earth, Hargenteen, Lebendorf - and the
package is the mesh actually loaded, chosen per stage from that place's _near /
_far / _dead variants. Two details fall out: PD is the Prometheus Driver
(BG_P_Driver.xpr, on S16, the boss stage), and the challenge stages that
re-dress a story sky carry bespoke BG_Stg24/26/27.xpr packages while keeping the
story stage's id - the same borrow-and-re-dress pattern already recorded for
maps and objectives.

Not settled: Original carries no BackGroundPackage at all; whether that means no
sky or an engine default is unknown.

All fifteen artefacts byte-identical.
2026-08-28 06:11:41 +00:00
Sylpheed RE agent
aa60d14ae7 re: value classes resolve by KIND; the 344-name residual drops from 31 to 4
Generalised the "does the referent exist?" sweep that cracked S14. The value
classes resolve as three different kinds:

  AIID                   31 values   31 records    0 files    0 unresolved
  FormationID           126 values  126 records    0 files    0 unresolved
  CollisionMeshes        18 values    0 records   18 files    0 unresolved
  EnumerateSubobjective  17 values    0 records   17 files    0 unresolved
  NamePlate              52 values   27 records   24 files    1 (empty string)
  BackGroundID           10 values    0 records    0 files   10 unresolved

FormationID 126/126 re-derives unit-group-table's own "every FormationID
resolves" from a different direction, as the control.

The 27 NamePlate values that stage-definition-table called "non-file plate ids"
are record names - ACROPOLIS x114, NP_ArrowHead x84, NP_ASFrigate x78 - and the
split 24 files + 27 records + 1 empty = 52 matches the two-vocabularies count
already recorded there. So the census residual shrinks: 313 resolve as files
plus 27 as records = 340 of 344, leaving the 4 never-shipped _Test tables as the
entire residual. The old reading was a KIND error, not a missing-file error.

BackGroundID is a genuinely unresolved third kind: the ten sky names, used 6-36
times each, are neither record names nor pak keys under 10 prefixes x 9 suffix
forms.

Recorded as powerless so it is not re-run: the image holds 0 exact strings for
all ten backgrounds, but also 0 for Formation_4_Bird, AI_Structure,
NP_ArrowHead, ACROPOLIS and CollisionSet_S03.bin, every one of which does
resolve in data. The executable names no data value at all, so the negative
discriminates nothing.

All fifteen artefacts byte-identical.
2026-08-28 05:55:52 +00:00
Sylpheed RE agent
5ec24b8ef9 re: S14's 13 are dangling deployments - the unit chain is absent for them
Two measurements settle the last open cell of the unit partition.

The deployment side is identical. S14's asteroid-cmesh squadron records match a
declaring stage's field for field - same squadron ids (GNN901, GNN902, ...) and
the same Count 1 / SideID Neutral / AIID AI_Structure / FormationID
Formation_1_only / DisableInterval No. Nothing about how S14 deploys them
differs, and these are the same kind of record compared like for like.

The definition side differs completely. Per stage, deployed / declared /
Generic-with-HP / any Generic: S01 8/8/8/8, S04 10/10/10/10, S05 5/5/5/5,
S08 6/6/6/6, S13 4/4/4/4 - 33/33/33 for the five - and S14 p1 9/0/0/0,
S14 p2 4/0/0/0.

So the 13 are not a manifest omission: they have no unit definition anywhere on
the disc, not even an un-HP'd Generic row. UnitGroup_S14.tbl names entities that
do not exist - 13 dangling references. That rescues the law (nothing is
deployed-without-being-loaded in any working sense) and explains why the gap is
exactly the cmesh class and nothing else in S14.

What it predicts needs the emulator: either those collision meshes fail to
appear in Stage 14 - a shipped bug - or another route supplies them. Flying S14
and checking whether the large asteroids collide distinguishes the two; no
static route can. Recorded as NEEDS-HUMAN.

Also recorded so the two asteroid populations are not conflated: asteroid-fields'
AsteroidGroup_00N tables scatter mob_n0NN models, while these are
UN_<stage>_Asteroid_cmesh_* squadron entities. S28.Phase_1 borrows
S14_p2_asteroid.tbl but deploys no cmesh of its own, so the borrow does not
carry the dangling references.

All fifteen artefacts byte-identical.
2026-08-28 05:39:44 +00:00
Sylpheed RE agent
f0dfff4fcd re: EnumUnit_S<NN> is the stage's LOAD MANIFEST; all 9 never-deployed units named
Joining each stage's own EnumerateUnit table to its own EnumerateSquadron table
is far sharper than the global partition: declared == deployed EXACTLY in 20 of
22 stages. S16 declares 4 it never deploys; S14 deploys 13 it never declares.
Every per-stage deployed-but-undeclared count is 0 except S14's 13, and that 13
is the entire disc-wide total - so the exception is one stage, not a scattering.

S16 explains what the table is. It declares 6 and deploys 2, and two of the six
are UN_e901_ADAN_Boss and UN_e910_core_ADAN_GeneratorCore - exactly the units
stage16-boss shows are brought in by their own loader chain (sub_82315AE8),
not by the squadron roster. So EnumUnit_S<NN> is the stage's load manifest, not
a mirror of the roster: a unit that code spawns is declared but never deployed.

That also closes the 9 declared-but-never-deployed units by name: the S16
code-spawned pair above, Test_ADAN_PrometheusDriver_InsideP2_01, and six
Ship_Test* placeholders.

S14 remains the one unexplained cell, and it is not a missing table:
EnumUnit_S14.tbl exists and declares six names, one of which is
UN_mn040_Asteroid_Big. S14 therefore declares an asteroid but not the 13
Asteroid_cmesh entities it deploys, which refutes "asteroids are exempt from the
manifest". Whatever the cause, it is specific to the collision-mesh entities.

All fifteen artefacts byte-identical.
2026-08-28 05:24:09 +00:00
Sylpheed RE agent
0ae295e9eb re: the weapon law does not generalise to units; the whole exception is S14
Applied the declared x deployed partition to units. Three vocabularies had to
be separated first: declared = field names of the 31 EnumUnit records (118);
deployed = slot 0 of each UnitGroup member tuple (122); datasheet = Generic
records carrying HP (114, which re-derives unit-datasheet-static's own number
from a different direction, as a control). Declared and deployed share the UN_*
vocabulary and overlap on 109; the datasheet's IDs are a third naming, disjoint
from both.

                    deployed   not deployed
  declared            109           9
  not declared         13           1

The cell that is empty for weapons (0 mounted-but-undeclared of 131) holds 13
for units, so the general form of that law is refuted: a unit CAN be deployed
with no table declaring it.

But the 13 are one coherent family - every one is UN_S14_p1_Asteroid_cmesh_* or
UN_S14_p2_Asteroid_cmesh_*. Asking the other population settles it: S01 8/8,
S04 10/10, S05 5/5, S08 6/6, S13 4/4 declared - 33 of 33 - while S14 declares
0 of 13, and non-asteroid deployed-but-undeclared is 0. The law survives in a
narrower form: every deployed unit is declared, except S14's asteroid collision
meshes.

Why S14 differs is not settled. It is the stage whose asteroid table S28
borrows, but nothing measured connects the borrow to the missing declarations,
so that is recorded as a coincidence rather than a cause.

All fifteen artefacts byte-identical.
2026-08-28 05:07:35 +00:00
Sylpheed RE agent
074c0296af re: every weapon placed (declared x mounted, 131); a fourth undeclared table
Partitioned all 131 Weapon.ID records against the two data routes by which a
weapon is ever named - a field name in an EnumWeapon record, and a Turret_NNN
record's WeaponID value:

                    mounted   not mounted
  declared            105         22
  not declared          0          4

105 + 22 + 0 + 4 = 131, with no unexplained cell. Both marginals re-derive
unit-substructure-records' own numbers independently: mounted 105, and
22 + 4 = its "26 weapons no turret mounts".

The empty cell is the result: nothing is mounted without being declared, 0 of
131, so declaration is the superset of mounting. The four undeclared and
unmounted records are therefore reachable by neither route. Each appears
exactly once per pak copy (x6) and only as a Weapon record's ID value; the live
control Weapon_TCAF_DeltaSaber_Beam appears x18 as an EnumWeapon field name and
x228 as a Turret_NNN.WeaponID. That places Weapon_TCAF_DeltaSaber_Laser_Ttrl,
the weapon this pass set out to find, and unifies all four as orphan datasheet
rows.

Recorded as powerless rather than as evidence: the image holds 0 exact strings
for the four, but also 0 for the live control and 0 for Weapon_NULL. Only three
Weapon_-prefixed strings exist at all and 0 contain Ttrl - the executable names
no weapon, so the negative does not discriminate.

New: sweeping EnumWeapon RECORDS rather than the three declared tables gives 130
distinct field names, not 127. The extra three come from a fourth record, pak
key 305595319, resolved as EnumWeapon_TestS01.tbl, carrying Weapon_TestS01_Gun/
_Laser/_Missile. It dangles at both ends - no stage declares it, and its three
weapons are the only declared names with no Weapon.ID record. 130 - 3 = 127
restores the previous pass's union.

All fifteen artefacts byte-identical.
2026-08-28 04:50:35 +00:00
Sylpheed RE agent
a304df1e50 re: _EXn is the n-th challenge stage; the Extra0n join is refuted
Reading the docs first paid: unit-substructure-records already had
UN_e001_ADAN_Elan_EX4 and arsenal-item-weapon-chain already had _T_EX5_el, so
last pass's "the only two _EX5 names on the disc" was too narrow. Withdrawn and
replaced by a census.

Every name ENDING in _EX<digit> across all 41 archives, taken from parsed IDXD
record names, field names and string values (never a raw byte scan): 26 names,
two digits only - _EX4 (7 units, 6 ADAN + 1 TCAF) and _EX5 (4 TCAF units + 1
weapon), each with a UnitName_/WeaponCannonName_ twin.

The stage join is exact. _EX4 appears only in tables S27 declares
(EnumUnit_S27.tbl, UnitGroup_S27.tbl); _EX5 only in S28's (EnumUnit_S28.tbl,
UnitGroup_S28.tbl, EnumWeapon_EX5.tbl); the other 20 stages have neither. The
challenge missions are S24-S29 in order, so S27 is challenge #4 and S28 is
challenge #5: _EXn names the n-th challenge ("EXtra") mission's bespoke
variants. S28 declares both schemes at once - EnumUnit_S28.tbl by stage number,
EnumWeapon_EX5.tbl by challenge index, for the same mission.

Refuted: _EXn is not the Extra0n numbering. There are four Extra slots but _EX5
exists, and under that reading Extra04 would be S29, which has no _EX assets.

Makes available but does not settle result-screens' open "what does EX_ mean":
"the challenge-mission debriefing" fits its evidence that the EX_ twin drops
overview_rank/overview_medals. Recorded there as a reading, not adopted.

All fifteen artefacts byte-identical.
2026-08-28 04:34:11 +00:00
Sylpheed RE agent
ad03991fa1 re: EnumWeapon_EX5.tbl adds exactly one weapon; a tutorial expectation refuted
Diffed S28's bespoke weapon table against the common one. The disc has exactly
three weapon tables, and each is a single EnumWeapon record whose FIELD NAMES
are the weapons - the Enumerate-style declaration shape again:

  EnumWeapon_Test.tbl       126 names
  EnumWeapon_EX5.tbl        127 names = the same 126, same order, plus one
  EnumWeapon_Tutorial.tbl     9 names = a strict subset of Test
  union                     127

The whole of S28's bespoke table is one extra weapon,
Weapon_TCAF_Ship_AAGun_EX5. Zero removals, identical order on the shared 126.

That explains one of weapon-struct-runtime's five "never instantiated" disc
records: the AA-gun variant is declared by EnumWeapon_EX5.tbl only, and that
table belongs to S28 alone, which the captured save was not playing.

It also refutes that document's expectation that "running the tutorial should
instantiate the _Ttrl pair". It will not: EnumWeapon_Tutorial.tbl's nine names
are Weapon_NULL, Weapon_ADAN_Puppy_NoseGun, Weapon_DSaber_P_wep_{01_Beam,
02_Missile,58_Laser} and Weapon_TCAF_DeltaSaber_{Beam,Cannon,Missile,
ASMissile} - neither _Ttrl weapon is among them. Four of the five
uninstantiated records are declared by no weapon table on the disc at all.

EX is still not identified but narrowed hard: the entire content of S28's own
weapon table is a single _EX5 AA-gun variant, and the only other _EX5 name on
the disc is UN_f001_TCAF_DeltaSaber_T_EX5.

All fifteen artefacts byte-identical.
2026-08-28 04:17:15 +00:00
Sylpheed RE agent
13ac9ccc29 re: every challenge mission is flown on a story mission's map
Applied the per-stage join to the other stage-resource fields. Of 338 distinct
(field, value) pairs across the 22 stage objects, 31 are used by more than one
stage. The strongest signal is CollisionMeshes:

  S24  CollisionSet_S11.bin  SUBObjectiveSettings_S01  Anastasis         Test
  S25  CollisionSet_S07.bin  SUBObjectiveSettings_S07  Hargenteen        Test
  S26  CollisionSet_S03.bin  SUBObjectiveSettings_S06  Hargenteen        Test
  S27  CollisionSet_S03.bin  SUBObjectiveSettings_S03  Planet_Lebendorf  Test
  S28  CollisionSet_S14.bin  SUBObjectiveSettings_S01  Lebendorf         EX5
  S29  CollisionSet_S15.bin  SUBObjectiveSettings_S15  Earth             Test

6 of 6 challenge missions reuse a story stage's collision set. Control: S01-S16
are perfectly diagonal, CollisionSet_SNN <-> SUBObjectiveSettings_SNN, 16/16.

Map, objectives and sky are borrowed independently - S24 flies S11's map with
S01's objectives; S26 flies S03's map with S06's objectives under a Hargenteen
sky belonging to neither; S28 flies S14's map with S01's objectives under S01's
sky. Three of six mix, so "S24 is S11" would be wrong.

S28 is the only stage with its own weapon table: 21 stages use
EnumWeapon_Test.tbl (x132), the tutorials EnumWeapon_Tutorial.tbl (x36), and S28
alone EnumWeapon_EX5.tbl (x6). S28 was already the odd stage - the only rou_f004
flight - so this is a second hook on it.

A datapoint for result-screens' open "what does EX_ mean": EX5 is also a variant
suffix on real names - UN_f001_TCAF_DeltaSaber_T_EX5 and
Weapon_TCAF_Ship_AAGun_EX5, 24 each. Not enough to fix the meaning.

Method note: an unanchored EX[0-9] search over binary payloads returns EX0..EX9
at 100-180 hits each - pure noise from matching inside compressed data. Only
prefix-anchored names are real.

All fifteen artefacts byte-identical.
2026-08-28 04:00:52 +00:00
Sylpheed RE agent
168bebc700 re: S28 does have an asteroid definition - it borrows Stage 14's
Withdrawing my own repeated claim that S28_p1 has an asteroid volume with no
definition. It came from a set-difference over FILE NAMES, which cannot see
reuse. Joining the fields per PHASE settles it:

  S28.Phase_1   MapMesh = S28_p1_AsteroidVolume_wp.col
                AsteroidDefinition = S14_p2_asteroid.tbl

S28 scatters Stage 14 phase 2's rock list inside its own volume. Nine
definition tables and nine _AsteroidVolume_wp meshes, with one table shared by
two phases: S14_p2_asteroid.tbl -> S14.Phase_2 and S28.Phase_1.

The earlier census's own arithmetic already contained the answer: the four
values counted x12 (2 per language pack) were S01, S04, S05 - two phases of ONE
stage - and S14_p2, which is two phases of TWO different stages. The count was
right; reading x12 as "two phases of the same stage" was the error.

test_s8p1_asteroid.tbl is not test-only either - it is what S02.Phase_1 ships
with. A test_ name here is a leftover, not a dead table.

The true residual is two phases and it is the opposite pairing to what I
recorded: S01.Phase_3 has the volume and no definition (S01 keeps its volume
across all three phases but scatters rocks only in 1-2), and S02.Phase_1 has a
definition and no volume (MapMesh is the plain mapmesh_box_500km).

All fifteen artefacts byte-identical.
2026-08-28 03:44:14 +00:00
Sylpheed RE agent
524e032542 re: the 2D/GP_READY_ROOM blocker - retry already done, two new structural facts
Item: retry the blocker with the "wrong prefix" lesson from
StageMessageSet_S02.tbl. The retry was already run and the owning doc says so:
archive-naming's "13 name transformations" list already includes 2d\, eng\,
hud\ and GP_MAIN_GAME_2D\ prefixes, every one scoring 0 against both the E2D
pak and all 16630 entries. Reading the doc end to end first would have shown
that in a minute. Recorded so the idea is not proposed a third time.

What the re-open did add - two measurements the earlier passes did not make:

1. The six 2D archives share ONE key set: 711 keys, identical in all six.
   Intersection = union = 711; 4266 entries, 711 distinct keys. The entry names
   are language-independent - the language lives in the pak file name, not the
   entry path. That refutes any <lang>\ prefix hypothesis structurally, not
   merely by a zero score: if the names carried a language directory the six
   key sets would differ.

2. The 2D key space is disjoint from the rest of the disc - 0 of 711 appear in
   any non-2D archive, while disc-wide 2010 of 16630 keys are shared between
   archives. Sharing is common; this set never does it.

The six named GP_READY_ROOM entries are all fonts - deu\GOTHICB.TTF, eng\,
esp\, fra\, ita\ likewise, and jpn\HGRGE00.TTF. The doc recorded the count "6
named" without saying what they are: localisation fonts named from elsewhere on
the disc, not a sample of the archive's own artwork vocabulary, so they cannot
seed the dictionary attack the doc names as the last lever. Only 2 of 1106
GP_READY_ROOM keys appear in another archive.

Still blocked, now for a sharper reason: the 2D names are language-independent,
unshared, and absent from disc and executable alike.

All fifteen artefacts byte-identical.
2026-08-28 03:29:37 +00:00
Sylpheed RE agent
9bf1973e27 re: every declared table name censused; NamePlate holds two vocabularies
From last iteration's n=1 control (EnumSquadron_Test.tbl matched no pak key),
censused properly: every declared value disc-wide - the eleven Enumerate*
fields plus MessageSet, NamePlate, CollisionMeshes, MotionEnumTable,
LodEnumTable, AsteroidDefinition, MapMesh, MapPath - against the 16630 pak keys
under the sixteen path prefixes.

  distinct declared values                        344
     resolve to a pak object                      313
     do not                                        31
        NamePlate values that are not file names   27
        _Test tables that were never shipped        4

313 + 27 + 4 = 344. Every family is complete (EnumerateUnit 29/29,
EnumerateFormation 29/29, EnumerateNullFrame 29/29, EnumerateAIParams 23/23,
CollisionMeshes 18/18, AsteroidDefinition 9/9, MapMesh 11/11, MapPath 11/11,
LodEnumTable 7/7, MotionEnumTable 7/7, EnumerateSubobjective 17/17,
EnumerateWeapon 3/3) except the four below.

NamePlate carries two disjoint vocabularies, split by owning record.
StageResource.NamePlate (174 uses) holds file names - nameplate_S01.tbl etc,
24 distinct, 24/24 resolve. Generic.NamePlate (522 uses) holds bare plate ids -
NP_Cyclops, NP_Gargantua, ACROPOLIS, OTHERENEMY - 27 distinct, 0/27 resolve,
none with a dot. Zero crossover, so the 27 "missing" are a category error.

The four genuinely dangling names are all the template's own -
EnumCharacter_Test.tbl, EnumLocalString_Test.tbl, EnumSquadron_Test.tbl,
StageMessageSet_test.tbl - and in each of those four families the count is
n-1 / n with the _Test entry the sole miss. Every non-_Test declaration
resolves.

Closed a corpus question on the way past: stage-mission-tables recorded
StageMessageSet_S02.tbl as "not in GP_MAIN_GAME_E.pak under that name (?,
probably localised elsewhere)". It is there - name_hash("message\" +
"StageMessageSet_S02.tbl") = key 705319170, in all six packs. The stage family
uses stage\; the message tables use message\.

All fifteen artefacts byte-identical.
2026-08-28 03:13:21 +00:00
Sylpheed RE agent
e222016dfc re: the Enumeration self-index is not a naming route, and the residual is two _Test variants
The tempting move - 360 objects state their own contents, so use that to name
them - does not work, and it is now measured rather than assumed:

  objects carrying an Enumeration record                       360
     key already named by archive-naming routes 1 + 2          348
     still unnamed                                              12
     newly named by name_hash(prefix + own record name + ext)    0

Sixteen prefixes x seven extensions x every record name of every unnamed
object: zero gained. A self-index names an object's RECORDS; a TOC key is
name_hash(path + file name). The two vocabularies never meet.

The residual is exactly two objects x six packs, each byte-identical across the
packs and each an unnamed VARIANT of a named _Test table, not a duplicate:

  2390212806 (1493 B)  Placement_00..02 + RouteTest_00..01 - a trimmed
                       Stage\Null_Test.tbl (3121167452, 27545 B, same five
                       records plus four Frame_Test_Asteroid_*)
  2719765792 (14124 B) Formation_100, Formation_Fleet_01, Formation_Fleet_02 -
                       the same three record names as Formation_Test.tbl
                       (3463590559, 14025 B) but a different payload

That answers the previous iteration's question about the six-record
Placement/RouteTest sibling: it is Null_Test.tbl minus the asteroid frames.

Independent confirmation for stage-mission-tables: name_hash(prefix +
"Formation_Test.tbl") -> key 3463590559, whose records are exactly those three,
so the two frame-ratio exceptions do live in the file it names.

Dangling link recorded, not fixed: idxd-container.md references
idxd-unnamed-keys.md twice and that file does not exist on this branch - it
belongs to auto/idxd-unnamed-keys.

One control missed unexplained: EnumSquadron_Test.tbl matches no pak key under
any of the sixteen prefixes. n=1, not chased.

All fifteen artefacts byte-identical.
2026-08-28 02:57:20 +00:00
Sylpheed RE agent
027e6bb7a7 re: Placement_*/RouteTest_* are Null_Test.tbl, and Enumeration is a self-index
The template object is stage\Null_Test.tbl: its key hashes as
name_hash("stage\" + "Null_Test.tbl"), the file stage-definition-table names as
EnumerateNullFrame. Ten records - 4 Frame_Test_Asteroid_01..04, 3
Placement_00..02, 2 RouteTest_00..01, Enumeration - and the nine carrying a
FrameCount are exactly the "9/9 for Null_Test.tbl" stage-mission-tables already
counted. The corpus knew the file and the count, not the record names. A second
object (the same five plus Enumeration, without the asteroid frames) matched
none of ten candidate names across sixteen prefixes.

Enumeration is a record that indexes its own object. Disc-wide: 7750 IDXD
objects, 360 carry one, and in 360/360 its field names equal exactly that
object's other record names - zero subsets, zero strays. Field/record counts
run from 2 past 25 ((3,3)x36, (9,9)x24, (13,13)x24, (2,2)x24, (25,25)x18,
(16,16)x12), every pair on the diagonal.

Enumeration vs Enumerate is one letter and opposite scope: Enumerate is
archive-naming's route-2 seed, a single-record object whose fields name OTHER
objects; Enumeration names the records of its own object. Written up in
idxd-container, which had left "an object's kind is known only from the caller
that loads it" open.

Sharper version of the previous correction: I credited the stage\ prefix to
archive_naming.py, but stage-mission-tables states it in prose ("The prefix is
stage\ - name_hash is case-insensitive") and its stagetbl.py examples pass
'stage\AIParams_S02.tbl'. Noted in asteroid-fields.md.

All fifteen artefacts byte-identical.
2026-08-28 02:36:20 +00:00
Sylpheed RE agent
eac6759f25 re: the eight-value frame censused disc-wide, and my novelty claim withdrawn
Grepping FrameCount first would have saved the previous iteration an overclaim.

Withdrawn: the 8-value layout was not a discovery. stage-mission-tables already
documents Route_* records as (time, quat.x,y,z,w, pos.x,y,z) with
len(fields) == 8*FrameCount + 1 holding 1449 of 1449, and unit-group-table
already documents 1 + 8*FrameCount for formation slot lists. I re-derived the
shape from the asteroid tables and called slot 0 an "index" - it is the
corpus's time slot. Corrected in asteroid-fields.md. What the asteroid pass did
add is the norm test.

The census the corpus was missing: both prior statements were measured on one
table family each. Sweeping every IDXD record on the disc carrying a
FrameCount - 10986 records, 10974 with anon == 8 * FrameCount, and the entire
residual is 12 records: Formation_Fleet_01 (ratio 136) and Formation_Fleet_02
(ratio 4), six each, one per language pack. The two exceptions the doc already
names are the only two on the disc.

The quaternion is now measured, not asserted: over 78762 frames in five
families, |q| is within 1e-4 of 1.0 on 78762 / 78762.

Slot 0 is time only where something moves. It equals the running ordinal
0..N-1 in every formation (2196/2196), asteroid (384/384), placement (36/36)
and test-route (24/24) record, and in only 6228 of 8334 real routes - the one
family that stores true timestamps there.

Two record families new to the corpus: Placement_00..02 and RouteTest_00..01,
sharing a six-record template object whose sixth record is named Enumeration,
two such objects per language pack.

All fifteen artefacts byte-identical.
2026-08-28 02:20:15 +00:00
Sylpheed RE agent
1f0f90e304 re: the 8 values per asteroid are index, quaternion, position
Measured over every Frame_*Asteroid* record on the disc - 384 records, 37518
items (6253 per language pack x 6; the remaining 380 per pack belong to the
seven record-less Alpha frames, the same 57-vs-64 split as before).

  slot 0    equals the running index 0..N-1   37518 / 37518, max 587, and the
            largest FrameCount is 588, so the last index is N-1
  slots 1-4 a unit quaternion                 37518 / 37518 within 1e-4 of 1.0
            (min 0.999999, max 1.000001)
  slot 4    sign of the real part             min +0.001, never negative -
            canonical w-last, so the order is (x, y, z, w)
  slots 5-7 position in metres                -163858..+79572, -114308..+76422,
            -105224..+110263 - inside the +/-250 km half-extent of
            mapmesh_box_500km

len(anonymous fields) == 8 * FrameCount on 384 of 384, zero mismatches.

The unit-norm test is the control: an arbitrary 4-tuple has no reason to sit on
the unit sphere and every one of the 37518 does. Two further cross-checks fell
out - the maximum index is exactly max(FrameCount) - 1, and every position lies
inside the 500 km box the corpus already knows (world unit = 1 metre).

There is no scale slot: an asteroid's size comes from its group's
AsteroidModelName, which is why a group is one model plus one placement list.

Not settled: whether the quaternion is object->world or world->object and its
handedness - a norm test cannot separate those; and why S28_p1 has an asteroid
volume with no definition.

All fifteen artefacts byte-identical.
2026-08-28 02:03:23 +00:00
Sylpheed RE agent
863a88dbf3 re: EnumAsteroid counts the rocks, and the hash join was mine to fix
EnumAsteroid is the frame record's FrameCount - 57/57, zero mismatches. Each
AsteroidFrameName is itself an IDXD record name; those records carry one named
field, FrameCount, plus a run of anonymous ones, and the anonymous fields
number exactly 8 x FrameCount on all 384 occurrences (ratio histogram
{8.0: 384}). The frame record is the placement table, 8 values per asteroid,
and EnumAsteroid counts the rocks. The frame-name -> EnumAsteroid map is
single-valued for all 64 names.

mission-wave-arrivals already met these records - they were the outliers that
broke its route-speed measurement, filtered out by name. regn-map-grid already
guessed the link and said so honestly ("a reading, not a measurement; nothing
here counts objects"). Neither says the count is exact.

The abandoned table lost its placement data too: exactly 7 of 64 frame names
have no record, and they are exactly the seven groups of the unreferenced
tenth object (Frame_Alpha_S01_Asteroid_{01,02,04}, Frame_Alpha_S01_Haze_
{01,02,03}, Frame_TAlpha_S01_Asteroid_03). 57 + 7 = 64, zero partials.

Withdrawing the previous commit's red flag: the hash join works and my call
was wrong. AsteroidDefinition values resolve to their objects' pak entry keys
as name_hash("stage\" + name), 9 of 9, each landing on the object the
frame-prefix join independently predicted. I had hashed the bare file name;
archive_naming.py hashes prefix + name over a path-prefix list. Two
independent joins, 9/9 agreement, both leaving the same tenth object
unreferenced.

Not settled: what the 8 values per asteroid are; why S28_p1 has a volume and
no definition.

All fifteen artefacts byte-identical.
2026-08-28 01:48:52 +00:00
Sylpheed RE agent
c53da9ea2a re: the asteroid-field tables (AsteroidGroup_00N) decoded
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<NN>[_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<Thing> is a COUNT; Enumerate<Thing> 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.
2026-08-28 01:32:52 +00:00
Sylpheed RE agent
9480f0aa8a re: the roster's two vocabularies, and a prefix match I mistook for a hit
Correction to the previous commit's own result. The raw byte sweep reported
rot_n001 "26 times in 7 archives" - a SUBSTRING count. The exact field value
occurs zero times, in every IDXD record and every non-IDXD payload. What the
disc carries is rot_n001_break x14, LOD_Frame_rot_n001_break x7,
GameModel_rot_n001_break x5. rot_n001 exact is not on this disc. A prefix
match is not an exact match, and a byte sweep is the tool that breaks it.

rou_e202 does exist, in the other vocabulary: a Model field of a Generic
record, six times, one per GP_MAIN_GAME_<lang> pack, with no GameResourceID -
which is why the GameResourceID census called it absent.

Set arithmetic over every IDXD record: roster 40, Generic.Model 46,
GameResourceID 480; roster n Model 34, roster n GID 38, roster n both 33.
38 + 34 - 33 = 39, so 39 of 40 are one or the other and rot_n001 is the sole
exception. The six that are GID but not Model: mob_n040..043 are the four
AsteroidGroup_00N.AsteroidModelName values (asteroid models, not units) and
rou_e004 is a Level_0.Model.

What selects the 40 is still open, but two answers are dead: not the
Generic.Model set (34 of 46), not the GameResourceID set (38 of 480). The 12
Generic.Model values outside the roster include rou_f002 and rou_f004 - the
player's own craft for S7-S16/S24-27,29 and for S28 - while rou_f001 is kept,
so "the playable models" is dead too.

All fifteen artefacts byte-identical.
2026-08-28 01:15:38 +00:00
Sylpheed RE agent
692a8045f0 re: the 60 duplicated functions are static-object constructors
Each of the 60 builds a 40-element array of 24-byte string objects: addis
0x820B + addi fetches the literal, addi r30,r11,12944 fixes the array
(0x828E3290 for the first), bl sub_8217FA08(dest, literal, -1) assigns, the
rest of the object is zeroed, addi r3,r30,24 steps on.

Why they have no callers: each appears exactly twice in the image - once in
the .pdata unwind table, where EVERY function appears as (address, prolog/
length word) and reading it as a registry is a trap - and once as a slot of a
null-terminated 182-entry function-pointer array at 0x82870018-0x828702EC,
170 of whose targets have zero direct xrefs. That is the static-initialiser
list. Zero callers here means "static ctor", not "dead code".

The teardown side confirms it: 39-43 functions sit between consecutive clones
(median 40), the dominant size in 0x8281xxxx-0x8284xxxx is 11 instructions
(2280 of them, about 60 x 40), and three consecutive ones call sub_823F3D68
on 0x828E3290, +24, +48 - one destructor thunk per element, same stride, same
global, all zero-xref.

rot_n001 and rou_e202 ARE on the disc: a raw byte sweep finds rot_n001 26
times in 7 archives (DefTables included) and rou_e202 6 in 6, exactly one per
GP_MAIN_GAME_<lang> pack. Controls: rou_e201 151/8, rou_e901 917/8, mob_n500
46/8. Narrows the earlier claim to "not GameResourceID values".

Not settled: what reads the arrays. An exact-address search returns 2347
functions outside the 60 - too broad to be a consumer list; the globals sit in
a dense data region and that route has no power.

All fifteen artefacts byte-identical.
2026-08-28 00:59:34 +00:00
Sylpheed RE agent
105ccab038 re: the base-solver's confidence axis was inverted
The remaining named false-positive mode - "107 rows solve to a 64K-boundary
base, a bare addis with no addi of its own, so any scatter of displacements
votes for it" - is refuted by its own measurement.

New positive test in the tool: simulate lis/addis rD,r0,HI + addi rD,rA,N +
or rD,rA,rA forward through each row's function and ask whether the solved
base lands in the solved register.

  64K-boundary bases ("low confidence") : 107 / 107 confirmed
  non-zero low half ("trustworthy")     :   8 / 154 confirmed

A round base is the case where the compiler needed no second instruction, so
`addis r11, r0, 0x820B` stands in the code in full. A miss on the other class
is silence (base built in the caller or loaded from memory), not refutation.

Control: every row the corpus independently validated against the disc has a
64K-boundary base - debriefing, career, save, leaderboard, the 205-name PG*
HUD roster, material slots, the S16 boss collision/frames/motions and its
loader. 13 rows over 10 functions. The dense-short-string false positives the
corpus did name (r31 = 0x8202xxxx) all sit in the "trustworthy" class.

The 0x820B0000 cluster is DUPLICATION, not error: 60 of its 82 rows are one
function emitted 60 times, exactly 491 instructions each, two instructions
differing (both global data pointers), identical 41-address string sequences.
40 resource names written into a per-copy global via sub_8217FA08 at 24-byte
strides. 38 of the 40 are disc GameResourceID values (480 distinct); rot_n001
and rou_e202 are not, and no disc GameResourceID uses the rot_ prefix.

Artefact diff 13/4, confined to the replaced section; the 261-row table and
the 64K histogram untouched; byte-identical on a second run. Fourteen other
artefacts byte-identical.
2026-08-28 00:42:46 +00:00
Sylpheed RE agent
bb6fb9024b re: r0 is not a base register - the last shortlisted row withdrawn
sub_825F2CF0 / sub_825F2F88 (r0, 30 names each, 97% disc names) are not
name-block readers at all: they are the compiler's VMX save/restore helper
pair. 145 instructions each, 72 differ, every difference stvx<->lvx over
v14..v127.

Root cause: in `addi rD, rA, N` the rA slot reads as literal zero when it
names r0, so `addi r11, r0, -N` is `li r11, -N` - vector spill offsets, not
displacements. The solver excluded r1 only; the bogus base landed inside the
stage-settings name block, dense enough to "resolve" 30 real field names.

name_block_bases.py now skips r0 as well. Rows 277 -> 261, functions 190 ->
176, non-64K bases 170 -> 154, data-table rows 53 -> 50; 154+107=261 and
277-16=261. Artefact diff -100/+3, every removed row line carries r0, control
sub_82341A20 r30 = 0x82088F94 217/226 untouched.

Nothing lost: all 30 names are a strict subset of sub_8230D1F8 r29 (129
names), the stage-settings loader already in the corpus. String-xref join
agrees - FinalPassBG, FogMinDistance, ScreenColorR, ExposureKey_BeginValue
each exist once with exactly one xref, from sub_8230D1F8.

Fourteen other artefacts byte-identical; name-block-bases.txt byte-identical
on a second run.
2026-08-28 00:21:26 +00:00
Claude (auto)
aeec0dab8f re: the leaderboard screen, and a Detail_* prefix trap
sub_8219E560 r11, censused against tables.pak exactly like the result screens.
444 instructions, one call site (sub_821A2A80), 100 % of its names are tables.pak
names.

The 18 names partition exactly: 13 record names + 5 field-only names.  The 13 are
the 12 Detail_* panel elements plus MISSIONS -- a 0-field record already owned by
isl-condition-builtins.md and challenge-mission-gate.md, so not new.  The five
field-only names are the screen's parameters: DETAIL_TITLE, one field in each of
six mode records (ScoreAttack, TimeAttack, Extra01..Extra04), and
Detail_Board_{Permanent,Monthly,Friend,Self}, four fields of CHIPS -- the board
scopes.

All 12 Detail_* record names are in the code block, zero residual -- the opposite
of the boss motions, where the block was a 25-of-55 subset.  The panel is Rank,
GamerTag, Main_{Time,Points}_{Self,Live}, Warships, Warplanes,
Weapon_{Nose,Main1,Main2,Main3}: your figure against the selected entry's, plus
that pilot's kills and four weapon slots.  _Live, GamerTag and the Friend/Monthly
boards make it the Xbox Live detail view.

The trap worth keeping: tables.pak has 12 Detail* RECORD names and 12 Detail*
FIELD names, and they are DISJOINT (0 overlap).  The fields are a different axis
-- 4 board scopes + 6 modes + Detail_Window_{Known,Unknown}.  A Detail_* name
means nothing until you say whether it is a record or a field.

Not settled: what Detail_Window_Known/_Unknown switch between, and whether
Extra01..04 map onto the four challenge missions.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
2026-08-27 23:58:13 +00:00
Claude (auto)
24c3168768 re: the S16 boss's frames and motions; attackC is a cut attack
sub_823BDAA8, the next boss piece.  The subsystem is a linear chain, one call site
each: sub_82385D48 -> sub_823AE908 (collision pairs) -> sub_823BDAA8 (frames +
motions) -> sub_823C0260 (hull part list).

r11, 33 names = the attach-frame roster, partitioning exactly: 15 hull frames + 9
muzzles (GN_MainGun_01_MuzC and GN_MainGun_02_Muz01..08 -- one gun with a centre
muzzle, one with eight) + 1 shield + 3 gear + 5 model ids = 33.  The 15 hull
frames are the same 15 the collision table pairs, and rou_e901_body_01 is present
here -- the one whose _c mesh the collision list did not carry.

r10, 25 names = a SUBSET of the disc's motion set.  The disc object is DefTables
2ef95cdd: 58 records = 3 structural + 55 motions; all 25 code names are in it
(25/25); 30 disc motions are not in the code block.

A reading I nearly published, refuted by checking the other population: in the
code block Motion_guard_start stands alone while every other stem carries _b1/_b2,
which looks like the exception.  It is not -- the disc has guard_start_b1/_b2 plus
guard_keep and guard_end with their variants.  The asymmetry is in the code's
list, not the data.

attackC is a third boss attack the executable never names.  Most of the 30 omitted
motions do exist as strings elsewhere (Motion_guard_keep, Motion_dead,
Motion_break, Motion_damage01 -- one row each), but the whole
Motion_attackC_{start,keep,end,charge} x {,_b1,_b2} family, 12 names, has 0 string
rows, against two identically-shaped controls at 3/3 (attackA_charge,
attackB_charge).  The boss ships with an animated third attack pattern no code
path can select by name.

Not settled: whether attackC is reachable by index rather than name, and what
eff_s900_e_Charge attaches to.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
2026-08-27 23:45:28 +00:00
Claude (auto)
e548db693e re: the Stage 16 boss closed end to end; five disc fields the image never names
New doc docs/re/structures/stage16-boss.md joining three iterations' pieces: the
data object (player-tuning-tables), the collision table (collisionset) and now the
loader.

sub_82315AE8 r11 is the boss loader, and it says so itself: 558 instructions, one
call site (sub_82308AB8), and its 21-entry block is 19 field names plus the
Japanese error "the boss's laser shell ID could not be obtained".

Zero partials: every one of the 19 is a Guardian or Core field -- 16 of Guardian's
19, 5 of Core's 10, DamageLevel1/2 shared.  The boss reads as a rotating,
vertically-moving platform with a guard cycle, three shell ids (AAGun, Laser,
HomingLaser -- the Shell_S16Boss_* set) and a Core that periodically drops
lock-on.

The residual is the finding: 8 disc fields the loader never names, and five of
them are not strings in the executable at all -- InitHight (the game's own typo
for Height) and the four DamageLevel{1,2}Bomb{,Rand}Time.  Controls: ChargeTime
(10 chars) and GuardTime (9) are the same shape and both ARE present, so it is not
an extraction artefact; HP (2 chars) is below the extractor's floor (shortest row
is 6); Radius exists but is generic.

So the disc carries fields no code names -- the mirror image of the usual trap, a
loader naming a field the disc never values.  Whether they are read positionally
or are dead data is not settled.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
2026-08-27 23:32:09 +00:00
Claude (auto)
ced73c5488 re: all 277 base-solver rows classified objectively; 53 are data-table schemas
name_block_bases.py extended with a per-row data-table test; artefact +57/-0,
byte-identical across two runs (now ~2 min 12 s -- 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 (13450 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.  "Round base" and "not a table" are
different questions.

The 53 contain every loader already known -- that is the control.  Five rows in
the 53 are unowned, each noun grepped and appearing in no docs/re/ file:
sub_823BDAA8 r11 (33) = the S16 boss's muzzle/attach frames (GN_MainGun_*_Muz*);
sub_823BDAA8 r10 (25) = motion names (Motion_stand, Motion_attackA_start), the
EnumMotions family DefTables declares; sub_82315AE8 r11 (20) = the Guardian
record's own fields, i.e. the S16 boss loader; sub_8219E560 r11 (18) = the
leaderboard screen keys; sub_825F2CF0 + sub_825F2F88 r0 (30 each, same base) =
post-processing (FinalPassBG, FogMin/MaxDistance).

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.

Not settled: none of the five was opened -- this iteration produced the shortlist,
not the findings.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
2026-08-27 23:09:57 +00:00
Claude (auto)
dd6b13c751 re: the last base-solver blocks -- two findings, and a correction to how I read the index
CORRECTION FIRST: a row in name-block-bases.txt is a (function, REGISTER) pair,
not a function.  Every one-line label I carried for these blocks quoted one of
two rows, and the other row of the same function is an unrelated schema:
sub_822E3EC8 r11 = 29 shader constants + techniques, r10 = 15 material map slots;
sub_822AFA50 r11 = XDK shader-compiler tokens, r10 = 13 menu camera tags;
sub_823AE908 r11 = the 43-name S16 boss collision table, r31 = 33 shader tokens
(the dense-block false positive).  Quote the register.

The engine's material vocabulary (into xbg7-mesh.md, which owns the
_col/_spc/_gls/_lum suffixes): nine base map slots -- ColorMap, LuminosityMap,
SpecularityMap, GlossinessMap, ReflectionMap, TransparencyMap, NormalMap,
FresnelMap, EdgeTransparencyMap -- and seven *MapArray twins; 9 - 7 = 2, and the
two without a twin are FresnelMap and EdgeTransparencyMap.  Four of the nine are
exactly the four documented suffixes; the other five have no suffix in the
shipped meshes, so the engine supports more channels than the assets use.  Plus
17 g_* shader constants, three cube samplers, and DepthOnly / MotionVector /
Transparent techniques -- the ...Last matrices with the motion-vector technique
say the renderer keeps the previous frame's per-object transform.

The S16 boss's collision parts (into collisionset.md): sub_823AE908 r11 is an
interleaved (mesh, logical-part) table -- rou_e901_body_02_c ->
Boss16CollisionBody02, five wing pairs, both tails, barrier, rou_e910_core_cmesh
-> Boss16CollisionCore, and mob_n900{,_b01,_b02,_gear}_cmesh -> Room00/01/02 +
Gear00/01/02.  23 logical parts + 20 meshes = 43, differing by exactly three:
Body01 opens with no mesh and the one gear mesh carries three gear names.  Cross-
links the S16 Guardian/Core object found during the PlayerParams work.

Not settled: whether those meshes are CollisionSet entries, why Body01 has no
mesh, and no shader slot was traced to a shipped shader.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
2026-08-27 22:51:53 +00:00
Claude (auto)
e7c89fdcd1 re: the debriefing and pilot-record screens; the save-screen block was already owned
Read-before-claiming caught one of the three.  savegame-format.md already
documents the shape AND this exact list -- the LOAD/SAVE screen's config key list
is compiled into the executable as a pointer array of key strings, run starting
0x820A0074.  sub_82286BC8's 18 names are that list (17/18 are a tables.pak field
or record).  Nothing new; residual PLAYER_AMMO_LESS_10, not a tables.pak name.

The other two are new screens of the same shape and they close exactly.  All
names resolve into tables.pak: sub_822814D8 24/24, sub_8227A3A0 21/21.

sub_822814D8 = the debriefing screen.  24 = 2 screen ids (STAGE_RESULT,
EX_STAGE_RESULT) + 21 stage_* fields + one sound cue (SE_BOSS_CORE_CHARGE); the
tables.pak STAGE_RESULT record has exactly 21 fields.

sub_8227A3A0 = the pilot record / career screen.  21 = 5 screen ids (LAST_RESULT,
EX_BASE, EX_MAIN, EX_OVERVIEW, OVERVIEW) + 7 ex_overview_* + 9 overview_*; the
records EX_OVERVIEW and OVERVIEW have exactly 7 and 9 fields.

2+21+1 = 24 and 5+7+9 = 21, with 21/7/9 measured independently off the pak.

The debriefing readout is nine num/points pairs plus three points-only lines:
kills by class, objectives, clear time, shoot-down ratio and both damage
categories carry a raw count and its score contribution, while friendly_fire,
shoot_down_others and weight have no counter column -- the same partition
mission-scoring.md measures on the settings side.  OVERVIEW is the seven
EX_OVERVIEW fields plus overview_rank and overview_medals.

Not settled: what EX_ means (its ids have no record of that name), and
PLAYER_AMMO_LESS_10.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
2026-08-27 22:38:56 +00:00
Claude (auto)
418fbf30ed re: the executable's own PG* HUD roster; the disc side was already owned
Took the biggest of the five unowned base-solver blocks, sub_822215D0 (205/206).

Grep-and-READ first, and it paid.  PGHUD* pointed at mission-script-manifest.md,
which documents HudResource's 24 fields -- but hud-config.md owns the whole
family: all 16 records in the six GP_MAIN_GAME_*2D paks, 419 distinct asset
paths.  Stopping at the first grep hit would have "discovered" a documented table
for the second time in three iterations.

Control: counting PG* field names straight off the paks reproduces that
document's own numbers exactly -- Sight 35, Radar 29, Marker 27, Wing 23,
Manuva 15; HudResource 23 PG* + HUD_RES_FONT = its stated 24.

The increment: the executable carries its own roster.  sub_822215D0 reads a
205-name block at base 0x820A0000, 205/205 PG*-prefixed, zero partials -- PGHUD
77, PGTARGET 31, PGGAUGE 25, PGMANUVA 16, PGMARK 12, PGWARNING 10, PGMARKTGT 9.
The disc has 228 distinct PG* names; 200 are in both, 5 code-only, 28 disc-only.
200 + 5 = 205 and 200 + 28 = 228, closing both ways.

Four of the five code-only names are printf families whose stem is a disc field
(PGHUD_HIT_NUM_EFF%d, PGHUD_HIT_NUM_RED%d, PGREMAIN_NUM%d, PGTIMER_NUM%d) and all
four stems sit in the disc-only 28: the code builds indexed instance names from a
base name the table declares.  After pairing, the residual is one code-only name
(PGMANUVA_EFF0) and 24 disc-only ones, listed in the doc.

Not settled: what the block is for -- the function body was not read.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
2026-08-27 22:25:40 +00:00
Claude (auto)
9e2524d028 re: every AIParams object mapped to its stage, zero residual
StageResource.EnumerateAIParams names the table, and every name resolves.

29 StageResource records; 28 carry EnumerateAIParams (the one without it is the
_Test template); 23 distinct table names declared; 23/23 hash to an AIParams
object key under the prefix "stage\"; 0 objects left unnamed; 0 declared names
with no object.

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.

aiparams_census.py extended with the stage mapping; artefact +7/-0, byte-identical
across two runs; the other fourteen verify unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
2026-08-27 22:12:58 +00:00
Claude (auto)
2456d28f1a re: AIParams disc-wide, and a correction to my own last entry
CORRECTION FIRST.  Last iteration I recorded sub_8233C368 as unblocking a
NEEDS-HUMAN item -- "the corpus carries the AI tail of Maneuver as
NEEDS-HUMAN/runtime; it is statically reachable after all."  That was wrong.
stage-mission-tables.md already documents AIParams_S02.tbl as exact original
values obtained by static RE, directly portable, listing all 20 field names and
both shapes.  I grepped FiringLength and saw the file but did not read the
section.  Finding the owning doc is not reading it.  The only genuinely new part
was the loader's name.

What is new: the census generalises Stage 02 to the disc.  23 AIParams objects,
identical in all six GP_MAIN_GAME_* paks, sharing ONE declared-name set of 34
profiles; 782 profile records = 23 x 34; 0 declared names without a record in
their own object.  So "34 AI profiles" is not a Stage-02 fact -- every stage
carries the same 34 and only the values move.  The roster is declared by an
Enumerate_AIs record whose field names are the profile names, the same
declaration-table mechanism that closed DefTables.

Type predicts the field count with exactly two exceptions: Fleet -> 6 fields is
253/253 zero partials; Squad -> 20 fields is 483/529.  The 46-record residual in
full: AI_Test and AI_CraftSquadron_Test, both Type = Squad with only the six base
fields, in all 23 objects.  No profile's shape varies between objects.

New regenerator aiparams_census.py, 45-line artefact, byte-identical across two
runs; the other fourteen verify unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
2026-08-27 21:57:24 +00:00
Claude (auto)
17b4a791eb re: mining the base-solver index -- a false-positive mode named, and the AI-table reader found
277 rows over 190 distinct functions (a function can read several blocks).
name_block_bases.py extended with a confidence split.

The tool's false-positive mode, measured and named: 107 of 277 rows solve to a
base on a 64K boundary -- a bare "addis rX, r0, 0xHHHH" with no addi, so any
scatter of displacements votes for it.  82 are 0x820B0000: about 60
near-identical functions in 0x8281xxxx-0x8284xxxx all "naming" the same rou_e0NN
list.  The 170 rows with a non-zero low half are the trustworthy set.  A round
base is not automatically wrong -- sub_822215D0 sits on 0x820A0000 and resolves
205/206 -- so read the ratio, not the base.

The index re-derives every loader we already knew (unit 217, stage settings 129,
PlayerParams 90, hangar 81, squadron orders, missile guidance, shell movement,
substructures, six camera/fog readers) -- that is the control.

The find: sub_8233C368 reads the AI behaviour table.  r28, base 0x8208583C, 20
names -- Enumerate_AIs, FiringLength, GuardLength, AutoGuardLength, CounterLength,
MusterLength.  stage-mission-tables.md owns those field names on the data side,
but Enumerate_AIs appears in no document and no reader was known; the corpus
carries the AI tail of Maneuver as NEEDS-HUMAN/runtime.  It is statically
reachable after all.  The same base also serves sub_82338EE0 (97 names, Weapon
TargetType SpecialWeaponType ReticleType IsCharging ...) -- the weapon datasheet
loader, also not previously named.

Five unowned blocks surfaced and NOT opened: PGHUD_*/PGREMAIN_NUM HUD part names
(205/206), STAGE_RESULT/stage_num_shoot_down_aircrafts/EX_OVERVIEW,
g_mWorldViewProjection/NormalMap/GlossinessMap engine material slots,
Boss16Collision* (cross-links the S16 Guardian object), and roh_n001_menu1_cam_pos
menu camera tags.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
2026-08-27 21:41:03 +00:00
Claude (auto)
ae37e15d7d re: the base-solver -- 277 name-block loaders indexed, and the analog block is SOLVED
New reusable tool, tools/re-capture/name_block_bases.py -> docs/re/data/name-block-bases.txt
(2880 lines, ~65 s, byte-identical across two runs).

A loader that reads a table by field name keeps one base pointer and emits
"addi rX, rBASE, -N" per name, so no static xref sees the strings.  Solve the base
from the DISPLACEMENT SET alone: every (string address, displacement) pair implies
a candidate base, and the true base collects a vote from every name it explains,
so it wins outright.  My first cut took candidates from ONE displacement and
scored the unit loader at 52/226 against the right answer's 217/226 -- vote over
the whole set, not a probe.

Control passes with no prior knowledge: the tool recovers sub_82341A20 -> r30 =
0x82088F94 at 217/226, and independently recovers sub_8230D1F8 (129/132),
sub_822F9498 (90/91) and sub_822AE628 (81/108).  277 name-block-reading functions
image-wide, with the schema each names.

The analog block is SOLVED: sub_821A6CF0, r29 = 0x820A1630, 22/24.  In code order
it names ControlTweakName, YawMagForNormal, the 12 Tweak fields, the 8
AnalogRevice_* curves and GP_MAIN_GAME -- the whole schema in the object's own
order plus its pak.  r29 is built at 0x821A6D34 as addi r29, r11, 5680 =
0x820A0000 + 5680, matching the solved base exactly.  It is the same function that
reads PlayerParams.

Two of my own verdicts withdrawn: "referenced by nothing" and the softened "not
found by these routes".  The measurements behind them were right; the conclusions
were wrong.  The base was solvable from the data the whole time.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
2026-08-27 21:22:50 +00:00