4bc970688f17633b49bcced9ee060dc6ee118135
339 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
35538e3b5b |
re: the Time/Points record is not on the debriefing path - and a ring drainer matches the message bus
Read 0x821A0AD0, the debriefing, end to end, resolving every call and string in code order. The word-B bit-setter sub_8219F2E8 is its FIRST call, before anything else; the function makes 44 calls and references exactly two strings, DEBRIEFING and BASE_INFO. No Time and no Points anywhere on this path - not in the debriefing, not in the bit-setter. Section 5.3's expectation that clearing a challenge stage records its metric alongside the bit is not supported by the code that clears it. The reach of that negative: the debriefing function and its directly-called bit-setter, both read whole; it does not rule out a store deeper inside one of the other 43 callees. 0x8219F670 turns out to be an online-state predicate: 88 bytes, no strings, reads the singleton, tests bit 1 of [0]+60, and if set passes [0]+64 to 0x824A9C90 and returns whether the result is 2. A connection or sign-in check, not a record store. The useful find is 0x8219F460, 368 bytes and self-recursive, which drains a ring based at r29+320 with element base +4, capacity +8, head +12 and count +16. Those are the corpus's documented message-bus offsets exactly - BACKLOG records sub_82175C20(bus+4, &msg) as a ring-buffer append with capacity +8, head +12, count +16 - and sub_82175C20 is the very notifier section 5.6 found the word-B writer calling with bit + 64. So the open question "ring-buffer PUSH, no handler" now has a candidate drainer with a byte-for-byte matching field layout, reached from the debriefing. What is NOT shown is that these are the same ring instance. The push targets bus+4, this drain targets r29+320. Matching offsets prove a shared container shape, not a shared object, and the identity needs the two base pointers tied together. All seventeen artefacts byte-identical. |
||
|
|
f7a0cf47b1 |
re: progress word B's writer is sub_8219F2E8 - it sets bit (stage - 24)
Section 5.5 narrowed the search to 21 functions but could not name one, because the scan assumed the copy destination was an r1-relative stack local. It is not. Reading the known word-A writer instead of guessing its shape shows a frame register: bl singleton, addi r3,r31,96 (dest), addi r4,r11,80 (src = obj+80), bl copier, stw r10,96(r31). That one wrong assumption made the first scan return 0 for all 21, the known-good included - the third filter this week killed by its own control. With the real shape - addi r3,rF,K before the copier, then stores at K+d on rF - word B sits at d = 1956-80 = 1876, and exactly one of the 21 hits: sub_8219F2E8, copy dest r31+560, storing 2436(r31), and 560+1876 = 2436. Control: the word-A writer 0x821C1630 stores at relative +0, i.e. word A. The proof is the bias. Word B's writer computes addi r11,r11,-24 then 1 << (index-24) before or-ing into the copy; word A's writer shifts by the raw stage number with no bias. That reproduces section 5.1's read-side rule - n >= 24 means bit (n-24) of word B - on the write side, so word B's bits 0-5 are challenge stages 24-29, the same six that 5.4's MISSION_ID names. The two writers are exact complements. It has exactly one real caller, 0x821A0B1C inside 0x821A0AD0, whose strings are BASE_INFO and DEBRIEFING - so clearing a challenge mission is recorded from the debriefing screen. A second bitfield falls out of the same function: it reads the copy at +1880 (2440(r31) = obj+1960, the word after B), re-reads obj+1960 after the setter, xors old against new, and loops the 32 result bits calling 0x82175C20 with bit + 64. So obj+1960 is a second 32-bit progress bitfield with ids 64-95, whose newly-set bits are announced one at a time - an unlock-notification loop. Against 5.3's own prediction: neither sub_8219F2E8 nor its caller references Time or Points, 0 for both. 5.3 expected word B's writer to also store the stage's Time/Points record. It does not - this is the bit-setter only, and the record store is still unlocated. All seventeen artefacts byte-identical. |
||
|
|
a41a6b9783 |
re: progress word B - search space closed at 21 functions, the field itself still unwritten
Section 5.3 left word B (+1956) with no known writer. Three routes ran this turn: one produced a population, two were refuted, and the field is still unwritten. The offset route has power here, unlike +184. +1956 is a large unusual offset with 9 stores and 28 loads image-wide, against +80's 5403 and 6923. Control: the known reader 0x821898C4 sits in the gate's list-builder 0x82189870, exactly where 5.1 puts it. But none of the 9 stores turns out to be a progress write - filtering by whether the storing function reaches the progress object gives 0 of 8 distinct functions, and the 9th stores through r1, the stack pointer, so it is a local. +1956 is simply an offset several unrelated structures also use. The obvious filter is dead, and it fails its own control. Testing "does the function reference the singleton global 0x828F48B0 or call 0x821707C0" against the KNOWN word-A writer returns no contact, because that path reaches the progress object through the copier and never through the global. A filter that rejects the known-good answer proves nothing about the others. What did work: progress is only ever changed copy-modify-store, so every writer must call both the copier 0x82175110 and the setter 0x8216FF70. Measured, 21 functions call the setter, 21 call the copier, and 21 call both - the two caller sets are the same set. That is the complete progress-write population and it is small enough to read exhaustively. Control: the word-A writer is among them as 0x821C1630 (BASE_INFO + DIFFICULTY), and 5.2's store 0x821C1820 lies inside it - the doc's 0x821C09D8-0x821C29F0 is the enclosing method, 0x821C1630 the function. No member of the 21 stores to +1956 directly. Their only large store offsets are into this - 1004, 1980, 2040, 2100, 2199, 2436 - with 1004 being 5.2's own this+1004 & 0x20000 guard. So word B is written through the stack copy, at localbase + 1876, which no offset scan can pick out. One false friend worth recording: two of the 21 reference Points, which looks exactly like the challenge record-storing path 5.3 predicts. It is not - their full string sets are Dependency, MissionObjective, Points, WEAPON, WEAPONS, i.e. the arsenal development economy, and neither references Time. There are two Points vocabularies, development points and the leaderboard metric. Next candidate, not yet checked: 0x8218EFE0 is the one progress writer whose strings are BASE_EXTRA and DIFFICULTY - the only member of the 21 carrying the EXTRA vocabulary. All seventeen artefacts byte-identical. |
||
|
|
3b80779180 |
re: which challenge stage is which leaderboard mode - CLOSED from the record's own fields
challenge-mission-gate.md section 5.3 left a marked 🟡 on the exact pairing and named
the right route: read the record's binary index section rather than the string pool.
Running that with the corpus's own reader settles all six missions at once.
TimeAttack MISSION_ID 24 REQUIREMENT 16 RECORD_TYPE Time
ScoreAttack MISSION_ID 25 REQUIREMENT 24 RECORD_TYPE Points
Extra01 MISSION_ID 26 REQUIREMENT 25 RECORD_TYPE Points
Extra02 MISSION_ID 27 REQUIREMENT 26 RECORD_TYPE Time
Extra03 MISSION_ID 28 REQUIREMENT 27 RECORD_TYPE Time
Extra04 MISSION_ID 29 REQUIREMENT 28 RECORD_TYPE Points
Control: the table appears in 6 pak entries, all in tables.pak, and all six are the
same table byte for byte - one per language, i.e. one user under the per-pak copy
rule. Zero variants.
Three things fall out, none of them assumed. MISSION_ID is the stage number: the six
IDs are 24-29 in slot order, exactly the six challenge stages S24-S29, bijectively -
so the "four Extra0n modes against five _EXn stages" worry dissolves, because _EXn is
a unit-variant suffix that indexes nothing here. REQUIREMENT is a strict chain, 16,
24, 25, 26, 27, 28, each mission requiring the previous one's stage with TimeAttack
anchored on stage 16, the final story mission; 5.3 inferred a chain from the shape of
the string pool, and it is now exact and branchless. RECORD_TYPE is the leaderboard
metric and it is per-stage, not per-name: 3 Time (S24, S27, S28) and 3 Points (S25,
S26, S29), so the tempting reading that the Extra0n family shares one metric is
wrong - Extra01 and Extra04 are Points while Extra02 and Extra03 are Time.
Withdrawn from 5.3: the string-pool reading listed the numeric tokens as 16, 25, 26,
27, 29 with 24 and 28 deduped away. The real REQUIREMENT set is 16, 24, 25, 26, 27,
28 - 29 is a MISSION_ID, never a requirement. A value pulled from a deduped pool by
adjacency got one member wrong; the indexed read has no such failure mode.
Left as an association rather than a proof: RECORD_TYPE takes exactly the two values
Points and Time, and result-screens names two ranking screens POINT_RANKING and
CLEAR_TIME_RANKING, but no code path has been shown selecting one screen from that
field.
All seventeen artefacts byte-identical.
|
||
|
|
0ace5db7c7 |
re: the 95 BASE_INFO screens partitioned - BASE_INFO discriminates screen-config from table-read
Item (a): mine the screen population found last iteration. Resolving every one's key list via the string-xref join over its own body gives 298 distinct keys and a three-way split: 95 = 9 corpus-known + 48 new with a readable key list + 38 bare, where "bare" means BASE_INFO and nothing else. The bare 38 are the honest limit of the instrument - a screen whose only catalogued string is BASE_INFO cannot be named this way. The control turned the finding into a law. Only 9 of the 19 loaders the corpus documents appear among the 95, and which nine is not arbitrary. The nine that reference BASE_INFO all read a SCREEN's own configuration: debriefing, career, save, medals, evaluation list, gamma, POINT_RANKING, CLEAR_TIME_RANKING, hangar. The ten that do not all read a DATA TABLE: unit definitions, weapon datasheet, AIParams, stage settings, chatter, PlayerParams, ControlTweak, the PG* HUD roster, material slots, and the leaderboard. 9/9 and 10/10, both off-diagonals empty. The leaderboard falling on the table side is a check rather than an exception - its own doc records that all 18 of its names are tables.pak names, so it is a table reader that happens to paint a screen. That exclusion is what last commit's "universal screen marker" claim was missing. 48 new screens are named by their most distinctive keys, including the title screen (sub_821C4EB0: TITLE_SCREEN/TITLE_MENU/LOADING), the ready room (sub_821D6D28: SCRIPT_ID/SCRIPT_PATH, confirmed independently by its own error string silph::GamePart_ReadyRoom::Impl::PrepareScript), movie subtitles, the three menu modes STANDARD/EXTRA/TUTORIAL, the speaker window, loadout and gallery views, mission select, and an equip screen carrying the game's own typo EQUIIP_LIMITATION. One caution worth recording: a high key count is not a rich screen. The two 27-key entries sub_82297550 and sub_822A2F00 resolve to pure coordinate pairs (1080,163 / 734,502 / 1164,403) - a layout table, not configuration. Read the values before trusting the rank. Still open: the 38 bare screens, and whether the EX_ in EX_MENU_DISABLE_SKIP and EX_LETTERS is the same EXTRA mode word as +184. All seventeen artefacts byte-identical. |
||
|
|
80406ce3e4 |
re: the RTTI route is dead - BASE_INFO enumerates 95 screens instead
Item (a): can the vtable scan name the corpus's anonymous classes? The route I proposed last turn - "the RTTI behind those two anon classes" - does not exist. Of all 1150 catalogued vtables, 1150 carry an ANON_ class name, 0 have rtti_present set, and 0 have a non-empty base_classes_json. Not one named class, not one RTTI flag, not one base-class edge in the whole table. Withdrawn as unrunnable; the ANON_Class_XXXXXXXX labels are synthesised, not recovered. The sibling methods cannot name a class either: slots 0, 2, 3 and 4 of both EX_ classes reference zero strings. Every naming datum sits in slot 1, the method the corpus already had. Re-verified at the true addresses (0x820A9760 and 0x820A9350) that sub_822814D8 and sub_8227A3A0 are each slot 1 uniquely, and sub_82286BC8 is in no vtable. What the shape DID give: both classes are 5-slot with an exactly-88-byte slot 0. 83 vtables have 5 slots, 16 also have the 88-byte slot 0, 13 of those have a catalogued slot 1, and 10 build a key list. Ranked by key count the two known screens sit at the top and the rest name themselves - a medals screen (MEDALS/ACHIEVED_MEDALS/NEW_MEDAL), an evaluation list (EVALUATION_LIST plus eval_%02d_eval / eval_%02d_points), the gamma screen (GAMMA_RGB/GAMMA_WB), POINT_RANKING, CLEAR_TIME_RANKING, and three bare ones. POINT_RANKING and CLEAR_TIME_RANKING being two separate screens is a direct lead for the open item "which challenge stage is which leaderboard mode": the leaderboard is ranked two ways and each way has its own class. But the obvious follow-on claim - that BASE_INFO marks this family - is false, and the control says so. BASE_INFO is a single string at 0x820A20EC referenced by 95 functions: 10 inside the family, 85 outside, including sub_82286BC8 (the save screen, in no vtable at all) and sub_822AE628 (the hangar loader). So the direction inverts: BASE_INFO is the universal first key of every screen key list, a 95-function population, while the 5-slot/88-byte shape is only a weak filter that catches 10 of them plus 6 non-screens. Enumerate screens by BASE_INFO, not by vtable shape. All seventeen artefacts byte-identical. |
||
|
|
141a65cb4a |
re: the ind_call damage is bounded - no corpus claim was contaminated
The protective sweep after last commit's refutation. The question was which existing claims the ind_call cross product voids. Answer: none of the ones the corpus makes. Every caller-count claim in the corpus verifies against call edges, with zero ind_call contamination: title-crash-stl-tree sub_82457780 "one caller" call=1 ind_call=0 isl-trigger-queue sub_8226EAB8 "16 callers" call=16 ind_call=0 isl-coroutine-spawner sub_822737C8 "seven callers" call=7 ind_call=0 challenge-mission-gate 0x82175110 "22 callers" call=22 ind_call=0 4 of 4 exact, and not one of the four carries a single ind_call edge. There is a structural reason for that rather than luck. Partitioning the two target populations: 1710 targets have ind_call edges, 12170 have call edges, 103 have BOTH, 1607 are ind_call-only, 12067 are call-only. And all 1710 ind_call targets are vtable entries - 1710 of 1710, zero partials - against a control where only 273 of the 12170 call targets are. So ind_call fires only on virtual methods, and 1607 of them have no direct caller at all, which is exactly why the two EX_ screens came back empty on call and flooded on ind_call. The residual risk is narrow and nameable: the 103 targets carrying both kinds, where a caller count taken without a kind filter would be inflated by about 633. Everywhere else the two populations are disjoint, so a call-based count is safe and an ind_call-based one is meaningless. Rule for future work: always filter kind='call'; and when a function has no call edges, that is a fact about it - it is a virtual method - not a gap to be filled from ind_call. All seventeen artefacts byte-identical. |
||
|
|
749567e835 |
re: the +184 writer is blocked - and xrefs.ind_call is a CROSS PRODUCT, not a call graph
Chasing what supplies the EX_ mode word. Two routes, both measured to have no power, plus one structural fact that did come out. The offset route is dead. stw ..., 184(rN) occurs 301 times in the image, lwz from +184 occurs 351 times, and 115 functions touch both +144 and +184. +184 is an ordinary small offset shared by many unrelated classes - the same shape the corpus already recorded as failing three times. Nothing narrows to a writer. The bigger result is an instrument refutation with corpus-wide reach. Asking xrefs for the callers of the two screens returns 633 sources for EACH, and the two lists are IDENTICAL, which cannot be right. Measuring the relation itself: ind_call rows 1827297 distinct targets 1710 distinct sources 6992 targets with EXACTLY 633 sources 236 236 different functions sharing an identical source count is the signature of an unresolved-indirect-call cross product, not of a call graph. Any reading that treats an ind_call edge as "X calls Y" is void, here and anywhere else in the corpus it may have been used. The control shows the other kinds are sound: sub_82286BC8 has exactly one caller, kind call. And both EX_ screens have ZERO non-ind_call edges - they are reached only through function pointers, which is why the direct graph is empty for them. What did come out: scanning the entries of all 1150 catalogued vtables in the flat .pe, both EX_ screens are slot 1 of their own class - sub_822814D8 in ANON_Class_271D5F25 and sub_8227A3A0 in ANON_Class_CAA8AD62 - while sub_82286BC8 is in no catalogued vtable at all. So the 2/0/0/1 partition from the previous commit reflects a structural difference rather than a coincidence: the two screens that select on the mode word are vtable methods of their own classes and the one that does not select is not a vtable method. Still not settled: what writes +184. Both the offset sweep and the call graph are exhausted for it. A route with actual power would be the RTTI behind those two anonymous classes, or a runtime watch on the field - not another static offset search. All seventeen artefacts byte-identical. |
||
|
|
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.
|
||
|
|
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.
|
||
|
|
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. |
||
|
|
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. |
||
|
|
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.
|
||
|
|
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.
|
||
|
|
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. |
||
|
|
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.
|
||
|
|
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. |
||
|
|
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. |
||
|
|
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.
|
||
|
|
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.
|
||
|
|
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. |
||
|
|
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. |
||
|
|
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. |
||
|
|
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. |
||
|
|
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. |
||
|
|
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.
|
||
|
|
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. |
||
|
|
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. |
||
|
|
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. |
||
|
|
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.
|
||
|
|
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.
|
||
|
|
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. |
||
|
|
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.
|
||
|
|
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. |
||
|
|
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.
|
||
|
|
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. |
||
|
|
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.
|
||
|
|
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.
|
||
|
|
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.
|
||
|
|
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. |
||
|
|
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.
|
||
|
|
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.
|
||
|
|
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.
|
||
|
|
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. |
||
|
|
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. |
||
|
|
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.
|
||
|
|
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. |
||
|
|
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
|
||
|
|
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
|
||
|
|
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
|