re(route-b): 13 defaulted Size_Y values read out of the running game
Live definition objects carry no name, so identity comes from the values: unit_signatures.rs prints (HP, Size_X/Y/Z) for every disc unit and vessel, and a live object is the record whose known fields it reproduces. 7 of 14 match a disc record outright; the other 7 match nothing because their records default a field -- 18 of 23 vessel records are missing at least one, nearly always Size_Y. Matching each single-default record against the live object that reproduces its remaining fields resolves 13 values (Destroyer 200, Cruiser 600/700, ASFrigate 80, ADAN Destroyer 300, Acropolis 400, ISCMissile 300, plus _Inv/_EX variants). Every one equals that unit's Size_X, so the engine's own parsed definitions confirm the statically-derived inheritance rule. Limits stated: variants share one live object, and the ...Ratio/...Count family is not resolved -- their offsets in the live object are still unknown. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
33
crates/sylpheed-formats/examples/unit_signatures.rs
Normal file
33
crates/sylpheed-formats/examples/unit_signatures.rs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
//! (HP, Size_X, Size_Y, Size_Z) for every craft unit and vessel on the disc.
|
||||||
|
//!
|
||||||
|
//! A live definition object in guest RAM carries HP at `+0x54` and the sizes at
|
||||||
|
//! `+0x30/34/38` but no name in its first words, so identity has to come from the
|
||||||
|
//! values themselves: this is the lookup table for that match.
|
||||||
|
//! Run: unit_signatures <disc-root>
|
||||||
|
use sylpheed_formats::game_data::{load_units, load_vessels};
|
||||||
|
use sylpheed_formats::PakArchive;
|
||||||
|
fn main() {
|
||||||
|
let disc = std::env::args().nth(1).expect("disc root");
|
||||||
|
let pak = PakArchive::open(format!("{disc}/dat/GP_MAIN_GAME_E.pak")).expect("pak");
|
||||||
|
println!("{:<34} {:>9} {:>8} {:>8} {:>8} kind", "id", "hp", "size_x", "size_y", "size_z");
|
||||||
|
for u in load_units(&pak) {
|
||||||
|
println!(
|
||||||
|
"{:<34} {:>9} {:>8} {:>8} {:>8} unit",
|
||||||
|
u.id.unwrap_or_default(),
|
||||||
|
u.hp.map(|v| v.to_string()).unwrap_or("—".into()),
|
||||||
|
u.size_x.map(|v| v.to_string()).unwrap_or("—".into()),
|
||||||
|
u.size_y.map(|v| v.to_string()).unwrap_or("—".into()),
|
||||||
|
u.size_z.map(|v| v.to_string()).unwrap_or("—".into()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
for v in load_vessels(&pak) {
|
||||||
|
println!(
|
||||||
|
"{:<34} {:>9} {:>8} {:>8} {:>8} vessel",
|
||||||
|
v.id.unwrap_or_default(),
|
||||||
|
v.hp.map(|x| x.to_string()).unwrap_or("—".into()),
|
||||||
|
v.size_x.map(|x| x.to_string()).unwrap_or("—".into()),
|
||||||
|
v.size_y.map(|x| x.to_string()).unwrap_or("—".into()),
|
||||||
|
v.size_z.map(|x| x.to_string()).unwrap_or("—".into()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
115
docs/re/captures/disc-unit-signatures.txt
Normal file
115
docs/re/captures/disc-unit-signatures.txt
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
|
||||||
|
|
||||||
|
id hp size_x size_y size_z kind
|
||||||
|
UN_e006_ADAN_Vindicator_MargrasF 6400 12 5 30 unit
|
||||||
|
UN_be005_ADAN_SpaceFortress 10000 2500 3100 2600 unit
|
||||||
|
UN_f001_TCAF_DeltaSaber_T 1000 10 7 29 unit
|
||||||
|
UN_f002_TCAF_DeltaSaber_W 1000 10 7 29 unit
|
||||||
|
UN_e009_ADAN_Phantom 1000 18 16 24 unit
|
||||||
|
UN_e001_ADAN_Elan_EX4 500 25 10 30 unit
|
||||||
|
UN_e011_ADAN_Attacker_B 1500 200 50 100 unit
|
||||||
|
UN_f001_TCAF_DeltaSaber_T_Ttrl 1000 10 7 29 unit
|
||||||
|
UN_f202_TCAF_Cargo 4000 1400 600 2000 unit
|
||||||
|
UN_e007_ADAN_Turret_EX4 100 20 9 22 unit
|
||||||
|
UN_e010_ADAN_Attacker_S 500 100 40 50 unit
|
||||||
|
UN_be001_ADAN_TerrafoamingUnit 10000 2600 2200 — unit
|
||||||
|
UN_e005_ADAN_ElanTypeQ_Margras 3200 24 15 38 unit
|
||||||
|
UN_e001_ADAN_Elan_GR_Violeta 1200 25 10 30 unit
|
||||||
|
UN_e004_ADAN_ElanPlus_N 800 24 10 30 unit
|
||||||
|
UN_e201_ADAN_ISCMissile 3000 300 — 2800 unit
|
||||||
|
UN_f001_TCAF_DeltaSaber_T_Player_Ttrl1 1500 10 7 29 unit
|
||||||
|
UN_f001_TCAF_DeltaSaber_T_Player_Ttrl2 1500 10 7 29 unit
|
||||||
|
UN_f003_TCAF_ArrowHead_EX4 300 10 5 20 unit
|
||||||
|
UN_f003_TCAF_ArrowHead_EX5 300 10 5 20 unit
|
||||||
|
UN_e011_ADAN_Attacker_B_HF 2250 200 50 100 unit
|
||||||
|
UN_e015_ADAN_Puppy — 7 5.5 10 unit
|
||||||
|
Ship_Test 100 20 14 29 unit
|
||||||
|
UN_e010_ADAN_Attacker_S_HF 750 100 40 50 unit
|
||||||
|
Test_ADAN_PrometheusDriver 10000 1 — — unit
|
||||||
|
UN_f001_TCAF_DeltaSaber_T_EX5 1000 10 7 29 unit
|
||||||
|
UN_e002_ADAN_Elan_N 800 20 10 30 unit
|
||||||
|
UN_e008_ADAN_TurretPlus 300 24 9 35 unit
|
||||||
|
UN_mn040_Asteroid_Big 10000 2000 3500 — unit
|
||||||
|
UN_mn500_ADAN_FloatingMine 1000 200 — — unit
|
||||||
|
UN_S04_Asteroid_cmesh_01 10000 46900 55500 13600 unit
|
||||||
|
UN_S05_Asteroid_cmesh_01 10000 10400 13000 34400 unit
|
||||||
|
UN_S04_Asteroid_cmesh_03 10000 42800 37700 47400 unit
|
||||||
|
UN_S04_Asteroid_cmesh_04 10000 59000 56900 21800 unit
|
||||||
|
UN_S05_Asteroid_cmesh_03 10000 8300 13000 19800 unit
|
||||||
|
UN_S04_Asteroid_cmesh_05 — 26000 21800 72400 unit
|
||||||
|
UN_S05_Asteroid_cmesh_04 10000 7400 15800 30100 unit
|
||||||
|
UN_e011_ADAN_Attacker_B_EX4 1500 200 50 100 unit
|
||||||
|
UN_e910_core_ADAN_GeneratorCore 10000 2000 — — unit
|
||||||
|
UN_n001_TTRL_Box 100 0.1 — — unit
|
||||||
|
UN_e006_ADAN_Vindicator_Margras 6400 12 5 30 unit
|
||||||
|
UN_e010_ADAN_Attacker_S_EX4 500 100 40 50 unit
|
||||||
|
UN_bf001_TCAF_SchlosBase 10000 2500 3100 2600 unit
|
||||||
|
UN_f001_TCAF_DeltaSaber_T_EX5_el 1000 10 7 29 unit
|
||||||
|
UN_n001_TTRL_Box_move 100 0.1 — — unit
|
||||||
|
UN_e004_ADAN_ElanPlus_NF 800 24 10 30 unit
|
||||||
|
Test_ADAN_PrometheusDriver_InsideP2_01 10000 1 — — unit
|
||||||
|
UN_e001_ADAN_Elan 500 25 10 30 unit
|
||||||
|
UN_e007_ADAN_Turret 100 20 9 22 unit
|
||||||
|
UN_e003_ADAN_ElanPlus_Margras 1600 24 10 30 unit
|
||||||
|
UN_e901_ADAN_Boss 10000 500 1700 — unit
|
||||||
|
UN_e013_ADAN_ElanPlus_Taskent 1300 24 10 30 unit
|
||||||
|
UN_e011_ADAN_Attacker_B_HF_Wayne 3000 200 50 100 unit
|
||||||
|
UN_f003_TCAF_ArrowHead 300 10 5 20 unit
|
||||||
|
UN_f004_TCAF_DeltaSaber_A_Player 1500 10 7 29 unit
|
||||||
|
UN_e010_ADAN_Attacker_S_HF_Wayne 1000 100 40 50 unit
|
||||||
|
UN_e015_ADAN_Puppy_2 — 7 5.5 10 unit
|
||||||
|
UN_S01_Asteroid_cmesh_01a — 23000 21700 13400 unit
|
||||||
|
UN_S01_Asteroid_cmesh_02a 10000 20900 24000 23800 unit
|
||||||
|
UN_S01_Asteroid_cmesh_01b — 18000 20900 14200 unit
|
||||||
|
UN_S01_Asteroid_cmesh_02b — 20400 21800 26700 unit
|
||||||
|
UN_S01_Asteroid_cmesh_03a 10000 23600 11700 30300 unit
|
||||||
|
UN_bf002_TCAF_TransitPlatform 10000 2500 3100 4600 unit
|
||||||
|
UN_e001_ADAN_Elan_GR 800 25 10 30 unit
|
||||||
|
UN_S13_Asteroid_cmesh_01a 10000 14200 10200 14700 unit
|
||||||
|
UN_S01_Asteroid_cmesh_03b — 21800 18600 37200 unit
|
||||||
|
UN_S01_Asteroid_cmesh_04a — 32800 19900 23300 unit
|
||||||
|
UN_S13_Asteroid_cmesh_01b 10000 8000 5400 16400 unit
|
||||||
|
UN_S13_Asteroid_cmesh_02a 10000 6200 5400 11300 unit
|
||||||
|
UN_S01_Asteroid_cmesh_04b — 26400 23100 19600 unit
|
||||||
|
UN_S04_Asteroid_cmesh_02a — 25100 20800 61400 unit
|
||||||
|
UN_S13_Asteroid_cmesh_02b 10000 5100 6900 10500 unit
|
||||||
|
UN_S05_Asteroid_cmesh_02a 10000 10100 13200 9100 unit
|
||||||
|
UN_S04_Asteroid_cmesh_02b 10000 25200 19700 67000 unit
|
||||||
|
UN_S05_Asteroid_cmesh_02b 10000 8700 14900 12300 unit
|
||||||
|
UN_S08_Asteroid_cmesh_01a 10000 14700 14900 21300 unit
|
||||||
|
UN_S08_Asteroid_cmesh_01b 10000 14900 14600 15800 unit
|
||||||
|
UN_S08_Asteroid_cmesh_02a 10000 17400 13400 15200 unit
|
||||||
|
UN_S04_Asteroid_cmesh_06a — 14400 20800 45900 unit
|
||||||
|
UN_S08_Asteroid_cmesh_02b 10000 16800 10700 14200 unit
|
||||||
|
UN_S08_Asteroid_cmesh_03a — 14000 10000 13700 unit
|
||||||
|
UN_S04_Asteroid_cmesh_06b 10000 14100 16200 21500 unit
|
||||||
|
UN_S04_Asteroid_cmesh_07a — 23200 14000 67100 unit
|
||||||
|
UN_S08_Asteroid_cmesh_03b 10000 13900 15100 15700 unit
|
||||||
|
UN_S04_Asteroid_cmesh_07b 10000 25300 4900 40600 unit
|
||||||
|
UN_e008_ADAN_TurretPlus_EX4 300 24 9 35 unit
|
||||||
|
UN_f001_TCAF_DeltaSaber_T_Player 1500 10 7 29 unit
|
||||||
|
UN_f003_TCAF_ArrowHead_Yoji 400 10 5 20 unit
|
||||||
|
UN_f002_TCAF_DeltaSaber_W_Player 1500 10 7 29 unit
|
||||||
|
UN_e104_ADAN_Carrier 20000 2300 1000 3300 vessel
|
||||||
|
UN_f106_TCAF_Destroyer 10000 200 — 2000 vessel
|
||||||
|
UN_e105_ADAN_Cruiser 30000 600 — 3800 vessel
|
||||||
|
UN_e101_ADAN_SDBattleship 100000 1900 2100 12300 vessel
|
||||||
|
UN_f105_TCAF_Cruiser 30000 700 — 3800 vessel
|
||||||
|
UN_f102_TCAF_LightCarrier 20000 500 — 1600 vessel
|
||||||
|
UN_e102_ADAN_BattleshipEX 50000 1900 2300 6500 vessel
|
||||||
|
UN_e107_ADAN_AAFrigate_EX4 4000 100 200 — vessel
|
||||||
|
UN_e102_ADAN_Battleship 50000 1900 2300 6500 vessel
|
||||||
|
UN_f104_TCAF_Battleship 30000 2000 — 6000 vessel
|
||||||
|
UN_f105_TCAF_Cruiser_EX5 30000 700 — 3800 vessel
|
||||||
|
UN_e108_ADAN_ASFrigateEX 4000 80 — 350 vessel
|
||||||
|
UN_f106_TCAF_Destroyer_Inv 10000 200 — 2000 vessel
|
||||||
|
UN_e107_ADAN_AAFrigate 4000 100 200 — vessel
|
||||||
|
UN_f104_TCAF_Battleship_EX5 30000 2000 — 6000 vessel
|
||||||
|
UN_f105_TCAF_Cruiser_Inv 30000 700 — 3800 vessel
|
||||||
|
UN_f102_TCAF_LightCarrier_Inv 20000 500 — 1600 vessel
|
||||||
|
UN_e108_ADAN_ASFrigate 4000 80 — 350 vessel
|
||||||
|
UN_e106_ADAN_DestroyerEX 10000 300 — 2100 vessel
|
||||||
|
UN_f101_TCAF_Acropolis 25000 400 — 1400 vessel
|
||||||
|
UN_e105_ADAN_CruiserEX 30000 600 — 3800 vessel
|
||||||
|
UN_e101_ADAN_SDBattleshipEX 100000 1900 2100 12300 vessel
|
||||||
|
UN_e106_ADAN_Destroyer 10000 300 — 2100 vessel
|
||||||
@@ -66,8 +66,51 @@ with them. It also gives independent support to the **`Size_Y` inherits `Size_X`
|
|||||||
default rule — five of the fourteen have `+0x30 == +0x34` exactly
|
default rule — five of the fourteen have `+0x30 == +0x34` exactly
|
||||||
(300/300, 400/400, 200/200, 600/600, 700/700) while the rest differ.
|
(300/300, 400/400, 200/200, 600/600, 700/700) while the rest differ.
|
||||||
|
|
||||||
❔ **Not yet resolved: which unit each object is.** Nothing in the first 96 words
|
## Identity without a name: match on the values
|
||||||
is an obvious name pointer, so these cannot be matched to their disc records yet
|
|
||||||
— and that match is what turns the snapshot into defaulted-field *values*. The
|
Nothing in the first 96 words is a name pointer, so identity comes from the
|
||||||
next step is to dump further into the object (and the entity that points at it)
|
numbers themselves. `examples/unit_signatures.rs` prints `(HP, Size_X, Size_Y,
|
||||||
looking for the id or name field, which is one more single-call run.
|
Size_Z)` for every craft unit and vessel on the disc; a live object is then the
|
||||||
|
record whose *known* fields it reproduces exactly.
|
||||||
|
|
||||||
|
Seven of the fourteen match a disc record on all four values — the player's
|
||||||
|
Delta Saber (`10, 7, 29`, HP 1500 in its player form and 1000 otherwise), the
|
||||||
|
ArrowHead, an ADAN turret, an Attacker. **The other seven match nothing, and
|
||||||
|
that is the point:** their disc records leave a field defaulted, so there is
|
||||||
|
nothing to match against. **18 of the 23 vessel records on disc are missing at
|
||||||
|
least one of these four fields**, nearly always `Size_Y`.
|
||||||
|
|
||||||
|
## ✅ Route B: 13 defaulted fields read out of the running game
|
||||||
|
|
||||||
|
Matching each disc record that defaults exactly one field against the live object
|
||||||
|
that reproduces its remaining fields resolves the missing value:
|
||||||
|
|
||||||
|
| unit | field | runtime value | live object (sx, sy, sz, hp) |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `UN_e201_ADAN_ISCMissile` | `Size_Y` | **300** | `0xbd3e0680` (300, 300, 2800, 3000) |
|
||||||
|
| `UN_f106_TCAF_Destroyer` | `Size_Y` | **200** | `0xbd3ee300` (200, 200, 2000, 10000) |
|
||||||
|
| `UN_f106_TCAF_Destroyer_Inv` | `Size_Y` | **200** | `0xbd3ee300` |
|
||||||
|
| `UN_e105_ADAN_Cruiser` | `Size_Y` | **600** | `0xbd3fd800` (600, 600, 3800, 30000) |
|
||||||
|
| `UN_e105_ADAN_CruiserEX` | `Size_Y` | **600** | `0xbd3fd800` |
|
||||||
|
| `UN_f105_TCAF_Cruiser` | `Size_Y` | **700** | `0xbd40e200` (700, 700, 3800, 30000) |
|
||||||
|
| `UN_f105_TCAF_Cruiser_EX5` | `Size_Y` | **700** | `0xbd40e200` |
|
||||||
|
| `UN_f105_TCAF_Cruiser_Inv` | `Size_Y` | **700** | `0xbd40e200` |
|
||||||
|
| `UN_e108_ADAN_ASFrigate` | `Size_Y` | **80** | `0xbd3d2d80` (80, 80, 350, 4000) |
|
||||||
|
| `UN_e108_ADAN_ASFrigateEX` | `Size_Y` | **80** | `0xbd3d2d80` |
|
||||||
|
| `UN_e106_ADAN_Destroyer` | `Size_Y` | **300** | `0xbd3e6f80` (300, 300, 2100, 10000) |
|
||||||
|
| `UN_e106_ADAN_DestroyerEX` | `Size_Y` | **300** | `0xbd3e6f80` |
|
||||||
|
| `UN_f101_TCAF_Acropolis` | `Size_Y` | **400** | `0xbd3e1f00` (400, 400, 1400, 25000) |
|
||||||
|
|
||||||
|
**Every resolved value equals that unit's `Size_X`** — so the running game
|
||||||
|
confirms the `Size_Y` inherits `Size_X` rule that was derived statically from
|
||||||
|
sibling records, this time from the engine's own parsed definitions.
|
||||||
|
|
||||||
|
Two honest limits. Variants share a live object (`_Inv`, `_EX5`, `EX` map to the
|
||||||
|
same definition as their base), so the snapshot cannot distinguish them — the
|
||||||
|
value is right for the class, and per-variant differences in *other* fields are
|
||||||
|
not addressed. And this run only reaches the units Stage 02 instantiates: the
|
||||||
|
`…Ratio` / `…Count` family (`HQRatio`, `PowerRatio`, `ShieldRatio`, `HatchCount`,
|
||||||
|
`NodeCount`, `SequencingCount`, …) is defaulted on 5–23 records each and is
|
||||||
|
**not** resolved here, because those fields' offsets in the live object are not
|
||||||
|
yet known. Finding them is the next run: dump deeper than 96 words and look for
|
||||||
|
the constants the disc *does* set on the records that set them.
|
||||||
|
|||||||
Reference in New Issue
Block a user