The gauge framebuffer is PLANE-PACKED: L4GAUGE.CFG gives each port a bit
mask, not a rectangle, so all six heads share the same x/y and are separated
only by bit -- the packing the VDB splits into the pod's physical screens.
Comparing RGB was therefore measuring nothing useful; the "magenta artifact"
just meant the wrong heads were lit at that pixel.
New instruments (emulator/render-bridge/gauge-ab/, with a README):
planes.py per-head scoreboard -- shipped vs ours, missing vs extra, per
port mask, recovering the 16-bit word from DOSBox's RGB565
BT_VIS_LOG a complete cockpit widget map from the single dispatch every
gauge widget is built through (MethodDescription::Execute),
printing name + port + authored position
ab.sh stages the fresh build over BTL4REC.EXE and kills any running
DOSBox before launching -- see below
The fix: the Comm page's pilot roster is the POD roster (the viewpoint mech's
ControlsMapper pilot array, mapper attributes 15/16), not the mission's
player list. It is zero in a solo mission, so the shipped page draws empty;
ours resolved the local player and painted its icon in colour 0xff. The gate
belongs in the row source, not PilotList::Execute -- Execute's empty branch
still has to run, because erasing is what the shipped page draws.
head shipped ours missing extra (was)
Eng1 17981 17981 0 0 (0 / 1030)
Eng2 17981 17981 0 0
Eng3 17981 17981 0 0
Comm 15656 13557 2099 0 (2099 / 1253)
Heat 21374 20913 566 105 (566 / 990)
Title-band magenta 404 -> 0. The 2099 Comm pixels we are missing were
missing before the gate, so it is a strict improvement.
Method note, which cost more than the bug: the rig's confs run BTL4REC.EXE
out of the mount while the build writes build410/btl4opt.exe, and nothing
connected the two. Three measurements ran an hour-old binary, so a change
that "did nothing" three times had never been tested -- and the correct first
hypothesis was discarded on that evidence. ab.sh now re-stages every launch.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
82 lines
2.9 KiB
PowerShell
82 lines
2.9 KiB
PowerShell
param(
|
|
[Parameter(Mandatory=$true)][string]$Out,
|
|
[string]$Match = "DOSBox-X"
|
|
)
|
|
|
|
Add-Type @"
|
|
using System;
|
|
using System.Runtime.InteropServices;
|
|
public class W {
|
|
[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 IsWindowVisible(IntPtr h);
|
|
[DllImport("user32.dll")] public static extern bool PrintWindow(IntPtr h, IntPtr hdc, uint flags);
|
|
[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);
|
|
public delegate bool EnumProc(IntPtr h, IntPtr p);
|
|
[StructLayout(LayoutKind.Sequential)] public struct RECT { public int L, T, R, B; }
|
|
}
|
|
"@
|
|
|
|
$found = @()
|
|
$cb = [W+EnumProc]{
|
|
param($h, $p)
|
|
if ([W]::IsWindowVisible($h)) {
|
|
$sb = New-Object System.Text.StringBuilder 512
|
|
[void][W]::GetWindowText($h, $sb, 512)
|
|
$t = $sb.ToString()
|
|
if ($t -like "*$Match*") { $script:found += ,@($h, $t) }
|
|
}
|
|
return $true
|
|
}
|
|
[void][W]::EnumWindows($cb, [IntPtr]::Zero)
|
|
if ($found.Count -eq 0) { Write-Output "NO-WINDOW"; exit 1 }
|
|
|
|
Add-Type -AssemblyName System.Drawing
|
|
|
|
function Test-Blank($bmp) {
|
|
# sample a grid; blank/uniform => PrintWindow failed on this surface
|
|
$seen = @{}
|
|
for ($y = 10; $y -lt $bmp.Height - 10; $y += 23) {
|
|
for ($x = 10; $x -lt $bmp.Width - 10; $x += 29) {
|
|
$seen[$bmp.GetPixel($x, $y).ToArgb()] = $true
|
|
if ($seen.Count -gt 6) { return $false }
|
|
}
|
|
}
|
|
return $true
|
|
}
|
|
|
|
$i = 0
|
|
foreach ($f in $found) {
|
|
$h = $f[0]; $title = $f[1]
|
|
[void][W]::SetForegroundWindow($h)
|
|
Start-Sleep -Milliseconds 600
|
|
$r = New-Object W+RECT
|
|
[void][W]::GetWindowRect($h, [ref]$r)
|
|
$w = $r.R - $r.L; $ht = $r.B - $r.T
|
|
if ($w -le 0 -or $ht -le 0) { continue }
|
|
|
|
# PRIMARY: PrintWindow(PW_RENDERFULLCONTENT) -- immune to occlusion
|
|
$bmp = New-Object System.Drawing.Bitmap $w, $ht
|
|
$g = [System.Drawing.Graphics]::FromImage($bmp)
|
|
$hdc = $g.GetHdc()
|
|
$ok = [W]::PrintWindow($h, $hdc, 2)
|
|
$g.ReleaseHdc($hdc)
|
|
$method = "PrintWindow"
|
|
|
|
if (-not $ok -or (Test-Blank $bmp)) {
|
|
# FALLBACK: screen copy (only valid if nothing is in front)
|
|
$g.Dispose(); $bmp.Dispose()
|
|
$bmp = New-Object System.Drawing.Bitmap $w, $ht
|
|
$g = [System.Drawing.Graphics]::FromImage($bmp)
|
|
$g.CopyFromScreen($r.L, $r.T, 0, 0, (New-Object System.Drawing.Size $w, $ht))
|
|
$method = "CopyFromScreen(FALLBACK -- occlusion-sensitive)"
|
|
}
|
|
|
|
$path = if ($found.Count -eq 1) { $Out } else { $Out -replace '\.png$', "_$i.png" }
|
|
$bmp.Save($path, [System.Drawing.Imaging.ImageFormat]::Png)
|
|
$g.Dispose(); $bmp.Dispose()
|
|
Write-Output "SAVED $path ${w}x${ht} via $method [$title]"
|
|
$i++
|
|
}
|