Files
Sylpheed/docs/re/structures/unit-datasheet-static.md
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

8.2 KiB
Raw Blame History

The STATIC unit datasheet and AI flight model — Generic, Maneuver, Effect

Found the same way as the weapon tables: IDXD field names are literal strings in the pool, so a field-shape search over all 190 782 records finds the carriers without guessing a filename. This is the static source for what the corpus had only measured at runtime — flight-speed-law, flight-controls-runtime, unit-struct-runtime.

CORRECTION (2026-08-27) — Generic is a per-FILE header, not a table

This page first read "Generic (394 per pak) is the unit datasheet". Wrong: only 114 of the 394 are. Generic is the record name every IDXD file gives its own header record — exactly one per pak entry — and its schema is set by what kind of file it is. Partitioned by field set, in each of the six GP_MAIN_GAME_*.pak identically:

Generic shape per pak what the file is
has HP 114 a unit datasheet (Maneuver, Effect, Shield, Mass, SE, Explosion, Turret_00N siblings)
{Count} only 204 a dialogue fileCount is the number of Message_NNN siblings
{ID, Name, SideID, Unique} 64 a character (Faces sibling) — 36 TCAF + 28 ADAN
{EnumAsteroidGroup} 10 an asteroid-group file
0 and 1 field 2 one empty, one naming eff_n0071

204 + 114 + 64 + 10 + 2 = 394, and 178 distinct Generic.ID = 114 unit + 64 character — the only two shapes that carry an ID. Positive control in the same loop: for the 204 dialogue headers, Count equals the number of Message_NNN siblings 204 / 204, no mismatches.

🔑 The 114 is not a coincidence: Maneuver = 114, Effect = 114, the entry sets are identical, every unit Generic has a Maneuver sibling, and Generic.Type splits 43 Craft + 71 Vessel — reproducing the count unit-struct-runtime reached from the other side. Artefact ../data/generic-record-partition.txt, tool tools/re-capture/generic_partition.py.

record per pak field names distinct IDs
Generic (unit shape) 114 67 max on one record 114
Generic (all shapes) 394 110 pooled 178
Maneuver 114 101
Effect 114 9

Repeated identically in every per-language GP_MAIN_GAME_*.pak. A unit's Generic, Maneuver and Effect share a pak entry — one entry is one unit's .tbl, which is why the runtime flattens all of them into a single object.

⚠️ Generic is the game's own record name, not a placeholder — it is also one of the 65 section names in data/config-keys.txt.

Generic — the unit datasheet

Sample UN_e006_ADAN_Vindicator_MargrasF:

ID / Name / Type Craft / UncertainName Craft
HP 6400.0        ShieldRatio 1.0      DefencePoint 0.0004
AttackCraftPoint 1.0                  AttackVesselPoint 0.001
ResistanceToShell 0.1   ResistanceToExplosion 0.5   ResistanceToOptics 0.8
ResistanceToPlayer 1.0  ResistanceParalyze 0.99
Size_X/Y/Z 12.0 / 5.0 / 30.0          Size_Radius 15.0
RadarRange 10000.0      FCSRange 8500.0
MountedFCS No           MountedShieldGenerator Yes
ScorePoint 4000   DamageScore 640   MassScore 30
HQRatio 1.0       ThrusterRatio 1.0   IsDestructible Yes
Model rou_e006    CollisionModel      Color_R/G/B 1.0 / 0.5 / 0.1
NozzleCount 3 + per-nozzle Jet / AfterBurner / ReverseThruster / Contrail
                 Fx model & frame names, NozzleSpec_00N

🔑 Model rou_e006 ties the datasheet straight to the mesh names the corpus already decodes, and Size_* / RadarRange / FCSRange are in the corpus's 1 metre world unit — a 10 km radar, an 8.5 km fire-control range, a 12 × 5 × 30 m craft.

Maneuver — the AI flight model

The single richest find for the port. Sample, same unit:

MaximumVelocity 1200   MinimumVelocity 100.0   CruisingVelocity 700
Acceleration 600       Deceleration 400
SideThrustAcceleration 1000.0            SideThrustVelocity_Max 1000.0
Turn_AngularVelocity 180.0               MaximumBank_Normal 60.0
YawDragFactor / RollDragFactor / PitchDragFactor 3.0
DragFactorThreshold 0.5
ArterBurner_Vc 2.5  ArterBurner_Acc 2.0      (the game's own spelling)
ReverseThrust_Vc -0.5   ReverseThrust_Acc 2.0
UsingChaffRatio 0.5     HomingResistAdjustment 0.8

and a set of named manoeuvres, each a Yes/No switch plus its own timing, ratio and length bounds:

TurnAttack   + _CutoffRatio _DoubleRatio _DoubleTimeMin/Max
TurnAway     + _Time_Minimum/Maximum
BarrelRoll   + _CountMinimum/Maximum _Radius _Time
SideRoll     + _Time _Length
Slalom       + _CutoffRatio _TurnCount_Min/Max
Through      + _Angle/Length Minimum/Maximum _Time1/2 Min/Max _CutoffRatio
HoldPosition + _MinimumTime _MaximumTime _CancelTime _CutoffRatio
               _BackRatio _SideRatio _LengthMin/Max
SolidCutoff  + _LengthMin/Max _Ratio
BoostAway    + _Time_Minimum/Maximum
CutoffTimeMin/Max
AA_* and AV_* Pitch/Yaw/Roll Min/Max pairs

🟡 The AA_ / AV_ prefixes pair with AttackCraftPoint / AttackVesselPoint, so anti-air vs anti-vessel rate limits is the obvious reading — still not adopted. See below for what the executable does say.

The two families are one interleaved block (2026-08-27)

Exactly one function in the image references the AA_/AV_ name strings: sub_822F9498, the unit-definition loader, itself called only from sub_821A6CF0. Mapping each addi rN, r29, -offset (the name) to the stfs that follows gives the definition-object layout, and it is a clean interleave — AV_ at X, AA_ at X+8, for all five axes:

axis AV_Max / AV_Min AA_Max / AA_Min
PitchPlus +196 / +200 +204 / +208
PitchMinus +212 / +216 +220 / +224
Yaw +228 / +232 +236 / +240
Roll +244 / +248 +252 / +256
AxisMode +320 / +324 +328 / +332

The constant +8 displacement is the structural point: whatever selects between the families does it as an offset of 0 or 8, not as two separate lookups. The loader reads 78 named float fields in total.

Data side: the ten suffixes match exactly between the two families (checked as a list equality, not a count), with 690 records each for the eight rate fields and 36 each for AxisMode. Where the values differ, AA_ is consistently the largerAA_AxisMode_Min 100 vs AV_ 35, AA_Yaw_Max 50 vs AV_ 25, AA_PitchPlus_Min 100 vs AV_ 200 being the exception that keeps this a description, not a rule.

⚠️ The 20 strings exist twice in the image, at 0x82085B98… and 0x8209F674…. The loader uses the second block; the first 20 are referenced by nothing.

🔴 The selection is still not found

Looking for the consumer by offset does not work here: functions loading two or more of +196/+204/+320/+328 number 39 across the image, and those offsets are far too common to identify anything — the same trap the corpus has paid for before. Only two of the 39 are call-graph-reachable from the loader's owner (sub_821A6CF0 itself, and sub_821AB650); that is the next handle, and it was not followed this iteration. So "anti-air vs anti-vessel" remains a reading.

Effect

ShieldHitEffectName, ShieldRecoverEffectName, Effect_Paralyze, plus JumpIn / JumpOut / FadeIn / FadeOut slots.

🟡 Not settled

  • No cross-check against the runtime captures — this locates and reads the tables; it does not reconcile them field by field.
  • HP_CLASS / HP_ID turned out not to be ship stats: they sit on records named for wingmen and ship classes (CARL, ELLEN, SANDRA, ACROPOLIS, Carrier_TCAF1, NP_Facility, …), 120144 instances each, alongside HPGauge / HPGaugeSub / RadarCursorType on the ObjectiveMarker_* and TutorialMarker_* records — a HUD gauge binding table, not a datasheet.
  • 178 distinct Generic IDs against 394 records per pak — the surplus is unexplained here. Settled 2026-08-27 — see the correction at the top.
  • The Generic field-name count of 110 is a POOL across all four shapes; no single record carries more than 67.