BT410 Phase 5.3.28: the visual A/B pass -- 93% pixel-identical to the shipped cockpit
Ran the reconstructed exe and the shipped ALPHA_1 binary against the SAME GAUGE content, same mount, gauges on, and diffed the 640x480x16 framebuffers. OUR EXE DRAWS THE COCKPIT: 90.1% pixel-identical on the first run, and every difference traced to a single root cause. The authored aux-screen block (screen number / background placement / label) lives in the subsystem resource -- which is freed with the Mech ctor's stream buffer (the 5.3.24 use-after-free landmine). Both this tree and the BT411 port had stubbed it, which is why BT411's displays show the same artifacting. PoweredSubsystem now caches the block at ctor time with accessors, and btl4gau2 uses the AUTHORED screen instead of a roster-order stand-in and builds the placement strip art. Panels snapped onto their authored screens -- weapon dials now land on the same panels as the shipped exe, mech icons appear on the sensor/myomer panels, generator letters correct -- taking the match to 93.2% identical / 85% coverage. Remaining differences are donor-inherited stubs: the panel title art (now proven to come from the q-strip statusImage path, not auxScreenLabel -- the label blit is wired and guarded regardless), the pilot name bitmap, and some lamp frames. The A/B rig is banked at emulator/render-bridge/gauge-ab/ (both confs + the window-grab script) so the comparison is repeatable. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
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 IntPtr GetForegroundWindow();
|
||||
[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", 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
|
||||
$i = 0
|
||||
foreach ($f in $found) {
|
||||
$h = $f[0]; $title = $f[1]
|
||||
$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 }
|
||||
$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))
|
||||
$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} [$title]"
|
||||
$i++
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
[sdl]
|
||||
output=opengl
|
||||
[dosbox]
|
||||
memsize=32
|
||||
machine=svga_s3
|
||||
[cpu]
|
||||
core=normal
|
||||
cputype=pentium
|
||||
cycles=20000
|
||||
[serial]
|
||||
serial1=disabled
|
||||
serial2=disabled
|
||||
[autoexec]
|
||||
mount c "C:\VWE\TeslaRel410\restoration\build410\run\image"
|
||||
c:
|
||||
set L4CONTROLS=RIO,KEYBOARD
|
||||
set BT_MECH_LOG=1
|
||||
set HEAPSIZE=15000000
|
||||
set L4TIMER=
|
||||
set L4SOUND=OFF
|
||||
set L4GAUGE=640x480x16
|
||||
set L4PLASMA=
|
||||
set L4VIDEO=OFF
|
||||
set BLASTER=A220 I5 D1 H5 P330 T6
|
||||
set TEMP=c:\
|
||||
32rtm.exe -x
|
||||
BTL4REC.EXE -egg test.egg > OUTREC.TXT
|
||||
echo GAME-RC=%errorlevel% >> OUT.TXT
|
||||
32rtm.exe -u
|
||||
exit
|
||||
@@ -0,0 +1,30 @@
|
||||
[sdl]
|
||||
output=opengl
|
||||
[dosbox]
|
||||
memsize=32
|
||||
machine=svga_s3
|
||||
[cpu]
|
||||
core=normal
|
||||
cputype=pentium
|
||||
cycles=20000
|
||||
[serial]
|
||||
serial1=disabled
|
||||
serial2=disabled
|
||||
[autoexec]
|
||||
mount c "C:\VWE\TeslaRel410\restoration\build410\run\image"
|
||||
c:
|
||||
set L4CONTROLS=RIO,KEYBOARD
|
||||
set BT_MECH_LOG=1
|
||||
set HEAPSIZE=15000000
|
||||
set L4TIMER=
|
||||
set L4SOUND=OFF
|
||||
set L4GAUGE=640x480x16
|
||||
set L4PLASMA=
|
||||
set L4VIDEO=OFF
|
||||
set BLASTER=A220 I5 D1 H5 P330 T6
|
||||
set TEMP=c:\
|
||||
32rtm.exe -x
|
||||
BTL4SHP.EXE -egg test.egg > OUTSHP.TXT
|
||||
echo GAME-RC=%errorlevel% >> OUT.TXT
|
||||
32rtm.exe -u
|
||||
exit
|
||||
@@ -116,6 +116,17 @@ PoweredSubsystem::PoweredSubsystem(
|
||||
|
||||
thermalResistivityCoefficient = subsystem_resource->thermalResistivityCoefficient;
|
||||
startTime = subsystem_resource->startTime;
|
||||
|
||||
//
|
||||
// Cache the authored aux-screen block NOW -- the resource memory dies
|
||||
// with the Mech ctor's stream buffer.
|
||||
//
|
||||
auxScreenNumber = subsystem_resource->auxScreenNumber;
|
||||
auxScreenPlacement = subsystem_resource->auxScreenPlacement;
|
||||
Str_Copy(auxScreenLabel, subsystem_resource->auxScreenLabel,
|
||||
sizeof(auxScreenLabel));
|
||||
Str_Copy(engScreenLabel, subsystem_resource->engScreenLabel,
|
||||
sizeof(engScreenLabel));
|
||||
startTimer = startTime;
|
||||
voltageScale = 1.0f;
|
||||
|
||||
|
||||
@@ -154,6 +154,23 @@
|
||||
virtual void
|
||||
DeathReset(Logical full_reset);
|
||||
|
||||
//
|
||||
// The AUTHORED aux-screen assignment (streamed). CACHED at ctor
|
||||
// time because MechSubsystem::resource points into the Mech ctor's
|
||||
// padded stream copy, which is FREED before the ctor returns -- the
|
||||
// 5.3.24 use-after-free landmine. The cockpit's engineering panels
|
||||
// key their screen number, background placement and title label off
|
||||
// these.
|
||||
//
|
||||
int
|
||||
GetAuxScreenNumber() const { Check(this); return auxScreenNumber; }
|
||||
int
|
||||
GetAuxScreenPlacement() const { Check(this); return auxScreenPlacement; }
|
||||
const char*
|
||||
GetAuxScreenLabel() const { Check(this); return auxScreenLabel; }
|
||||
const char*
|
||||
GetEngScreenLabel() const { Check(this); return engScreenLabel; }
|
||||
|
||||
Subsystem*
|
||||
ResolveVoltageSource() { return voltageSource.Resolve(); }
|
||||
int
|
||||
@@ -233,6 +250,10 @@
|
||||
SubsystemConnection voltageSource;
|
||||
AlarmIndicator electricalStateAlarm;
|
||||
AlarmIndicator modeAlarm;
|
||||
int auxScreenNumber;
|
||||
int auxScreenPlacement;
|
||||
char auxScreenLabel[64];
|
||||
char engScreenLabel[64];
|
||||
Scalar thermalResistivityCoefficient;
|
||||
Scalar startTime;
|
||||
Scalar startTimer;
|
||||
|
||||
@@ -1163,6 +1163,13 @@ static Logical
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// The AUTHORED aux screen (binary sub+0x1dc). PoweredSubsystem caches the
|
||||
// streamed block at ctor time (its resource memory dies with the Mech
|
||||
// ctor's stream buffer), so the panels land on the screens the artists
|
||||
// assigned -- not in roster order. A non-powered panel subsystem (or a
|
||||
// model that authored nothing) falls back to the sequential walk.
|
||||
//
|
||||
static int
|
||||
AuxScreenAssignmentOf(
|
||||
Entity *mech,
|
||||
@@ -1174,6 +1181,16 @@ static int
|
||||
position = 0,
|
||||
count = mech->GetSubsystemCount();
|
||||
|
||||
if (subsystem->IsDerivedFrom(PoweredSubsystem::ClassDerivations))
|
||||
{
|
||||
int authored =
|
||||
((PoweredSubsystem *)subsystem)->GetAuxScreenNumber();
|
||||
if (authored > 0)
|
||||
{
|
||||
return authored;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 1; i < count; i++) // slot 0 = the controls mapper
|
||||
{
|
||||
Subsystem
|
||||
@@ -1748,7 +1765,22 @@ void
|
||||
num.Draw(&localView, 0.0f);
|
||||
}
|
||||
|
||||
//---- subsystem label bitmap @(0x125,0x172): SKIPPED (no live label).
|
||||
//---- subsystem label bitmap @(0x125,0x172): the AUTHORED aux-screen
|
||||
// label, cached on PoweredSubsystem at ctor time (the streamed
|
||||
// resource dies with the Mech ctor's buffer). This is the panel
|
||||
// title art -- "SENSOR CLUSTER", "MYOMERS", the weapon name and
|
||||
// range strip.
|
||||
//
|
||||
if (sub->IsDerivedFrom(PoweredSubsystem::ClassDerivations))
|
||||
{
|
||||
const char *label =
|
||||
((PoweredSubsystem *)sub)->GetAuxScreenLabel();
|
||||
if (label != NULL && *label != ' | ||||