Commit Graph

286 Commits

Author SHA1 Message Date
Claude (auto)
418fbf30ed re: the executable's own PG* HUD roster; the disc side was already owned
Took the biggest of the five unowned base-solver blocks, sub_822215D0 (205/206).

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

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

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

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

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

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

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

AIParams_S01..S16 (16) + AIParams_S24..S29 (6) + AIParams_Tutorial.tbl shared by
six (UnitGroup_S18..S23, the tutorials) = 23 tables over 16 + 6 + 6 = 28 records.
The arithmetic closes both ways and matches the 23-object count found last
iteration.

Same sharing shape as the settings family (stage-settings-table.md: 24 objects,
StageParameter_Tutorial shared by six tutorials).  Two independent families agree
on how the tutorials are handled -- n=2, a pattern rather than a rule.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
2026-08-27 21:22:50 +00:00
Claude (auto)
38e0a63946 re: the unit loader was already in the corpus (sub_82341A20), and 0-xref does not mean no reader
Item "what loads the 114 unit Maneuver records" -- the corpus already answers it,
and I should have found that before framing it as new.  My own error, corrected:
I claimed live-unit-definitions.md did not exist.  It does, at docs/re/, not under
structures/.  Looking in one directory is not looking.

sub_82341A20 is the unit-definition loader, documented in unit-struct-runtime.md
with a checked-in 177-line layout at
crates/sylpheed-formats/data/unit_definition_layout.txt.  Verified independently:
3969 instructions, 227 of its 236 "addi rX, r30, -N" displacements resolve to
strings -- the unit schema in order, Generic, UncertainName, IsDestructible,
NamePlate, Size_X/Y/Z ...  Maneuver sits at exactly 0x82088F94 - 13404.

Record keys are tag_hash, confirmed on the data side: the Maneuver record's key is
0x43FAA517 = tag_hash("Maneuver"), not name_hash (0x63A248D7), x114 records.

That explains the 0-xref phenomenon and softens my own verdict from last
iteration.  Maneuver's string has 0 xrefs AND a known reader: the loader takes its
address as a register-passed base plus a displacement, which no static xref can
see.  So the AnalogRevice/Tweak verdict goes from RED to AMBER -- a
base-plus-displacement sweep over the 2283 functions forming the 0x820A high half
reaches it 0 times, but run against the unit block as a control the same sweep
recovers only 5 references and misses sub_82341A20 itself.  The measurement
stands; "the reader is unreachable" does not.

Also fixed: player-tuning-tables.md credited live-unit-definitions.md with
documenting sub_822F9498.  It documents sub_82341A20, a different function.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
2026-08-27 21:02:58 +00:00
Claude (auto)
6334f36dde re: the object is PlayerParams, and sub_822F9498 is ITS loader -- not the unit loader
Resolving every string sub_822F9498 references, in code order, gives 90, and they
are exactly the 890e1be4 object's schema in its record order: Misc (5 effect
names), SpecialAttack (+17), TacticalManeuver (+7), SpecialWeapon (+8), Booster
(+50).  It never names Generic, Maneuver, Explosion, Shield or StructureCount, and
the string Maneuver has 0 xrefs.  One call site, from sub_821A6CF0, which is
itself called once and references the literal PlayerParams.

CORRECTION to unit-datasheet-static.md, which calls sub_822F9498 the
unit-definition loader.  It loads one object: the player parameter table.  Its
AA_/AV_ interleave still stands as a struct layout, but the struct is
PlayerParams's Booster record, not each unit's Maneuver.  What loads the 114 unit
Maneuver records is now an open question -- it uses none of these strings.

Ranking all 114 units by how many of Booster's 50 values they reproduce: the top
five are exactly the five _Player units, all at 39/50; the sixth drops to 13/50;
none matches 50/50.  The five players agree with each other on all 50 -- the three
player ships share one flight model.  Booster stands alone on 10 fields; the
eleventh, AA_Yaw_Max, is 65.0 vs 65, formatting not a value (last iteration's
"11 differ" over-counted).

Still open, and sharper: nothing selects Booster.  PlayerParams is loaded once,
unconditionally, no branch -- yet the measured flight tracks the unit Maneuver
(1.25/1.20/1.28) and misses Booster badly.  Both cannot govern the same craft; the
next test is a runtime watch.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
2026-08-27 20:48:56 +00:00
Claude (auto)
92d1a39ce2 re: the player-tuning consumer -- found for half, BLOCKED for the other, two of my labels refuted
The consumer is sub_822F9498, the unit-definition loader the corpus already
documents.  The field-name block 0x8209F300-600 (BulletTimeVolumePC, FireBirdPower,
ChargeMaximum, Turn180RequiredTime, ShieldDoubler_Time, ...) is 36/36 referenced,
every reference inside that one function.

REFUTED, my own label: "Booster is a new schema".  Its 50 field names are a strict
subset of the unit Maneuver record -- 50/50 shared, 0 Booster-only.
CruisingVelocity occurs in exactly 115 records disc-wide: 114 Maneuver + this one.
The first check compared against Generic and scored 0/50, which read as brand new:
the wrong record of a multi-record object is not a control.

REFUTED: "Booster is the player craft's flight envelope".  flight-speed-law.md
measured ~125/~420/~1530; unit Maneuver 100/350/1200 gives consistent ratios
1.25/1.20/1.28, Booster 100/612/2100 gives 1.25/0.69/0.73.  Booster is a SECOND,
faster profile: 39 of 50 values identical to the player unit, and of the 11 that
move the three velocities scale by exactly x1.75 and two accelerations by x1.5.

BLOCKED: the AnalogRevice/Tweak reader.  That schema is its own contiguous block at
0x820A119C-0x820A1378 headed by ControlTweakName -- 0 of 28 strings has a code
xref and no instruction anywhere names a 0x820A1xxx operand, against a control of
36/36 on the neighbouring block.  Runtime watch needed.

Caught on the way: mov_stick_play/eye_stick_play exist twice; the referenced copy
belongs to sub_822AE628, which reads GP_HANGAR_ARSENAL's ControlTweak records (6 of
them, and none anywhere else) -- a clean, wrong answer if taken for ours.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
2026-08-27 20:33:27 +00:00
Claude (auto)
c171a194d9 re: the GP_MAIN_GAME unnamed block -- 333 of 337 already owned, 4 genuinely new
All six language copies carry the identical 337 unnamed IDXD hashes.  Partitioned
by record-name shape (53 shapes), 333 map onto families the corpus already
documents: 131 weapon datasheets, 114 unit datasheets, 64 unit Faces tables, 10
unit message sets, 8 chatter rule tables, 5 enumerations/formations/placement.

114 is exactly the corpus's unit count (43 Craft + 71 Vessel) -- these are the
tables the corpus has always worked with, reached by SHAPE because they have no
names.  Naming them adds nothing.

The Enumerate object in each GP_MAIN_GAME_* is EMPTY, zero fields, which is why
route 2 named 1283 entries in DefTables and 0 here.

The 4 unclassified objects are new -> docs/re/structures/player-tuning-tables.md:
the analog stick response curves (8 axes, 11 samples + a named Count = 11, tested
8/8; yaw/roll/throttle are the identity ramp, the shaping is all on pitch and the
camera axes; adv_yaw is non-monotone and unexplained), the player craft's flight
envelope (Booster, 50 fields -- the player side of the AA_/AV_ pair documented
for NPCs -- plus TacticalManeuver, SpecialAttack + three gauge bands,
SpecialWeapon, Misc), the Stage 16 boss (identified by Shell_S16Boss_* ids;
Guardian HP 65000, Core 42000), and one unidentified Generic naming eff_n0071.

New regenerator main_game_unnamed.py, 112-line artefact, byte-identical across
two runs; the other twelve artefacts verify unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
2026-08-27 20:17:10 +00:00
Claude (auto)
53084a11ca re: the Enumerate declaration tables close DefTables (130 -> 1413 of 1425 IDXD)
The string harvest could never name DefTables because those names are not
spelled out as "something.tbl" anywhere on the disc -- they are declared.

An IDXD object whose single record is named Enumerate is a DECLARATION TABLE:
its field NAMES are the names of other objects, each resolving as
name_hash("<field name>.tbl").  EnumLODSet_test.tbl declares 676,
EnumGameModel_test.tbl 360; the disc holds 144 such objects (138 in DefTables,
one in each GP_MAIN_GAME_*) declaring 1298 distinct names.

Route 2 names +1283 entries route 1 could not.  130 + 1283 + 12 = 1425, no
overlap, 99.2 % coverage.  Zero partials: of 5 suffixes x 6 prefixes, ('', .tbl)
scored 1036/1036 and every other combination scored 0.  Residual in full: 8
declaration tables nothing declares, 2 LOD sets (Model rou_e004 / rou_e013), 2
motion sets; 15 declared names have no pak entry at all.

REFUTED alongside it: the 40 XPR2 manifests are not the naming source -- their
82 Name= values and 82 DataFile/Source paths resolve 0 entries under any of the
5 suffixes.  They share the MODEL namespace only: 40 of the 82 appear as the
Model field value inside the tables.

Artefact +17/-18, every line paired, byte-identical across two runs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
2026-08-27 19:59:20 +00:00
Claude (auto)
bcc23cba4a re: why each archive is low-coverage -- two different reasons, cleanly separated
Splitting every unnamed entry by magic turns the coverage percentages into an
explanation.

The three low-coverage UI archives have ZERO unnamed IDXD.  GP_HANGAR_ARSENAL
is 180 IDXD, 180 named, 0 unnamed -- its 1191 unnamed entries are 1149 T8aD/RATC
plus 42 LSTA, i.e. sprites.  GP_MISSION_SELECT and GP_DEBRIEFING_PILOTLOG hold
no IDXD objects at all.  So "22.6 % named" is misleading: every data table in
that pak is named, and these three are the same artwork-naming phenomenon as the
blocked 2D and READY_ROOM archives.

DefTables is the only genuine data gap: 1425 IDXD, 130 named, 1295 unnamed, in
17 record-name shapes -- Generic + Level_0..Level_3 (807, LOD sets) and Default +
EnumMotions + Generic + ReferenceFrames +/- Motion_break/dead/down (463, motion
sets).  One LOD and one motion table per model.  Level_0, EnumMotions and
ReferenceFrames appear in no document.

Control: the 100 % archives have no unnamed entry of any kind, and GP_MAIN_GAME_E
is 667/337 IDXD with zero unnamed artwork -- two failure modes, not a gradient.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
2026-08-27 19:41:29 +00:00
Claude (auto)
a543b8da96 re: correction -- the naming sweep already covered the 24; it just never said so
archive_naming.py already harvests 6027 candidate names under 16 prefixes, and
testing its candidate set directly shows it names all 24 StageParameter_S<NN>
objects, 24/24.  The previous entry presented that naming as new -- it is not.
What was new was the identification (which object is which stage, the shared
_Tutorial table, IsBoss16Enable = S16), not the method.

The real gap, now closed: the sweep reported only per-archive percentages and
never emitted WHICH entry got which name, which is exactly why nobody could say
the settings objects were StageParameter_*.  It now prints the resolved name
families per archive -- 6573 named entries, 1631 families disc-wide.

Determinism caught again by the verify loop: the resolved map was built by
iterating a set, so collided hashes picked a different winner each run.  Now
iterated sorted().  Second time in two iterations -- any map built from a set
needs a sort.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
2026-08-27 19:25:30 +00:00
Claude (auto)
589d09f2b5 re: all 24 settings objects named -- stage\StageParameter_S<NN>.tbl
The queued item (the 29-object resource manifest) turned out to be already
settled by challenge-mission-gate.md and stage-mission-tables.md, so the
iteration moved to the open question those docs make answerable.

Hashing EVERY string that appears in any IDXD object on the disc resolves all
24: StageParameter_S01..S16 (story), S24..S29 (challenge), StageParameter_
Tutorial shared by all six tutorials, and StageParameter_Test.  16+6+1+1 = 24,
which is exactly why the settings family is 24 against 29 stage records.

IsBoss16Enable confirmed independently: that object is StageParameter_S16.
GravityFactor is non-zero in S10 (700), S11 (400), S03 (250), S27 (250); the
three unscored objects are S24, S27, S28; the one without SplinterCell is _Test.

Refuted on the way: TOC order is not stage order (the TOC is hash-sorted and the
Boss16 object is 17th, not 16th), and neither the StageResource template nor a
real 19-field record names the settings table.  The AUTO_SETTINGS filenames are
a different scheme -- none of the 28 hashes to any of these 24.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
2026-08-27 19:05:28 +00:00
Claude (auto)
5f764458cd re: the Phase_1/2/3 block of the settings family -- mostly per stage, not per phase
72 records, 94 distinct field names, 31 in every one (post-processing core, five
Fog*, three ScreenColor*, SpaceSize, five Supply*, UnderCommandSquadron, the
first BGOperate slot).  Optional families sit in clean tiers: Nebura_* and
ColorLayer* in 69/72, DOF_*/UnsharpMask_*/ExposureKey_* in 15/72, FinalPass* in
3/72.

REFUTED: BGOperateFrameCount is NOT the number of BGOperateFrameName_i slots --
36 of 72.  What holds is Count <= slots, 72/72: a fixed slot array with a live
count, the same shape as MessageCount under the 32-slot clamp.

68 of the fields common to all three phases NEVER differ in any of the 24
objects.  The phase block is a per-stage environment block copied three times;
what a phase change is actually for is the backdrop animation (BGOperate*) and
the supply/command squadron assignment.

MapPath is not in this family -- its 87 records per pack are 29 x 3, the
resource manifest's Phase_N.

Also fixed a non-determinism the verify loop caught: most_common() over a set
iteration ordered ties differently per run; now sorted by (-count, name).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
2026-08-27 18:50:27 +00:00
Claude (auto)
26b7728ceb re: the rest of the stage-settings object -- cameras, player limits, difficulty
53 objects in GP_MAIN_GAME_E carry a Phase_1 record, in TWO families: 29 are
the resource manifest the corpus already owns (Phase_N = 4 fields) and 24 are
the settings table (Phase_N = 31-90 fields).  That answers the 24-vs-28 puzzle
left open by the scoring entry -- the 29 is a different table, not the settings.

Camera: three chase rigs in metres, 13 of 14 fields identical in every stage --
Nose (0, 4.5, 7), Near (0, 10, 40), Far (0, 15, 80), FOV 0.92; only CameraFar
varies, once.  Player: BulletLimit 512 / HomingLimit 256 / LaserLimit 32 and
the three 0.30 axis adjustments are constant, GravityFactor is non-zero in 4 of
24 stages, and IsBoss16Enable appears in exactly ONE object -- the first
per-stage handle for a family whose filenames do not resolve.

Difficulty_Easy/Normal/Hard is a SECOND difficulty record (8 damage and
guidance multipliers), separate from Score_*.

New doc structures/stage-settings-table.md; mission_scoring.py extended.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
2026-08-27 18:35:05 +00:00
Claude (auto)
192d3bf67f re: mission scoring and the S/A/B/C/D rank thresholds
sub_8230D1F8 -- the loader that contaminated the AA_/AV_ offset search -- is the
stage-settings loader.  Reading its 122 field NAMES instead of its offsets found
the scoring block, which nothing in docs/re owned.

24 IDXD objects per language pack x 6 = 144, each holding Score_Easy /
Score_Normal / Score_Hard: 72 records per pack on one 22-field schema, no
variants.  Difficulty moves 10 of the 22 fields and never the five RankScore_*
thresholds -- the rank bar is per stage, difficulty scales the earning rate
(x0.5 / x1.0 / x2.0) and the penalties.  23 of 24 objects differ from the
commonest Normal record; 9 of 72 records zero the scoring entirely.

Not settled: which object is which stage.  None of AUTO_SETTINGS's 28 filenames
resolves to any of the 24 under 19 prefixes, and 24 vs 28 is unexplained.

New doc structures/mission-scoring.md, regenerator mission_scoring.py, artefact
data/mission-scoring.txt.  Twelve artefacts now.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
2026-08-27 18:22:36 +00:00
Claude (auto)
1a1a8d98d9 re: the AA_/AV_ selection is BLOCKED for static RE, with the mechanism
Route 3 (call-graph bound): sub_821A6CF0 and sub_821AB650 each read 196/200/204
off THREE different base registers -- three unrelated objects, not the block.

Route 4 (data-flow bound): the definition object lives in the global at
0x828F358C; 18 functions touch it, 9 also touch block offsets.  The two best
are refuted -- sub_8230D1F8 is the rank/score loader storing
CraftScore_Adjustment, FFPenalty_Zessel_Maximum and RankScore_S/A/B at exactly
256/320/324/328/332, and sub_82398CC0 uses r19 as a float-constant pool.

So the offset region is shared by two unrelated objects and a constant pool:
offset-based discrimination is contaminated by construction, which is why it
has now failed three times.  Catching the selection needs a runtime watch.

Side finding, unowned by the corpus: sub_8230D1F8 is the rank/score loader.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
2026-08-27 18:07:42 +00:00
Claude (auto)
4ffbd39a01 re: AA_/AV_ are one interleaved block; the selection is still unfound
sub_822F9498 (the unit-definition loader, called only from sub_821A6CF0) is the
only function referencing the AA_/AV_ name strings.  Mapping each name to the
stfs that follows gives an exact interleave: AV_ at X, AA_ at X+8, for all five
axes -- PitchPlus 196/204, PitchMinus 212/220, Yaw 228/236, Roll 244/252,
AxisMode 320/328.  A selector is an offset of 0 or 8, not two lookups.

The 20 strings exist twice in the image; the first block is referenced by
nothing.  Data control: the ten suffixes match exactly between families.

The selection stays a reading: functions loading two or more of 196/204/320/328
number 39 image-wide, and only two are call-graph-reachable from the loader.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
2026-08-27 17:54:53 +00:00
Claude (auto)
36f97c6322 re: the player-facing chatter partition; correcting the wingman claim
The 13 tables that voice every player-facing event are all
PresetMessage_Katana_*, the PLAYER's own sets -- unit-group-table.md already
names the link (DeltaSaber_T_Player carries msg=MessageSet_Katana).  Exact
partition: 13 voice all 14 events, 131 voice none, 0 voice some, and no
non-Katana table voices any of them.  CharacterKATANA is the only speaker
exclusive to the set.

Corrects preset-message-rules.md, which called those the wingman tables.  The
wingman roster is owned by isl-condition-builtins.md (UNITS: Bird1-Sandra ...
Rhino2-Katana, Rhino3-Ellen) and needed no experiment.

Residual: Katana_09_S10-1 and Katana_14_S16-2 voice none of the 14.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
2026-08-27 17:42:28 +00:00
Claude (auto)
b710c9b9f0 re: Interval and IntervalFluctuation are the cooldown; the chatter system closes
sub_82210C38 retires a line and is the only chatter function reading the rule's
+20/+24 as words: node[+16] = Fluctuation * rand01 + Interval frames, state
0x20.  Interval is a floor, Fluctuation a uniform additive jitter.

The queue node IS the cooldown timer -- the tick counts +16 down and only then
frees the slot, so the already-queued bail in sub_82210670 and the cooldown are
one mechanism: a speaker cannot repeat an event until its node expires.  A
lingering node still holds one of the 128 pool slots.

Also: +20 on the node is a ducking level, raised while a higher-priority line
plays and released on retirement.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
2026-08-27 17:28:57 +00:00
Claude (auto)
f78565fe9e re: the bark queue, Priority ordering, and Yes = a one-shot line
sub_822109B0 allocates 68-byte nodes from a pool capped at 128; the Pattern
byte at +23 doubles as the occupancy flag.  Priority orders the pending list
descending; the >=10 front-push arm is dead because the disc's range is 1..9.

The tick sub_8220FC50 does rule[+36] |= (1 << node[+25]) & rule[+32], so a
line enters the used mask only if its Yes bit is set: Yes = one-shot, No =
repeatable.  388 one-shot lines on the disc.  Closes what reads +32.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
2026-08-27 17:17:01 +00:00
Claude (auto)
8dc270091b re: the bark firing sites and the Probability roll
36 of the 64 event names are executable strings, so the string-xref join names
the eight sites that raise them; all eight share one entry, sub_8220FA98.
Probability is a per-cent roll -- the scaling constant at 0x820856F8 is exactly
100.0.  sub_82210670 suppresses an already-queued (speaker, rule) pair, treats
a message as eligible only if its bit is clear in the runtime mask at +36, and
picks uniformly, then hands Pattern/Priority/EffectiveTime to sub_822109B0.

Measured negative: the pick path never reads +20, +24 or +32.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
2026-08-27 17:04:18 +00:00
Claude (auto)
6ee041c1bf re: the chatter phase merge is not additive; 224 authored variants never play
sub_82215A58 reads CrewCount + PresetMessage_Phase1/2/3 and reaches the loader
from ONE call site, so the three phase tables fold into one map keyed by the
event record name.  Every merge collides; sub_82213840 reconciles on the
message list plus +16/+17/+18/+20/+24/+28 (NOT the +32 Yes mask), and the
incumbent always wins.  Measured: 26432 collisions, 26208 identical, 224
different (189 differ only in the message list), 0 mask-only differences.

Refuted handle: intersecting functions by the object's offsets finds dozens of
unrelated layouts -- offset shape is not an identifier.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
2026-08-27 16:50:42 +00:00
Claude (auto)
c4fd90baa9 re: the seven chatter fields, read off sub_82213980
The one function referencing all seven field-name strings is the rule table's
loader.  Interval / IntervalFluctuation / EffectiveTime are SECONDS, emitted
as *60 frame counts; Probability is a percentage and zero skips the record;
Pattern is a 4-arm enum of which only Sound and Window ship; the Yes/No pair
element is a u32 bitmask, which is why MessageCount is clamped to 32 (max on
disc is 26).  40-byte object layout recorded.  13 dead records characterised.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
2026-08-27 16:35:56 +00:00
Claude (auto)
6a81268cf1 re: all 144 chatter rule tables named; the undeclared 8 are NOT tutorial
Three more naming routes: strip _msg from a message table (137/144, superset
of route 1, zero non-rule hits), predict the name from the Sperkers roster
(6/6, control 0/4), and sweep the naming grammar (1/144).  Union 144/144,
and every rule table has its _msg companion.

Refutes the reading left by 2c2af7a: the undeclared tables are two story-stage
tables and six TCAF fleet/ship tables, no tutorial content at all.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
2026-08-27 16:22:19 +00:00
Claude (auto)
2c2af7ad78 re: the reactive-chatter rule table behind ORDOR_SQUADRON_EXTENDED
864 records = 144 rule tables per language pack x 6.  One schema for all
9216 event records; MessageCount*2 == positional count with zero mismatches.
Named 136/144 by two independent routes that agree as sets.  2388/2405
message ids join the settled sound-cue table.

Corrects squadron-orders.md: the executable misspells all four SQUADRON
entries of the 0x820AEEB0 enum as ORDOR_, and the disc data matches.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
2026-08-27 16:07:17 +00:00
Sylpheed RE agent
d1368c12db re: the objective/tutorial marker records; S24-S29 are CHALLENGE, not story
Correction first.  stage-numbering-and-player-craft.md called S24-S29
'story' and reported 22 story stages.  challenge-mission-gate.md OWNS
that split and already had it: S01-S16 story, S18-S23 tutorials,
S24-S29 challenge.  A third confirmation fell out of this iteration:
AUTO_SETTINGS names stage01..16_settings.tbl, tutorial01..06 and
challenge01..06, and its field tags are 1-16, 18-23, 24-29 -- the
shipped stage numbers exactly.  The campaign is 16 story missions, not
22.  AUTO_SETTINGS and IGNORE are likewise already owned by
isl-condition-builtins.md, so nothing there was new either.

What is new: the 11 marker records hud-config.md listed as unread.  12
named fields each, portable HUD data.  Colour is ARGB, three per marker
-- objectives red 0xFFFF0000, guard/waypoint green 0xFF20FF20,
Acropolis/tanker cyan 0xFF00FFFF, sub-objective yellow 0xFFFFFF00, and
every TutorialMarker_* amber 0xFFFFA010.  Each carries a Normal sprite
and an Emphasis part with optional Sub companions; HPGauge 0/1/2;
RadarCursorType Circle/Rectangle/blank; BlinkCycle 0x800 on all eleven.
TutorialTarget is Yes on exactly the five TutorialMarker_* and No on the
six ObjectiveMarker_*.

Parameters binds the roles: TargetMarker -> ObjectiveMarker_Target,
HelpMarker -> ObjectiveMarker_SubObjective, TutoTargetMarker ->
TutorialMarker_Target, plus ReloadDispTime 0.1.
Enumerate_ObjectiveMarkers is the 11-name roster.

Artefact +13 lines / 0 deletions; the other nine regenerate
byte-identical.
2026-08-27 15:46:31 +00:00
Sylpheed RE agent
15c9e13c12 re: CoverArea is a 6-bit MOUNT mask; the three other turret leftovers closed
Six bits, never more.  Over all 835 turrets: 41 distinct values, max
0x3f, bits 6/7 set on none; per-bit 709/117/274/274/260/237.  0x00 (34)
= the empty Weapon_NULL hardpoints with YawLimit 0; 0x01 alone (439) =
craft hardpoints with YawLimit 2.5/1.0/0; 2-6 bits (362) = warship
mounts with YawLimit 45-180.

It tracks the MOUNT, not the weapon.  UN_e107_ADAN_AAFrigate has eight
identical AAFrigate_AAGun turrets: GN_GunXS_01..04 are 0x1d and 05..08
are 0x2d -- same gun, same YawLimit 120, different mask.  The Battleship
spreads five masks over GN_TGunL_01..05 / GN_TGunM_02..03, all firing
the same CAF_Ship_ASGun.

Which sector each bit denotes is NOT determined; six bits and the name
invite +-X/+-Y/+-Z or six hull faces, but nothing static fixes the
convention.  Not adopted.  Refuted on the way: bits 2 and 3 are not a
mutually-exclusive pair -- 188 turrets set both.

The 26 weapons no turret mounts are a coherent set: 11 _P player
variants, the nose/twin mounts, two _Child sub-munitions, the three
S16Boss_*, three Weapon_Test_*, and ADAN_Attacker_S_GunTurret.

Versatile_NNN ships nowhere -- 0 populated records across all 41
archives, only the ??? template row.

The two units one turret short: both extras are missile mounts with no
Frame.  Elan_EX4 = NoseGun + Missile, both mask 0x01, both frameless;
AAFrigate_EX4 = eight framed guns plus one Ship_AAMissile at 0x0c with
no Frame.  n=2, stated as the observed pattern, not a rule.

New artefact and regenerator; the other nine regenerate byte-identical.
2026-08-27 15:35:09 +00:00
Sylpheed RE agent
27602a3032 re: BLOCKED -- the 2D / READY_ROOM TOC names, three routes closed with controls
1. A different hash family.  The corpus knows three (idxd-tag-hash.md):
name_hash, tag_hash, ixud_hash.  Scoring all 5977 harvested names x 6
prefixes: GP_TITLE 8/16 and GP_PAUSE_MENU 6/11 under name_hash (the
positive controls), and tag_hash and ixud_hash explain NOTHING anywhere
-- including the paks name_hash does explain.  So they are not the TOC
function, and the unnameable pair is not keyed by a different one.
GP_MAIN_GAME_E2D stays at 0/711 under all three.

2. The executable.  sylpheed.db's strings table holds 7140 rows, of
which exactly two look like asset paths -- Data\gmicon002_2.t32 and
Data\gmicon006_2.t32, in a Data\ directory nothing else on the disc
uses -- and neither resolves in any archive.  The binary is not the name
source; it holds two strays and no table.

3. Name transformations -- 13 of them on the 419 config paths, all 0.

The container runs out here.  Those TOC keys hash names that exist on
neither the disc nor the executable in readable form.  The only lever
left is a dictionary attack using name_hash's shape (top byte = the
character-sum checksum), and that needs a plausible name corpus this
disc does not contain.  Noted as blocked rather than improvised around.

The port does not need these names: sprites and bundles are readable by
content (T8aD, RATC), and the config records already say which asset
each HUD element uses.  Only the archive-key to name mapping is missing.

Artefact +22 lines / 0 deletions; the other eight regenerate
byte-identical.
2026-08-27 15:22:13 +00:00
Sylpheed RE agent
15b97af170 re: which archives the disc can name -- 100% for menu paks, 0.0% for the 2D paks and READY_ROOM
Chasing more prefixes for the 419 HUD config paths would have been the
same mistake twice, so this censuses the whole disc: harvest every
plausible asset-name string from every archive (6027), hash each under
the 16 known path prefixes, and ask per archive what fraction of its TOC
that explains.  idxd-container.md and idxd-tag-hash.md own the hash;
neither says which archives are reachable by it.

The result is bimodal.  GP_TITLE 16/16, GP_PAUSE_MENU 11/11,
GP_STAGE_CLEAR 44/44, GP_CHALLENGE 151/151, GP_MOVIE_THEATER 56/56,
MiscBin 40/40, GP_GAMEOVER, GP_BUNK, GP_SYSTEM, GP_TUTORIAL and fonts
are at 100%; tables.pak 78/79, GP_DIALOG 139/140, the six language paks
115/117; GP_MAIN_GAME_* 751/1119.  Then the cliff: the six
GP_MAIN_GAME_*2D.pak at 0 of 711 each, and GP_READY_ROOM at 6 of 1106 --
the largest UI pak on the disc, not previously noted anywhere.

Eleven paks at 100% in the same run is the control that makes 0.0% a
finding rather than a failed guess.

So the 419 HUD paths are not missing assets: nothing in the 2D paks is
reachable by name from the disc's own strings at all.  Those TOC keys
hash names that are not written anywhere readable.

Also refuted first: the 419 values under 13 name transformations, every
one scoring 0 against the E2D 711 and against all 16630 entries.

Not settled: what those names are.  The lever left is the hash's shape
-- the top byte is the character-sum checksum -- but that needs a name
corpus the disc does not contain.

New structure doc, artefact and regenerator; the other eight regenerate
byte-identical.
2026-08-27 15:08:24 +00:00
Sylpheed RE agent
bb37b16847 re: the in-game HUD config -- 16 records, 419 asset paths, a per-stage ResourceTable
Checked first: no docs/re file mentions ArmsStatus, RangeFinder, Radar,
Sight, Wing, NamePlate or ResourceTable.  Only HudResource had been
opened; the other fifteen records had not.

The six IDXD entries of GP_MAIN_GAME_E2D.pak: two carry the 16-record
HUD config, two the 13-record ObjectiveMarker_*/TutorialMarker_* set,
one Face (52 portrait sprites), one ResourceTable.  Between them they
name 419 distinct asset paths -- the whole flight HUD -- with new
subdirectory prefixes throughout (ArmsSt, ActvArm, RangeF, Marker,
Manuva, Map, Speed, Radar, Sight, Wing, Hitmark, Lockon, Info).

The decisive control uses the config's own exact path strings, so no
guessing is left in the loop: 419 distinct .prt/.t32/.tbl values, ZERO
resolve as a pak entry under 10 prefixes, and the four config filenames
resolve to nothing either.  The .t32 sprites certainly exist -- 574 T8aD
in that pak.  So the 2D pak is not addressed by name_hash of the name
its config uses.

This supersedes the earlier framing: the 28 'dangling' .prt names were
never a missing-asset story; they are 28 of a set where none of the 419
resolves.

ResourceTable is 58 positional fields = 29 pairs, alternating
HudResource.tbl / HudMarkerResource.tbl, identical in all six language
paks, with exactly one override at pair index 25 -- HudResource_S26.tbl.
Reading: indexed by stage number minus 1, so index 25 is S26, the one
stage with its own HUD config.  Arithmetic exact, indexing unproven, not
adopted.

New structure doc, artefact and regenerator; the other seven regenerate
byte-identical.
2026-08-27 14:53:49 +00:00
Sylpheed RE agent
dcf4945db5 re: the variant reading is refuted; HudResource is the in-game screen config
Censused the 32 bundle-less in-game .prt names with the 68 as control:
4 of 32 appear as an element of some 2D bundle, against 58 of 68.
Being an element is normal for a part that exists; these are not
elements either.  Last iteration's 'not adopted' reading is refuted.
28 names are neither a bundle nor an element.

They are not unreferenced: a HudResource record inside the 2D paks' own
IDXD entries names them -- the in-game screen config, parallel to
tables.pak for menus.  GP_MAIN_GAME_E2D.pak has 6 IDXD entries; two
carry HudResource (24 named fields) beside ArmsStatus, Map, ArmsItem,
RangeFinder, Marker, Manuva, Number; two more carry the
ObjectiveMarker_*/TutorialMarker_* set.

Its values carry subdirectory prefixes -- Hitmark\, Lockon\ -- which my
hand-written prefix list never had.  So I rebuilt the negative the right
way round: harvested every IDXD field value containing a backslash, kept
the 60 commonest directories, and re-swept.  241/376 with the hand list;
241 with hand + harvested -- zero new resolutions.  After the language\
miss, this is the closure that counts: the prefix list came from the
disc, not from me.

Still open: whether the 28 are cut features or assembled at runtime from
sprites.  Nothing static separates those two.

Artefact +45 lines / 0 deletions; the other six regenerate
byte-identical.
2026-08-27 14:42:20 +00:00
Sylpheed RE agent
1132d95227 re: an embedded .prt part is a top-level RATC bundle, addressed by its element prefix
Read first: ui-rat-layout.md OWNS the RATC stack and already documents
the 60-byte element declaration table and 'one bundle = one
(context x language) build of that screen'.  What it does not say is how
a part is addressed when it is NOT a pak entry.

The elements of one bundle share a common name prefix, and that prefix
is the part name.  GP_MAIN_GAME_E2D.pak has 130 bundles with 114
distinct element prefixes -- pgmenu_btn00, pghud_wing, pgface,
pghud_range, pgmanuva_eff0 -- exactly the in-game part names.

The 100 in-game-only .prt names match a 2D bundle prefix 68 times; the
control, the 241 shipped parts, matches ZERO.  The two families are
disjoint on the test.  13 shipped parts match a bundle prefix in their
own screen pak, which is the expected shape.

Four of the five mission banners land here: pgmsg_start.prt is E2D
bundle 0x89fac252, a one-element bundle declaring pgmsg_start_sub.rat;
likewise _end_, _failed_, _restart_.  pgmsg_update has no bundle at all,
consistent with having no _sub.rat.

32 in-game names still have no 2D bundle.  They cluster into families
whose base name is a bundle, reading as a variant declared inside a
parent bundle -- a reading, not adopted.

Artefact +38 lines / 0 deletions; the other six regenerate
byte-identical.
2026-08-27 14:27:38 +00:00
Sylpheed RE agent
e4000d9cb5 re: who references a .prt decides whether it ships -- menu named, in-game embedded
Censused the whole 135 unresolved set instead of chasing the five.
Cross-tab over all 376 .prt names: referenced by tables.pak AND
resolves 241; neither 100; referenced but absent 35; resolves WITHOUT
being referenced by tables.pak -- ZERO.

So resolving implies a tables.pak reference, 0 counterexamples of 376.
tables.pak holds the menu screen configs and every part it names ships
as <lang3>\<name>.prt.  The 100 names that appear only inside the
GP_MAIN_GAME_*2D.pak bundles never ship as entries -- the in-flight HUD
is authored into the bundles, not loaded by name.  The five pgmsg_*.prt
were never a special case; the split is menu = named + shipped,
in-game = embedded.

It is not a prefix rule: 6 of the 135 pg* names do resolve (pgloading,
pgloading2, pgmsg_scr, pgpause, pgpause_ttrl, pgpbase), because
tables.pak names them.

The 35-name residual is one coherent family -- pgmenu_btn*/item*/pad,
pgfacewin*, pgtextwin, phinfo1-3, 02d, 02d_scr, psview_release: the
in-game pause menu and squadron-order overlay, named by a menu config
but drawn from the in-game bundles.  Consistent with the split, not
separately proved.

Artefact +44 lines / 0 deletions; the other six regenerate
byte-identical.
2026-08-27 14:13:41 +00:00
Sylpheed RE agent
94065d40ec re: where .prt screen parts live; the five mission banners are a real exception
Read first, as the rule now requires: ui-prm-primitives.md,
ui-rat-layout.md, ui-screen-runtime.md -- they own the .prt/RATC/T8aD
stack but describe element names inside bundles, not where parts live.

A .prt IS a pak entry, under a LANGUAGE directory.  Of 376 distinct
.prt names referenced on the disc, 241 resolve as archive entries --
220 under eng\ and jpn\, 38 under each of the other four.  prbase.prt
resolves under all six; palogo1.prt under two.

All five pgmsg_*.prt resolve under nothing, across 15 prefixes that
include every language dir, so this is not the missing-prefix mistake
again.

A lead, refuted by its own control: four of the five have a same-stem
_sub.rat sub-bundle inside every per-language 2D pak, and pgmsg_update
has none -- but only 4 of 376 .prt stems have a _sub.rat, and
prbase.prt, which does ship as an entry, has none.  Not the container
convention, not adopted.

An intermediate pass nearly reported 'the five appear only in the
manifest'; that was wrong because the token census was truncated by a
slice and the RATC hits were pgmsg_start_sub.rat, a longer token.

New artefact and regenerator; the other six regenerate byte-identical.
2026-08-27 14:01:24 +00:00
Sylpheed RE agent
9d25a3a582 re: correction -- the dialogue table was already in the corpus, and one negative was false
The previous commit announced message\MissionDialogMessage.tbl as a
find.  structures/ixud-localised-text.md OWNS it and already says
S02_P1_OBJECTIVE and friends are record names in an IDXD map,
message\MissionDialogMessage.tbl, whose positional fields list the
lowercase per-line IXUD names -- and that *_GRAPH is the odd one, a
single named field holding a texture.  I grepped the manifest doc and
not the text doc.  Third overclaim in four days; the rule is now to grep
the doc that owns the DATA, not only the doc that owns the FILE.

Worse, the headline negative was wrong.  MissionDialog_local_string.tbl
DOES resolve -- as language\MissionDialog_local_string.tbl, IXUD, in all
six GP_MAIN_GAME_* paks.  My 33 prefixes omitted language\, which is
precisely the convention ixud-localised-text.md records for language
paks.  A prefix sweep is only as good as its prefix list, and the list
should come from the corpus.

'LOSE has exactly one entry' was wrong too.  Per kind: HINT_PAUSE 4,
HINT 3, LOSE 4, OBJECTIVE 4 positional fields, and GRAPH 1 NAMED field
holding the .t32.  The single-field bucket was GRAPH, not LOSE.

What survives as new: the 11 non-MISSION field values read as a block;
the GP_TEST and TEXTS sibling records; the per-stage phase census; and
the five pgmsg_*.prt resolving nowhere under 34 prefixes x 41 archives,
now with two working controls in the same sweep.

Artefact 8 insertions / 3 deletions, every deleted line replaced by its
corrected form; the other five regenerate byte-identical.
2026-08-27 13:45:35 +00:00
Sylpheed RE agent
acbcd8d75c re: Stage\script.tbl's 11 non-MISSION fields, and the mission dialogue table
structures/mission-script-ssb.md owns this manifest and names its 40
fields but never read the 11 that are not MISSION<n> = StageNN.ssb.
They are DIALOG_MESSAGE, DIALOG_LOCAL_STRING, FONT (+size), TEXT_POS,
TEXT_LINES and the five pgmsg_*.prt.  Two sibling records were also
unread: GP_TEST (PATH = dat\GP_TEST\, a debug archive not on the disc)
and TEXTS (a second text style).

Probed 7 values x 33 prefixes x 41 archives.  One resolves:
message\MissionDialogMessage.tbl, in all six GP_MAIN_GAME_* paks --
200 records, 25280 bytes, every name S<NN>_P<n>_<KIND> with five kinds
40 each (HINT_PAUSE, HINT, OBJECTIVE, GRAPH, LOSE), fields positional
and tagged 0..3, each value a message key.  An index from (stage,
phase, kind) to the localised strings, on the same S<NN>_P<n> keying
the ISL corpus already uses.

Control: the 40 stage-phases span stages 1-16 and 24-29 -- a subset of
the 28 shipped, and the six with no hints are exactly 18-23, the
tutorials.  A fifth independent route to the story/tutorial split, and
it gives the phase count per stage.

The other six do not resolve, with the control in the same sweep:
MissionDialog_local_string.tbl and all five pgmsg_*.prt are not a pak
entry under any of the 33 prefixes, while
message\MissionDialogMessage.tbl and Stage\script.tbl both resolve in
6 archives.

name_hash is CASE-INSENSITIVE (message\ == Message\); tag_hash is not.

New structure doc, artefact and regenerator; the other five artefacts
regenerate byte-identical.
2026-08-27 13:37:31 +00:00
Sylpheed RE agent
ccdc226022 re: the stage numbering is consistent everywhere; the manifest still declares the cut S17
Correction first: the previous commit claimed the corpus 'did not know
the stage NUMBERS'.  It did -- structures/mission-script-ssb.md has
recorded S01-S16, S18-S29, S17 absent by a three-way convergence (hash
lookup, the table sweep, the loader's != 16 guard) since before that
pass.  What was actually new there is the player craft per stage and
ResourceID's ordinal tags; the numbering is a fourth independent route,
not a discovery.

The question this item asked: do the ISL StageNN.ssb files and the
UnitGroup_SNN.tbl tables use the same numbering, or is something
silently mis-labelled?  They are identical.  Stage\StageNN.ssb,
stage\UnitGroup_SNN.tbl and stage\Route_SNN.tbl all resolve for the
same 28 N with an EMPTY symmetric difference; AIParams is the same minus
18-23.  Nothing in the corpus is mis-keyed.  Formation_SNN and
Message_SNN resolve 0/40 -- those are field names, not files, and the
four families above are the controls that make that negative real.

New: Stage\script.tbl's SCRIPTS record lists 29 MISSION*.ssb fields --
MISSION1..MISSION29, including MISSION17 = Stage17.ssb -- and only 28 of
29 name a file that ships.  Control: MISSIONn -> Stage<n>.ssb with the
same n, 0/29 mismatched.  So S17 is a cut mission the shipping manifest
still names, which is why the loader carries an explicit != 16 guard
rather than just running 0..27.

Artefact +14 lines / 0 deletions; the other four regenerate
byte-identical.
2026-08-27 13:27:19 +00:00
Sylpheed RE agent
28a59d839f re: the disc's stage numbers, and the player's craft per stage
The corpus knew '28 stages' and 'six tutorials with no AIParams'.  It
did not know the NUMBERS, and they are not 1..28.  Hashing
stage\UnitGroup_S%02d.tbl for N=0..39 against GP_MAIN_GAME_E.pak:
1-16 and 18-29 ship, 17 does not -- 28 files -- and the six with no
AIParams_SNN.tbl are exactly 18-23.  So S01-S16 story, S17 absent,
S18-S23 tutorials, S24-S29 story: 22 + 6.

eng\GP_HANGAR_ARSENAL_3D.tbl's ResourceID record keys a player-craft
mesh by stage: Unit_St1_6 -> rou_f001 (DeltaSaber T), Unit_St7_16 ->
rou_f002 (W), plus six further fields tagged with the raw numbers
24,25,26,27,28,29 -> rou_f002 x4, rou_f004 (DeltaSaber A) at 28,
rou_f002.  The bare tags are the last six story-stage numbers.  Control
in the same record: tag_hash(name) == tag for 11/11 named fields, so
those six genuinely carry no name.

Cross-check from a different file: grouping the Arsenal pak's 168
stage-scoped entries by which _Player craft their loadout mounts gives
f001 = 6, f002 = 15, f004 = 1, tutorials 5+1 = 6, and every number
closes against ResourceID -- 6 = Unit_St1_6; 15 = Unit_St7_16 (10) plus
tags 24,25,26,27,29 (5); 1 = tag 28; 6+15+1+6 = 28.

So the player flies the DeltaSaber A in exactly one mission, S28, and
the DeltaSaber T only for the first six.

Not settled: the Arsenal entry filenames, so which of the 168 is S24 vs
S25 is constrained but not pinned.

New structure doc, artefact and regenerator; the other five artefacts
regenerate byte-identical.
2026-08-27 13:18:42 +00:00
Sylpheed RE agent
2b9709eaa0 re: who selects a loadout -- the pak entry does; the Arsenal pak is stage-scoped
Settled by elimination with two controls.  The 15 loadout names appear
as a field VALUE nowhere on the disc -- 0 occurrences across every pak
-- while the control Arbalest_155KG, which is referenced, appears 150
times as a value in the same pak.  They occur only as the field names of
the UNITS record.  They are not in the executable either; control:
WEAPONS (a section key) is, Arbalest_155KG is not.

So nothing references a loadout by name.  The selection is which pak
entry the Hangar loads.  GP_HANGAR_ARSENAL.pak has 180 IDXD entries in
10 shapes; 168 carry a UNITS roster = 28 stages x 6 languages, and the
other 12 = 2 x 6.  Each entry is one stage's whole Hangar config, and
its UNITS roster is the flight for that stage -- 5 distinct rosters over
15 / 5 / 5 / 2 / 1 stages, including one where Rhino2-Katana flies
alone.  Every count is a multiple of 6 and they sum to 28, the disc's
stage count by a fourth independent route.

Corrects the previous commit: it read the pak with setdefault, so
'15 loadouts / 24 allow-lists / STANDARD_ARM1 has 11 entries' is the
union of first-seen records, not one table.  Contents vary per entry --
PlayerSET_ARM1 has 5 distinct contents, STANDARD_ARM1 3, ExSET_NOSE 4.
The chain and both controls (60/60, 70/88) are per-record and
unaffected; only the per-list sizes were over-generalised.

Not settled: the entry filenames.  name_hash probing over 8 templates x
40 indices x 6 languages resolved 0, while the same probe's controls
<lang>\weapon.tbl and <lang>\strings.tbl resolved 12/12.

Artefact +40 lines / 1 changed heading; the other four regenerate
byte-identical.
2026-08-27 13:09:07 +00:00
Sylpheed RE agent
2b2d984f0c re: the Hangar loadout system -- loadout, allow-list, arsenal item
15 loadout records, one per flight position x pilot (Bird1-Sandra ..
Rhino4-Yoji), each with Arm1/Arm2/Arm3/Nose + UnitID.

The same trap as PlayerWeapon, one level up: Arm1/Arm2/Arm3/Nose do NOT
name items.  They name a per-slot ALLOW-LIST record -- one of 24 whose
only named field is Type (the slot kind) -- and the candidate items are
that record's positional, unnamed fields, in order.  Four hops:

  Rhino4-Yoji.Arm1 -> STANDARD_ARM1 -> [Falcon_9AM, Condor_105AM, ...]
  -> item.PlayerWeapon = Turret_NNN -> slot.WeaponID -> Weapon.ID

Controls: Arm1/2/3/Nose -> allow-list record 60/60; allow-list
positional entries -> arsenal item 70/88, and every one of the 18
misses is the single sentinel No_Equipment -- one of the four
WEAPONS-roster values with no item record, i.e. the empty-slot marker.

UnitID is two ID spaces at once: 5 rows name a unit Generic.ID (the
three -Katana rows are the player -- a _Player craft plus an extra,
empty PlayerUnit field), 8 name a character, resolving as Character +
the value into the 64-record character table.

Two values resolve to nothing, both single rows against 13 that do:
Rhino2-Ellen.UnitID = UN_f001_TCAF_DeltaSaber_W exists nowhere (checked
as a Generic.ID across every pak and as a record name, 0 hits) while
UN_f002_TCAF_DeltaSaber_W does -- consistent with a shipped typo,
reported not diagnosed -- and Rhino4-Brandon.UnitID = BRANDON has no
CharacterBRANDON among the 64.

New structure doc, artefact and regenerator; the other four artefacts
regenerate byte-identical.
2026-08-27 12:58:53 +00:00
Sylpheed RE agent
21e710a531 re: the 59-of-131 arsenal question is closed -- an item names a hardpoint
An Arsenal item does not reference a Weapon record.  It references a
Turret_NNN HARDPOINT SLOT on the player craft's own unit table, and the
slot is what carries the WeaponID.  Three hops:

  Arbalest_155KG.PlayerWeapon -> Turret_050  (a slot on
  UN_f001_TCAF_DeltaSaber_T_Player) -> .WeaponID ->
  Weapon_DSaber_P_wep_50_Cannon

Controls, both in the same loop: 0/59 distinct PlayerWeapon values are a
Weapon.ID; 59/59 are a Turret_NNN slot id; the full chain lands on a
Weapon.ID 59/59.  WingmanWeapon resolves identically.  The WEAPONS
roster's 59 = 55 item names + 4 empty-slot sentinels.

Wingmen fly a cheaper gun: following the same 59 slots across craft
variants, the _Player tables give each item its own weapon record (59
distinct) while the AI tables collapse all 59 onto 10 generic classes.
That is most of the 131.

Upgrades yesterday's 'hardpoint catalogue' reading from 21 to adopted,
proved from an independent file, and corrects its '10 distinct WeaponID'
figure -- that was the AI variant, not the player's.

Also adds an __main__ guard to unit_substructures.py so importing
pak_entries from it does not run its report; its artefact is unchanged
and still byte-identical.
2026-08-27 12:49:22 +00:00
Sylpheed RE agent
4a5e5d55e6 re: the destructible-subsystem model -- a unit's sub-records
The corpus has named these since unit-struct-runtime.md but never
opened them.  Per unit table: Turret_NNN 835 records (max 63 on one
unit), ShieldGenerator_NNN 46, Thruster_NNN 38, Hatch_NNN 26,
Bridge_NNN 25, plus one each of Shield/Mass/SE/Explosion/
StructureCount and NS_Body on 68 of 114.

Turret/ShieldGenerator/Thruster/Hatch/Bridge are ONE record shape: a
shared 19-field destructible-part base (ID, Name, ParentStructureID,
Frame = a mesh NODE name, NomalModel, CollisionModel, Radius, HP, the
four Is* flags, SpreadDamage, damaged/destroy motion + time, and the
three Effect_*), with per-kind extras.  Turrets add WeaponID,
AngularVelocity, YawLimit, PitchLimit_Elevation/_Depression, CoverArea,
IsAuto, HasBarrel and up to 80 CannonModel_NNN/CannonFrame_NNN.  Shield
generators, thrusters and bridges add PowerRatio.  Hatches add
SquadronID, LoadedCount, MaxAvailableCount, TakeoffInterval -- a
carrier's launch bay.

Control 1: StructureCount.<Kind>Count == #<Kind>_NNN records, over 684
comparisons -- 612 equal, 55 "0 declared, one blank placeholder"
(55/55 blank in Name AND NomalModel AND Frame), 11 differ, 6 kind
absent.  All 11 exceptions are Turret and all are declared < records.

Control 2: 835/835 Turret_NNN.WeaponID resolve to an ID in the
131-record Weapon datasheet, zero unresolved; 26 weapons are never
mounted on a turret.

Refuted in the same pass: "the DeltaSaber's 59 non-NULL hardpoints are
the 59-name WEAPONS arsenal roster".  The counts match exactly and the
sets overlap in 0 values -- two namespaces, one coincidence.

New structure doc, artefact and regenerator; other artefacts unchanged.
2026-08-27 12:39:50 +00:00
Sylpheed RE agent
39b3a4de7d re: correction -- Generic is a per-FILE header record, not a table
Yesterday's page said "Generic (394 per pak) is the unit datasheet".
Only 114 of the 394 are.  Every IDXD file carries exactly one Generic
record and its schema is set by what kind of file it is.  Partitioned
by field set, identically in all six GP_MAIN_GAME_*.pak:

  114  has HP                        -- a unit datasheet
  204  {Count} only                  -- a dialogue file
   64  {ID, Name, SideID, Unique}    -- a character (36 TCAF + 28 ADAN)
   10  {EnumAsteroidGroup}           -- an asteroid group
    2  degenerate

204+114+64+10+2 = 394, and 178 distinct Generic.ID = 114 unit + 64
character, the only two shapes carrying an ID.  That settles the
"178 IDs vs 394 records" question the previous entry left open.

Positive control in the same loop: for the 204 dialogue headers Count
equals the number of Message_NNN siblings, 204/204, zero mismatches.

Cross-check from the other side: Maneuver = 114, Effect = 114, the
carrying-entry sets are identical, every unit Generic has a Maneuver
sibling, and Generic.Type splits 43 Craft + 71 Vessel -- the same
43/71/114 unit-struct-runtime.md reached from live guest memory.

New tool generic_partition.py + artefact; ISL artefacts byte-identical.
2026-08-27 12:29:30 +00:00
Sylpheed RE agent
69e6f6bcd8 re: the static unit datasheet and the AI flight model
Same technique as the weapon tables -- field names are literal, so a
field-shape search over 190782 records finds the carriers.

Generic (394 per pak, 110 field names, 178 distinct IDs) is the unit
datasheet: HP, ShieldRatio, DefencePoint, AttackCraft/VesselPoint, the
five Resistance* terms, Size_X/Y/Z + radius, RadarRange 10000,
FCSRange 8500, MountedFCS, MountedShieldGenerator, Score/Damage/Mass,
NozzleCount with per-nozzle FX, and Model rou_e006 -- which ties the
sheet to the mesh names the corpus already decodes.

Maneuver (114) is the AI flight model: MaximumVelocity 1200,
CruisingVelocity 700, Acceleration 600, Deceleration 400,
SideThrustAcceleration 1000, Turn_AngularVelocity 180,
MaximumBank_Normal 60, per-axis DragFactor 3.0, afterburner and
reverse-thrust factors, plus nine named manoeuvres each with its own
timing/ratio/length bounds and AA_/AV_ rate pairs.  This is the static
source for what flight-speed-law.md measured at runtime.

Corrects a guess in the same pass: HP_CLASS/HP_ID are not ship stats --
they sit on records named for wingmen and ship classes alongside
HPGauge/RadarCursorType, i.e. a HUD gauge binding table.

New structure doc + artefact; ISL artefacts byte-identical.
2026-08-27 12:20:03 +00:00
Sylpheed RE agent
f2cacce59d re: the static weapon datasheet -- Weapon, Shell, AssortMissileParam
The corpus's weapon numbers came from a runtime capture; the static
tables are now read with no emulator.  The move: IDXD field names are
literal strings in the pool, so the stat-shaped keys already harvested
from the disassembly (MegaTons, GuidanceType, SpiralType, ...) can be
searched directly.  8648 records carry at least one, clustering on
three record names in every per-language GP_MAIN_GAME_*.pak:

  Weapon              131 records, 21 fields -- the launcher
  Shell               131 records, 37 fields -- the projectile
  AssortMissileParam    9 records, 20 fields -- missile guidance

131, not the 59 of the WEAPONS roster: that roster is the arsenal
menu's list, not the full set.

Controlled negative in the same pass: stageNN_settings.tbl does not
exist.  AUTO_SETTINGS names 28; four path prefixes give 0/116 while
both controls in the same loop are found.

Also decoded: EnumUnit (54 unit ids tying eNNN/fNNN mesh prefixes to
faction/class names), ArmsItemFile (59 weapon -> HUD icon mappings),
SETTINGS (sound config), Parameters (HUD markers).

New structure doc + artefact; ISL artefacts byte-identical.
2026-08-27 12:11:03 +00:00
Sylpheed RE agent
2a1236074b re: decode the config records -- render/input constants, and a correction
parse() + named() on each named record (data/config-records.txt).

The find, directly portable: Rendering carries the tone-mapping and
bloom numbers (Brightness 1.4, Contrast 1.0, ExposureKeyValue 0.18,
BrightPassThreshold 0.6, LuminanceMin/Max 0.15/3.6, BloomScale 0.5,
AfterimageScale 0.05, StarScale 1.20, GlareType 8), ControlTweak the
input constants (CameraSpeed 0.50, TargetSpeed 0.50, TargetMovingRange
-10..10, mov_trigger_play 20, eye/mov_stick_play 6000), and Camera
NoseCameraFOV 0.92 / ChangeTime 0.20.

Corrects my own last entry: WEAPONS is a 59-entry NAME ROSTER, not a
stat sheet, and UNITS is not units at all -- seven craft-slot/pilot
pairs, the wingman roster.  IGNORE is a 13-name blocklist over the
weapon enumeration.

AUTO_SETTINGS lists exactly 28 stageNN_settings.tbl files -- a third
independent confirmation of the 28-stage count.

The objective-shaped records are UI: SUB_OBJECTIVE is a HUD layout (two
variants ship), SQUADRON_ORDER_OBJECT is 29 .prt sprite names, MISSIONS
holds the challenge categories, STAGES has zero fields.

Docs + one new artefact; ISL artefacts byte-identical.
2026-08-27 12:02:13 +00:00
Sylpheed RE agent
bc48fa576d re: the config sections are real disc data -- 64/65, with a clean 0/54 control
Testing every literal key from data/config-keys.txt against the 3496
distinct IDXD record keys with tag_hash:

  section names (arg to sub_82448AA0) -> 64 / 65 present
  field  keys  (arg to sub_824482D0) ->  0 / 54 present

The two classes are each other's control and the dissociation is
total, confirming the model: sub_82448AA0 finds a RECORD keyed by
tag_hash(name); sub_824482D0 reads a FIELD whose name is a literal
string in the pool, so none of them can be a record key.

The single absent section is LANGUAGE -- the one real section in the
disc-root config.ini.  The same API serves both stores.

This deflates my own earlier framing: I had listed "64 sections whose
hardcoded fallbacks are worth pinning".  They are not fallbacks; all
but one are real data.  STAGENN_UNIT_MAX is the genuine exception,
because its name is built at runtime from the stage index.

The artefact now names the pak each record lives in -- notably
GP_HANGAR_ARSENAL.pak holds WEAPONS, UNITS, Camera, ControlTweak,
Rendering, AUTO_SETTINGS and IGNORE.

Docs + regenerated artefact; ISL artefacts byte-identical.
2026-08-27 11:54:36 +00:00
Sylpheed RE agent
49b5892df5 re: Stage30-33 do not exist; AIParams is missing for exactly the tutorials
Probing pak entry names with name_hash across all 41 paks:
UnitGroup_SNN.tbl exists for exactly S01-S16 and S18-S29 = 28 stages.
S17 and S30-S33 are absent, so the 33-entry STAGENN_UNIT_MAX table is
headroom over a 28-stage game.  Control is built in: 28 hits from 33
probes shows the loop resolves entry names.

Corroboration that fell out of it: AIParams_SNN.tbl exists for S01-S16
and S24-S29 but is missing for exactly S18-S23 -- the six stages the
corpus identified as tutorials by a completely different route (their
lone guard is request_next() != 1).  Two unrelated methods, same six.

Stage_SNN.tbl probes find 0, which is expected: stagetbl.py documents
that the per-stage record is not name-addressed.

Drops the [phase+356]/[phase+360] consumer question: 243 non-stack lwz
reads from those offsets image-wide, and the only ISL-region hits are
the two writers plus two vtable calls.  Not a cheap static question.

Docs only; artefacts byte-identical.
2026-08-27 11:40:29 +00:00
Sylpheed RE agent
731c3cd849 re: the section reader is a generic IDXD accessor; UNIT_MAX absence re-tested
Resolving the r4 string at every sub_82448AA0 / sub_824482D0 call site
gives 65 section names and 54 int keys (data/config-keys.txt) -- far
more than the 400-byte config.ini holds -- and BASE_INFO, SYSTEM,
MISSIONS and FONTS are all present as IDXD record keys on the disc.  So
it is a generic named-section accessor over the .tbl containers, not
"the INI reader" as I labelled it yesterday.

My first re-test was invalid: IDXD record keys are tag_hash, not
name_hash.  Scanning with name_hash returned 0 for STAGENN_UNIT_MAX and
0 for every control name too -- the control is what exposed it.

Redone with tag_hash: 7750/7750 entries parsed, 190782 records, 3496
distinct keys, controls BASE_INFO/SYSTEM/MISSIONS/FONTS found, and
STAGE01..33_UNIT_MAX -> 0 records.  With the literal search and
config.ini printed in full, the section exists nowhere on the disc, so
PLANE=200 / VESSEL=20 for every stage now rests on a controlled test.

Docs + one new artefact; ISL artefacts byte-identical.
2026-08-27 11:33:30 +00:00