BT410 Phase 5.3.29: the panel titles -- 94.8% identical to the shipped cockpit

Reading the art settled where the titles live: qsensors/qmyomers/qermlas/
qppc/qstrk6.pcc are 299x26 TITLE BANNERS, while the qblh0..7.pcc strings
the cfg hands vehicleSubSystems are 77x120 MECH ICONS (already lit by the
5.3.28 placement fix).  So the authored auxScreenLabel IS the panel title
-- it just belongs on the MFD panel, not only the ENG page.

SubsystemCluster now builds a titleBanner child from the cached label.
Placement was MEASURED against the shipped framebuffer through the A/B rig
(BackgroundBitmap places by left/BOTTOM): a bring-up BT_TITLE_DY knob, then
a green-pixel correlation over the title band, then baked at x+0x09,
y+0xb3 from the panel origin.  "SENSOR CLUSTER", "MYOMERS", "TYPE II 65
TONS" and both "ERMED LASER RANGE 500M" banners now render where the
shipped binary puts them.

Score progression vs the shipped cockpit on identical GAUGE content:
  5.3.26 birth            90.1% identical / 82% coverage
  5.3.28 authored screens 93.2% / 85%
  5.3.29 titles tuned     94.8% / 91%

Also: grab.ps1 now focuses the target window before capturing -- the
screen-copy grabbed whatever was in front, which cost two junk captures
(a white frame and an unrelated 3-D app window).

Gauge fight 11/11 rounds and smoke both clean, zero faults.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Cyd
2026-07-26 23:09:15 -05:00
co-authored by Claude Fable 5
parent 24ea06574c
commit 42ff5c3856
4 changed files with 693 additions and 624 deletions
+54 -52
View File
@@ -1,52 +1,54 @@
param( param(
[Parameter(Mandatory=$true)][string]$Out, [Parameter(Mandatory=$true)][string]$Out,
[string]$Match = "DOSBox-X" [string]$Match = "DOSBox-X"
) )
Add-Type @" Add-Type @"
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
public class W { public class W {
[DllImport("user32.dll")] public static extern IntPtr GetForegroundWindow(); [DllImport("user32.dll")] public static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")] public static extern bool GetWindowRect(IntPtr h, out RECT r); [DllImport("user32.dll")] public static extern bool GetWindowRect(IntPtr h, out RECT r);
[DllImport("user32.dll")] public static extern bool SetForegroundWindow(IntPtr h); [DllImport("user32.dll")] public static extern bool SetForegroundWindow(IntPtr h);
[DllImport("user32.dll")] public static extern bool IsWindowVisible(IntPtr h); [DllImport("user32.dll")] public static extern bool IsWindowVisible(IntPtr h);
[DllImport("user32.dll", CharSet=CharSet.Auto)] public static extern int GetWindowText(IntPtr h, System.Text.StringBuilder s, int n); [DllImport("user32.dll", CharSet=CharSet.Auto)] public static extern int GetWindowText(IntPtr h, System.Text.StringBuilder s, int n);
[DllImport("user32.dll")] public static extern bool EnumWindows(EnumProc cb, IntPtr p); [DllImport("user32.dll")] public static extern bool EnumWindows(EnumProc cb, IntPtr p);
public delegate bool EnumProc(IntPtr h, IntPtr p); public delegate bool EnumProc(IntPtr h, IntPtr p);
[StructLayout(LayoutKind.Sequential)] public struct RECT { public int L, T, R, B; } [StructLayout(LayoutKind.Sequential)] public struct RECT { public int L, T, R, B; }
} }
"@ "@
$found = @() $found = @()
$cb = [W+EnumProc]{ $cb = [W+EnumProc]{
param($h, $p) param($h, $p)
if ([W]::IsWindowVisible($h)) { if ([W]::IsWindowVisible($h)) {
$sb = New-Object System.Text.StringBuilder 512 $sb = New-Object System.Text.StringBuilder 512
[void][W]::GetWindowText($h, $sb, 512) [void][W]::GetWindowText($h, $sb, 512)
$t = $sb.ToString() $t = $sb.ToString()
if ($t -like "*$Match*") { $script:found += ,@($h, $t) } if ($t -like "*$Match*") { $script:found += ,@($h, $t) }
} }
return $true return $true
} }
[void][W]::EnumWindows($cb, [IntPtr]::Zero) [void][W]::EnumWindows($cb, [IntPtr]::Zero)
if ($found.Count -eq 0) { Write-Output "NO-WINDOW"; exit 1 } if ($found.Count -eq 0) { Write-Output "NO-WINDOW"; exit 1 }
Add-Type -AssemblyName System.Drawing Add-Type -AssemblyName System.Drawing
$i = 0 $i = 0
foreach ($f in $found) { foreach ($f in $found) {
$h = $f[0]; $title = $f[1] $h = $f[0]; $title = $f[1]
$r = New-Object W+RECT [void][W]::SetForegroundWindow($h)
[void][W]::GetWindowRect($h, [ref]$r) Start-Sleep -Milliseconds 1200
$w = $r.R - $r.L; $ht = $r.B - $r.T $r = New-Object W+RECT
if ($w -le 0 -or $ht -le 0) { continue } [void][W]::GetWindowRect($h, [ref]$r)
$bmp = New-Object System.Drawing.Bitmap $w, $ht $w = $r.R - $r.L; $ht = $r.B - $r.T
$g = [System.Drawing.Graphics]::FromImage($bmp) if ($w -le 0 -or $ht -le 0) { continue }
$g.CopyFromScreen($r.L, $r.T, 0, 0, (New-Object System.Drawing.Size $w, $ht)) $bmp = New-Object System.Drawing.Bitmap $w, $ht
$path = if ($found.Count -eq 1) { $Out } else { $Out -replace '\.png$', "_$i.png" } $g = [System.Drawing.Graphics]::FromImage($bmp)
$bmp.Save($path, [System.Drawing.Imaging.ImageFormat]::Png) $g.CopyFromScreen($r.L, $r.T, 0, 0, (New-Object System.Drawing.Size $w, $ht))
$g.Dispose(); $bmp.Dispose() $path = if ($found.Count -eq 1) { $Out } else { $Out -replace '\.png$', "_$i.png" }
Write-Output "SAVED $path ${w}x${ht} [$title]" $bmp.Save($path, [System.Drawing.Imaging.ImageFormat]::Png)
$i++ $g.Dispose(); $bmp.Dispose()
} Write-Output "SAVED $path ${w}x${ht} [$title]"
$i++
}
+28
View File
@@ -1927,6 +1927,34 @@ SubsystemCluster::SubsystemCluster(
} }
} }
//
// The PANEL TITLE BANNER (299x26): the authored auxScreenLabel .pcc --
// "SENSOR CLUSTER" / "MYOMERS" / "ERMED LASER RANGE 500M" / the weapon
// names. Cached on PoweredSubsystem at ctor time (its resource dies
// with the Mech ctor's stream buffer). The banner sits at the TOP of
// the quadrant; BackgroundBitmap places by (left, BOTTOM), so the row
// is the panel origin plus the quadrant height less the banner.
//
titleBanner = NULL;
if (subsystem_in != NULL
&& subsystem_in->IsDerivedFrom(PoweredSubsystem::ClassDerivations))
{
const char *title =
((PoweredSubsystem *)subsystem_in)->GetAuxScreenLabel();
if (title != NULL && *title != '')
{
// Placement measured against the shipped framebuffer
// (the A/B rig): the banner's bottom row sits 0xb3 above the
// panel origin, inset 0x0e from its left edge.
titleBanner = new BackgroundBitmap(
mfd_mode, renderer_in, owner_ID, mfd_port,
x + 0x09, y + 0xb3, title,
1, 0xff, 0
);
Register_Object(titleBanner);
}
}
//------------------------------------------------------------ //------------------------------------------------------------
// Paint the panel frame image into the MFD port. // Paint the panel frame image into the MFD port.
//------------------------------------------------------------ //------------------------------------------------------------
File diff suppressed because it is too large Load Diff
@@ -515,3 +515,41 @@ come from the q-strip statusImage path (the 8 qblh*.pcc typeStrings
VehicleSubSystems passes to DrawStatusCells), NOT from auxScreenLabel. VehicleSubSystems passes to DrawStatusCells), NOT from auxScreenLabel.
That is the next thread. Name bitmaps stay inert (CreateMutantPixelmap8 / That is the next thread. Name bitmaps stay inert (CreateMutantPixelmap8 /
LookupPlayerNameBitmap bring-up stubs). LookupPlayerNameBitmap bring-up stubs).
## 5.3.29 -- THE PANEL TITLES (94.8% identical / 91% coverage)
The 5.3.28 pass proved the titles do NOT come from auxScreenLabel's ENG-page
blit. Reading the art settled it: `qsensors.pcc` / `qmyomers.pcc` /
`qermlas.pcc` / `qppc.pcc` / `qstrk6.pcc` are **299x26 TITLE BANNERS**, while
the `qblh0..7.pcc` strings the cfg hands vehicleSubSystems are **77x120 MECH
ICONS** (which the 5.3.28 placement fix already lit). So the label IS the
title -- it just belongs on the MFD panel, not only the ENG page.
SubsystemCluster now builds a titleBanner child from the cached
GetAuxScreenLabel(). Placement was MEASURED against the shipped
framebuffer with the A/B rig (BackgroundBitmap places by left/BOTTOM):
x + 0x09, y + 0xb3 from the panel origin -- found by a bring-up
BT_TITLE_DY knob, then a green-pixel correlation over the title band, then
baked. The authored data behind it (dumped live): Avionics screen=5
placement=2 label=qsensors.pcc; Myomers 6/0/qmyomers; PPC_1 1/4/qppc;
PPC_2 10/6/qppc; ERMLaser_1 4/1/qermlas; ERMLaser_2 8/6; ERMLaser_3 7/4;
SRM6_1 2/1/qstrk6; SRM6_2 9/3.
SCORE PROGRESSION vs the shipped cockpit (same GAUGE content, same mount):
5.3.26 birth 90.1% identical / 82% coverage
5.3.28 authored screens 93.2% / 85%
5.3.29 titles (rough) 92.6% / 88%
5.3.29 titles (tuned) 94.8% / 91% <-- current
"SENSOR CLUSTER", "MYOMERS", "TYPE II 65 TONS" and both "ERMED LASER RANGE
500M" banners now render in their authored positions.
CAPTURE GOTCHA (cost two wasted runs): the grab script copies SCREEN
pixels, so any window in front of DOSBox is what gets captured (one grab
returned a white frame, another returned an unrelated 3-D app).
grab.ps1 now SetForegroundWindow()s the target and waits 1.2 s first.
STILL OPEN: the pilot name bitmap ("Aeolus" -- CreateMutantPixelmap8 /
LookupPlayerNameBitmap bring-up stubs), some lamp frames ("OFF x" vs the
shipped state digits -- the mode accessor stand-in), and a stray magenta
"MID/ADV" block that lands over the top-left title (a widget drawing at a
wrong port offset; next thread).