Skip to content

PT — Aircraft Flight Model (.PT)

.PT files are the per-aircraft aerodynamics and avionics records. There are 145+ .PT files in FA_2.LIB, one per aircraft variant. They use BRF (Brent's Relocatable Format) — plain ASCII text that is parsed at game startup by SetupPT.

Tools

fx

fx pt info   F16C.PT          # field dump: thrust, speed, fuel, stall, ceiling
fx pt unpack F16C.PT -o F16C.pt.txt
fx pt pack   F16C.pt.txt -o F16C_mod.PT

See BRF.md for the full field reference (OT base fields + PT extension fields, G-envelope section, hardpoints, systemDamage array).

File Layout

Plain text; BRF syntax. This page documents the in-memory PLANE_TYPE binary struct the text compiles into — the text-format field reference lives in BRF.md.

In-Memory Struct: PLANE_TYPE

At game startup _SetupPT (0x4A7220) chains through _SetupNT_SetupOTFUN_004a6b10 to load each .PT file and lock its binary data into memory. The binary layout is needed for runtime patching, network-sync analysis, and AI parameter extraction.

Layout derived by direct byte-counting against F16C.PT (type_size = 660 = 0x294). Offsets marked confirmed were read or written directly by decompiled the game executable code. All others are inferred from packing (BRF fields written sequentially, no alignment padding).

file_name is not stored in the binary struct — it is the LIB lookup key only. ot_names is a single ptr; it points to a name-record holding all name strings.

Total layout: OBJ_TYPE 166 B (0x00–0xA5) + NPC_TYPE 20 B (0xA6–0xB9) + PLANE_TYPE main 258 B (0xBA–0x1BB) + 9 hardpoints × 24 B (0x1BC–0x293) = 660 B

OBJ_TYPE section (0x00–0xA5, 166 B)

Offset Size Field BRF type Notes
0x00 1 struct_type byte = 5 for PT
0x01 2 type_size word F16C = 660
0x03 2 instance_size word confirmed_T_AddObj@12 reads *(short*)(ptr+3)
0x05 4 ot_names ptr single ptr to name record; F16C = ptr
0x09 4 ot_flags dword F16C = $806bf3 (flyable, visible in library)
0x0D 2 obj_class word confirmed_SetupOT tests bits 0xc000/0x2000/0x1000/0x800/0x400/0x200
0x0F 4 shape ptr confirmed_SetupOT passes iVar4+0x0f to FUN_004a71e0; string compared to "eject_SH"
0x13 4 shadow_shape ptr confirmed_SetupOT reads *(char**)(iVar4+0x13) for shape-name derivation
0x17 4 (damage_shape_a) ptr filled by _SetupOT ("_a" variant); dword 0 in source BRF
0x1B 4 (damage_shape_b) ptr filled by _SetupOT ("_b" variant); dword 0 in source BRF
0x1F 2 Unknown word F16C = 0; possibly dst_debris_pos[0]
0x21 2 Unknown word F16C = 0; possibly dst_debris_pos[1]
0x23 2 Unknown word F16C = 30; possibly dst_debris_pos[2]
0x25 4 (damage_shape_c) ptr filled by _SetupOT ("_c" variant); dword 0 in source BRF
0x29 4 (damage_shape_d) ptr filled by _SetupOT ("_d" variant); dword 0 in source BRF
0x2D 2 Unknown word F16C = 30; possibly dmg_debris_pos[0]
0x2F 2 Unknown word F16C = 0
0x31 2 Unknown word F16C = 0
0x33 4 dmg_type / damage_set dword F16C = 0; written _Rand(2)+1 at destruction, selects the {_C,_D} wreck pair when 2 — see shape-selection.md
0x37 4 year_available dword F16C = 1984 (F-16C service entry year)
0x3B 2 max_vis_dist word F16C = 148
0x3D 2 camera_dist word F16C = 0
0x3F 2 laser_targeting_sig word F16C = 88
0x41 2 ir_signature word F16C = 88
0x43 2 rcs_signature word F16C = 100
0x45 2 hit_points word F16C = 100
0x47 2 dmg_planes word F16C = 0
0x49 2 dmg_ships word confirmed_T_AddObj@12 reads as DAT_0050ce8e (crash damage threshold); F16C = 92
0x4B 2 dmg_structs word F16C = 255
0x4D 2 dmg_armor word F16C = 255
0x4F 2 dmg_other word F16C = 255
0x51 2 Unknown word F16C = 255
0x53 2 Unknown word F16C = 255
0x55 1 explosion_type byte F16C = 30
0x56 1 crater_size byte F16C = 0
0x57 4 empty_weight dword F16C = 14,567 lbs (matches real F-16C)
0x5B 2 Unknown word F16C = 199
0x5D 16 movement info 8×word speed/accel params; F16C = 0,16380,14560,−14560,16380,0,0,0
0x6D 16 movement dwords 4×dword F16C = ^0,^0,^300,^60000
0x7D 4 utilProc symbol F16C = _PLANEProc
0x81 4 loopSound ptr
0x85 4 secondSound ptr
0x89 4 engineOnSound ptr
0x8D 4 engineOffSound ptr
0x91 1 (byte) byte F16C = 1
0x92 16 sound params 8×word F16C = 15000,320,160,20,1600,0,20,60
0xA2 4 hudName ptr

NPC_TYPE section (0xA6–0xB9, 20 B)

Offset Size Field BRF type Notes
0xA6 4 ctType dword F16C = 0
0xAA 4 ctName ptr
0xAE 1 (byte) byte F16C = 12
0xAF 1 (byte) byte F16C = 32
0xB0 1 (byte) byte F16C = 20
0xB1 2 (word) word F16C = 32767
0xB3 2 (word) word F16C = 0
0xB5 1 (byte) byte F16C = 9 (hardpoint count)
0xB6 4 hards ptr → hardpoints array

PLANE_TYPE section (0xBA–0x1BB, 258 B)

Offset Size Field BRF type Notes
0xBA 4 carrier_flags dword F16C = $11 (land-based); see BRF.md carrier_flags table
0xBE 4 env ptr → G-envelope section in same file
0xC2 2 neg_g_count word F16C = −4 (number of negative-G envelope entries)
0xC4 2 pos_g_count word F16C = 9
0xC6 2 max_speed_sl word F16C = 1342 mph (sea level)
0xC8 2 max_speed_36k word F16C = 1934 mph (36,000 ft)
0xCA 130 aero block 65×word control-authority, stall/spin and landing-gate parameters — every word now code-traced via the _cgt type-record mirror (0x50D268 + off); see § The 65-word aerodynamic block for the full per-word map
0x14C 1 num_engines byte F16C = 1
0x14D 2 Unknown word F16C = 0; unlisted in BRF.md
0x14F 4 military_thrust dword F16C = 17,687 lbf (≈ F100-PW-229 dry)
0x153 4 afterburner_thrust dword F16C = 32,000 lbf
0x157 2 throttle_accel word F16C = 40 %/sec
0x159 2 throttle_decel word F16C = 60 %/sec
0x15B 2 tv_min_angle word F16C = 0 (no thrust vectoring)
0x15D 2 tv_max_angle word F16C = 0
0x15F 2 tv_speed word F16C = 0
0x161 2 fuel_consumption_mil word F16C = 2
0x163 2 fuel_consumption_ab word F16C = 16
0x165 4 fuel_capacity dword F16C = 6,972 lbs (matches real F-16C internal fuel)
0x169 2 aero_drag word F16C = 256 (baseline)
0x16B 2 g_drag word F16C = 33
0x16D 2 airbrake_drag word F16C = 256
0x16F 2 wheel_brake_drag word F16C = 102
0x171 2 flap_drag word F16C = 76
0x173 2 gear_drag word F16C = 23
0x175 2 weapons_bay_drag word F16C = 0
0x177 2 flaps_lift word F16C = 51
0x179 2 drag_loaded word F16C = 30
0x17B 2 g_drag_loaded word F16C = 13
0x17D 2 gear_pitch word F16C = 40
0x17F 2 max_landing_speed word F16C = 40 ft/sec
0x181 2 max_side_speed word F16C = 40 ft/sec
0x183 2 max_sink_rate word F16C = 2560 ft/sec
0x185 2 max_landing_pitch word F16C = 5120
0x187 45 systemDamage[] byte×45 subsystem hit thresholds; F16C values range 6–150
0x1B4 2 misc_per_flight word F16C = 10 (maintenance man-hours)
0x1B6 2 repair_multiplier word F16C = 10
0x1B8 4 mtow dword F16C = 33,000 lbs

Hardpoints section (0x1BC–0x293, 9 × 24 B)

F16C.PT has 9 hardpoints. Each hardpoint is 24 bytes: 8×word + ptr (4B) + byte + word + byte.

Offset Size Field Notes
+0x00 2 hld loading-data flags
+0x02 2 offset_x ft right/left
+0x04 2 offset_y ft up/down
+0x06 2 offset_z ft fore/aft
+0x08 2 slew_heading 1° = 182
+0x0A 2 slew_pitch
+0x0C 2 slew_limit_heading
+0x0E 2 slew_limit_pitch
+0x10 4 default_type ptr to weapon filename
+0x14 1 weight hundreds of lbs
+0x15 2 quantity
+0x17 1 location see BRF.md location codes

Engine Notes

Setup call chain

_SetupPT  (0x4A7220)  -- thin wrapper
  └─ _SetupNT  (0x4A7200)  -- NT-layer init
       ├─ FUN_004a6b10(param_1)  -- MM handle → raw data ptr
       └─ _SetupOT  (0x4A6EB0)  -- OT-layer init: resolves shape ptrs via RMAccess_8
            └─ FUN_004a71e0(ptr)  -- loads one SH file: *ptr = _RMAccess_8(*ptr, 0x8000)

FUN_004a71c0  (0x4A71C0)  -- single-object variant; calls FUN_004a71e0 on shape fields

_SetupOT reads six shape ptr fields in the binary struct and resolves each via _RMAccess_8 (load from LIB). For flying objects (obj_class & 0xc000 ≠ 0) it also derives four damage-state shape names (_a / _b / _c / _d suffix) from shadow_shape, writes them into the struct, and resolves them.

Entry points

VA Symbol Role
0x4A7200 _SetupNT NT-layer wrapper; calls FUN_004a6b10 + _SetupOT
0x4A7220 _SetupPT PT entry point; thin wrapper calling _SetupNT
0x4A71C0 FUN_004a71c0 Single-object shape loader; calls FUN_004a71e0 on shape fields
0x4A6EB0 _SetupOT OT-layer shape resolver; patches +0x0f/+0x13/+0x17/+0x1b/+0x25/+0x29
0x4A71E0 FUN_004a71e0 Resolves one shape ptr: *ptr = _RMAccess_8(*ptr, 0x8000)
0x4A6B10 FUN_004a6b10 MM handle → raw data ptr (*(int*)(param+0xf), with MM lock if bit 1 of +0xe)
0x4A6B30 FUN_004a6b30 BRF record lookup + string→binary conversion via _RMType_4 / _RMFind_4

The 65-word aerodynamic block (0xCA–0x14B)

The 65 words at 0xCA–0x14B are the flight-model tuning table. They are named here by code trace, not by value-guessing: at startup GetCurObj copies the active aircraft's PLANE_TYPE record into the _cgt type-record mirror at base 0x50D268 byte-for-byte, so PT offset X is read by the engine as DAT_[0x50D268 + X] (anchored by exact hits — fuel_consumption_mil at PT 0x161 = DAT_0050d3c9, military_thrust at 0x14F = DAT_0050d3b7, g_drag at 0x16B = DAT_0050d3d3). Enumerating every reference into 0x50D332–0x50D3B3 (aero block) gives the consumer of each word. Every word has a consumer — none are engine-unused. Words 0xCA–0xF8 are consumed as two block-copied limit vectors (see below), which is why the middle words of those blocks carry no direct xref of their own.

The 12 stall/spin words (0x128–0x13E) confirm and now position the fields BRF.md already named but could not place; the remaining words are newly named. F16C values shown; "per-a/c" = varies by aircraft, "const" = identical across the eight sampled .PT files (F16C/F14/A10/B52/AH64/AV8/C130/F117).

Control-authority limit vectors (0xCA–0xF8, words 0–23). _COBv@0 (0x477EA0) block-copies words 0–11 into the live control buffer 0x547338; _COBrv@0 (0x477ED0) block-copies words 12–23 into 0x547350 and scales them down by battle-damage (DAT_0050ce8e) and control-surface loss (DAT_00522547) — i.e. these are the max control-response limits, degraded as the airframe takes hits.

word off field F16C reader / evidence
0 0xCA accel_runway (BRF) −73 _COBv block[0]
1 0xCC decel_runway (BRF) 0 _COBv block
2 0xCE roll_speed_min (BRF) 73 _COBv block
3 0xD0 roll_speed_max (BRF) 73 _COBv block
4 0xD2 pull_rate (BRF) −146 _COBv block
5 0xD4 neg_g_limit (BRF) 146 _COBv block
6–11 0xD60xE0 ground/low-speed handling limits (cont.) 7,7,−146,146,73,73 · const _COBv block words 6–11; ±-paired rate limits, roles inferential
12–15 0xE20xE8 rotational-authority axis A {neg,pos,rate,accel} −270,270,362,724 · per-a/c _COBrv block; fighters ±270, bombers ±30
16–19 0xEA0xF0 rotational-authority axis B {neg,pos,rate,accel} 0,0,9,9 · per-a/c _COBrv block
20–23 0xF20xF8 rotational-authority axis C {neg,pos,rate,accel} −45,45,90,90 · per-a/c _COBrv block

Stick-to-motion gains and turn coordination (0xFA–0x116, words 24–46). The three primary control axes are proven by their _StickInput_28 calls in _FMFlight@0 (0x47B020): each is driven by a specific pilot input, so the axis identity is definitive.

word off field F16C reader / evidence
24 0xFA pitch-input → pitch-rate gain (×/9) 20 · const _FMFlight DAT_0050d362 * pitchIn / 9
25 0xFC forward-motion scale (×256) 70 · const _MovePlane@0 (0x476AE0)
26 0xFE vertical/reverse-motion scale (×−256) 15 · const _MovePlane@0
27 0x100 AI visual-detection / contrail interval factor 115 · per-a/c FUN_0047759b (time-of-day-scaled spotting timer)
28–31 0x1020x108 rudder turn-coordination axis {neg,pos,rate,accel} −4,4,4,9 _FMFlight _StickInput(&DAT_0050d007, …, _rudder), speed/G-scaled
32 0x10A adverse-yaw (roll→yaw) coupling 10 · const _FMFlight DAT_0050d02b = DAT_0050d372 * rollRate
33 0x10C sideslip / yaw drag coefficient 128 · const FUN_0047a970 (× sideslip); also PROJGuideLoft
34 0x10E yaw/heading response rate 5 · per-a/c _FMFlight DAT_0050d00f += d376·resp; PROJGuideLoft
35–38 0x1100x116 roll axis {neg,pos,rate,accel} −90,90,362,724 _FMFlight _StickInput(&DAT_0050d033, …, _stickX)
39–42 0x1180x11E pitch axis {neg,pos,rate,accel} −90,90,362,724 _FMFlight _StickInput(&DAT_0050d037, …, _stickY)
43–46 0x1200x126 yaw axis {neg,pos,rate,accel} −90,90,362,724 _FMFlight _StickInput(&DAT_0050d03b, …, _rudder)

Stall / spin model (0x128–0x13E, words 47–58). These are the BRF.md stall/spin fields, now pinned to their binary offsets. Names are BRF.md's; offsets and readers are the new evidence.

word off field (BRF name) F16C reader / evidence
47 0x128 stall_warn_delay 512 · per-a/c ?CheckForEvents2 fires the stall-warning event; _FMFlight
48 0x12A stall_duration 512 · per-a/c ?CheckForEvents2; _FMFlight d08d < DAT_0050d392
49 0x12C stall_severity 256 _FMFlight (DAT_0050d394)
50 0x12E stall_pitch_down (deg/sec) 30 · const _FMFlight _MatchF24(&pitch, −0x5A00, …) drives nose to −90°; ?PROJEventProc
51 0x130 spin_entry_ease (0 = harder) 0 · per-class FUN_0047ccb0 sets spin state d08c=3; fighters 0, bombers 1, helo 2
52 0x132 spin_exit_ease (neg = harder) −2 · per-class FUN_0047cdb0 recovery selector (−2 / −1); _FMFlight
53–54 0x1340x136 spin_yaw_low / _high (deg/sec) 120,180 · const _FMFlight FUN_0047ce70(lo, hi, spinPhase)
55–56 0x1380x13A spin_aoa_low / _high (deg) 30,70 · const _FMFlight FUN_0047ce70
57–58 0x13C0x13E spin_bank_low / _high (deg) 15,5 · const _FMFlight FUN_0047ce70

Gear pitch and landing gate (0x140–0x14A, words 59–64). Word 59 is the gear-down nose-up trim; words 60–64 are the universal landing-quality gate (identical in every .PT) that _CheckLandingParms@0 (0x477140) scores — each threshold overshoot returns 5 (warning) or 6 (bad landing).

word off field F16C reader / evidence
59 0x140 gear-down pitch-trim authority (× 0xB6) 0 · per-a/c _FMUpdateGearPitch@0 (0x4514C0); AV8 = 3
60 0x142 landing gate: reference speed (fail if exceeded by >0x32) 330 · const _CheckLandingParms; ?HUDDrawSpeed reads it as the HUD approach ref
61 0x144 landing gate: max sink rate (fail if >10 over) 51 · const _CheckLandingParms
62 0x146 landing gate: pitch/AoA limit 95 · const _CheckLandingParms; ?HUDDrawAlt
63 0x148 landing gate: attitude limit 1 (fail if >0x14 over) 25 · const _CheckLandingParms
64 0x14A landing gate: attitude limit 2 (fail if >0x14 over) 10 · const _CheckLandingParms

Confidence: the three primary axes (roll/pitch/yaw, words 35–46) and the stall/spin/landing blocks (words 47–64) are reader-proven. The two block-copied limit vectors (words 0–23) have a proven consumer (_COBv/_COBrv) and proven damage-scaling role, but the per-word breakdown inside words 6–23 is inferred from the ±-paired numeric structure and is annotated as such. Method is reproducible with scripts/ghidra/run_ghidra.sh over the 0x50D332–0x50D3B3 range.

Open Questions

1. Debris-position candidates

The three 2-byte unknowns at 0x1F/0x21/0x23 and three at 0x2D/0x2F/0x31 are plausible candidates for dst_debris_pos[3] and dmg_debris_pos[3] (i16[3] each per BRF.md), but the 0x23/0x2D values of 30 don't obviously map to debris-offset coordinates — needs a second PT file comparison to confirm.

Status: open — re-static (#54)

Formats: BRF — full BRF field reference for all PT fields, hardpoints, G-envelope; OT — OT base fields that PT inherits; JT — weapon/projectile types loaded via SetupJT (0x4A7230); SH — 3D model format referenced by shape / shadow_shape ptr fields.