Full arena1 mission on the pod rig (podrun.sh pod_render_norio, 5.3.83-86 staged), run clean to steady state with no fault. Operator: "the artifact appears to be gone and the bleed from the control mode seems to be gone as well." Both reported symptoms -- the yellow-orange bar on the colour head and the bleed into the MFDs -- from the one uninitialised read, closed by the one zero-fill. WEIGHT OF THE EVIDENCE, stated plainly rather than banked as a win: This is ONE run of an INTERMITTENT, heap-content-dependent artifact. A clean run is exactly what the UNFIXED build also produced on the gauge A/B rig, three boots running, which is the whole reason the fix could not be verified there. So the observation is not logically airtight on its own. What makes it convincing anyway is the shape of the fix rather than the count of runs: after it, no index in 64..255 can return anything but zero, whatever the heap holds. The behaviour is deterministic by construction, where before it was a lottery. Combined with the operator having seen the artifact repeatedly across BOTH this reconstruction and BT411, one clean run on the rig that was showing it is meaningful. Further clean runs strengthen it. A recurrence would mean a SECOND source -- most likely whatever blits the *CRIT/*HEAT silhouettes, still unidentified, still carrying 1500-2500px of index 231 apiece -- and NOT a failed fix. That distinction is worth keeping straight if it ever comes back. Still open and unchanged by this: the 354px runtime-generated cluster, and which widget blits the 231-bearing avatar art. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1010 lines
28 KiB
C++
1010 lines
28 KiB
C++
//===========================================================================//
|
|
// File: l4grend.cc //
|
|
// Project: MUNGA Brick: LBE4 Gauge Renderer //
|
|
// Contents: //
|
|
//---------------------------------------------------------------------------//
|
|
// Date Who Modification //
|
|
// -------- --- ---------------------------------------------------------- //
|
|
// 02/10/95 CPB Initial coding. //
|
|
// 04/12/95 CPB Moved BuildGraphicsPorts in from RP/BTL4GRND.CPP. //
|
|
//---------------------------------------------------------------------------//
|
|
// Copyright (C) 1995, Virtual World Entertainment, Inc. All rights reserved //
|
|
// PROPRIETARY and CONFIDENTIAL //
|
|
//===========================================================================//
|
|
|
|
#include <mungal4.hpp>
|
|
#pragma hdrstop
|
|
|
|
#if !defined(L4APP_HPP)
|
|
# include <l4app.hpp>
|
|
#endif
|
|
|
|
#if !defined(TIME_HPP)
|
|
# include <time.hpp> // for profiling
|
|
#endif
|
|
|
|
#if !defined(L4GREND_HPP)
|
|
# include <l4grend.hpp>
|
|
#endif
|
|
|
|
#if !defined(L4GAUGE_HPP)
|
|
# include <l4gauge.hpp>
|
|
#endif
|
|
|
|
#if !defined(MISSION_HPP)
|
|
# include <mission.hpp>
|
|
#endif
|
|
|
|
#if !defined(L4PLASMA_HPP)
|
|
# include <l4plasma.hpp>
|
|
#endif
|
|
|
|
#if !defined(NOTATION_HPP)
|
|
# include <notation.hpp>
|
|
#endif
|
|
|
|
// #define LOCAL_TEST
|
|
|
|
#if defined(LOCAL_TEST)
|
|
# define Test_Tell(n) cout << n
|
|
#else
|
|
# define Test_Tell(n)
|
|
#endif
|
|
|
|
//
|
|
//#############################################################################
|
|
// L4MethodDescription list
|
|
//#############################################################################
|
|
//
|
|
MethodDescription
|
|
ChainToPrevious = METHOD_DESCRIPTION_CHAIN(NULL); // Null:end of chain
|
|
|
|
MethodDescription
|
|
*L4MethodDescription[] =
|
|
{
|
|
&MakeConfigMethodDescription,
|
|
&MakeExternConfigMethodDescription,
|
|
&BackgroundReconfig::methodDescription,
|
|
&BackgroundLine::methodDescription,
|
|
&BackgroundRect::methodDescription,
|
|
&BackgroundFilledRect::methodDescription,
|
|
&BackgroundPixelmap::methodDescription,
|
|
&BackgroundBitmap::methodDescription,
|
|
&NumericDisplayScalar::methodDescription,
|
|
&NumericDisplaySpeed::methodDescription,
|
|
&NumericDisplayInteger::methodDescription,
|
|
&DigitalClock::methodDescription,
|
|
&RankAndScore::methodDescription,
|
|
&BarGraphSolidScalar::methodDescription,
|
|
&BarGraphPixelMapScalar::methodDescription,
|
|
&ColorState::methodDescription,
|
|
&TwoState::methodDescription,
|
|
&SegmentArcNormalized::methodDescription,
|
|
&ChainToPrevious
|
|
};
|
|
|
|
//
|
|
//#############################################################################
|
|
// L4GaugeRenderer
|
|
//#############################################################################
|
|
//
|
|
L4GaugeRenderer::L4GaugeRenderer():
|
|
GaugeRenderer()
|
|
{
|
|
//
|
|
//---------------------------------------------------------------------
|
|
// Ensure that graphicsDisplay is NULL
|
|
//---------------------------------------------------------------------
|
|
//
|
|
graphicsDisplay = NULL;
|
|
externalDisplay = NULL;
|
|
|
|
palettesAreActive = True;
|
|
scrambleVideoFlag = False;
|
|
|
|
char
|
|
*mode_string = getenv("L4GAUGE");
|
|
//
|
|
//---------------------------------------------------------------------
|
|
// NOTE that L4Application::Initialize() searches for the environment
|
|
// variable "L4GAUGE" to determine whether or not to create
|
|
// a gauge renderer.
|
|
//---------------------------------------------------------------------
|
|
//
|
|
if (mode_string != NULL)
|
|
{
|
|
//
|
|
//---------------------------------------------------------------------
|
|
// Attempt to initialize external display
|
|
//---------------------------------------------------------------------
|
|
//
|
|
char
|
|
*plasma_string = getenv("L4PLASMA");
|
|
|
|
if (plasma_string != NULL)
|
|
{
|
|
if (strnicmp(plasma_string, "COM2", 4) == 0)
|
|
{
|
|
Tell("Plasma display created on COM2\n");
|
|
externalDisplay = new PlasmaDisplay(PCS_COM2);
|
|
}
|
|
else
|
|
{
|
|
Tell("Plasma display created on COM1\n");
|
|
externalDisplay = new PlasmaDisplay(PCS_COM1);
|
|
}
|
|
|
|
if (externalDisplay != NULL)
|
|
{
|
|
Register_Object(externalDisplay);
|
|
}
|
|
}
|
|
//
|
|
//---------------------------------------------------------------------
|
|
// Attempt to initialize main display
|
|
//---------------------------------------------------------------------
|
|
//-------------------------------------------------
|
|
// Search notation file for specified resolution
|
|
//-------------------------------------------------
|
|
//
|
|
NotationFile
|
|
notation_file("GAUGE\\L4GAUGE.INI");
|
|
|
|
if (!notation_file.PageExists(mode_string))
|
|
{
|
|
Tell("L4GaugeRenderer::L4GaugeRenderer - '" <<
|
|
mode_string <<
|
|
"' not found in L4GAUGE.INI!\n"
|
|
);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
//-------------------------------------------------
|
|
// Requested resolution exists, read parameters
|
|
//-------------------------------------------------
|
|
//
|
|
int
|
|
mode(0),
|
|
width(640),
|
|
height(480),
|
|
page_size(64),
|
|
gran(64),
|
|
bpl(640*2),
|
|
pfp(0),
|
|
special(0),
|
|
q(0);
|
|
|
|
q += notation_file.GetEntry(mode_string, "mode", &mode);
|
|
q += notation_file.GetEntry(mode_string, "width", &width);
|
|
q += notation_file.GetEntry(mode_string, "height", &height);
|
|
q += notation_file.GetEntry(mode_string, "sizeInKB", &page_size);
|
|
q += notation_file.GetEntry(mode_string, "granularityInKB", &gran);
|
|
q += notation_file.GetEntry(mode_string, "bytesPerLine", &bpl);
|
|
q += notation_file.GetEntry(mode_string, "pageFcnPtr", &pfp);
|
|
q += notation_file.GetEntry(mode_string, "special", &special);
|
|
|
|
if (q == 8)
|
|
{
|
|
graphicsDisplay = new
|
|
SVGA16(
|
|
mode,
|
|
width,
|
|
height,
|
|
page_size,
|
|
gran,
|
|
bpl,
|
|
pfp,
|
|
special
|
|
);
|
|
if (graphicsDisplay != NULL)
|
|
{
|
|
Register_Object(graphicsDisplay);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// ~L4GaugeRenderer
|
|
//#############################################################################
|
|
//
|
|
L4GaugeRenderer::~L4GaugeRenderer()
|
|
{
|
|
# if defined (LOCAL_TEST)
|
|
Tell("L4GaugeRenderer::~L4GaugeRenderer()\n");
|
|
# endif
|
|
Check(this);
|
|
|
|
int i;
|
|
|
|
//----------------------------------------------------
|
|
// Make sure all gauges removed first
|
|
//----------------------------------------------------
|
|
Remove(0);
|
|
//----------------------------------------------------
|
|
// Delete the graphics ports
|
|
//----------------------------------------------------
|
|
for(i=0; i<maximumGraphicsPorts; ++i)
|
|
{
|
|
if (graphicsPort[i] != NULL)
|
|
{
|
|
Check(graphicsPort[i]);
|
|
Unregister_Object(graphicsPort[i]);
|
|
delete graphicsPort[i];
|
|
}
|
|
}
|
|
|
|
if (graphicsDisplay != NULL)
|
|
{
|
|
Check(graphicsDisplay);
|
|
Unregister_Object(graphicsDisplay);
|
|
delete graphicsDisplay;
|
|
}
|
|
|
|
if (externalDisplay != NULL)
|
|
{
|
|
Check(externalDisplay);
|
|
Unregister_Object(externalDisplay);
|
|
delete externalDisplay;
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// LocalEmergencyShutdown - restore the ABSOLUTE MINIMUM to get back
|
|
// to the system!
|
|
//#############################################################################
|
|
//
|
|
void
|
|
L4GaugeRenderer::LocalEmergencyShutdown()
|
|
{
|
|
if (graphicsDisplay != NULL)
|
|
{
|
|
// It's an EMERGENCY: a 'verify' somewhere may have already failed.
|
|
// Therefore don't check it, just kill it!
|
|
delete graphicsDisplay;
|
|
}
|
|
|
|
GaugeRenderer::LocalEmergencyShutdown();
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// TestInstance
|
|
//#############################################################################
|
|
//
|
|
Logical
|
|
L4GaugeRenderer::TestInstance() const
|
|
{
|
|
return GaugeRenderer::TestInstance();
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// StartImplementation
|
|
//#############################################################################
|
|
//
|
|
void
|
|
L4GaugeRenderer::LoadMissionImplementation(Mission *mission)
|
|
{
|
|
Check(this);
|
|
# if defined (LOCAL_TEST)
|
|
Tell("L4GaugeRenderer::LoadMissionImplementation()\n");
|
|
# endif
|
|
|
|
GaugeRenderer::LoadMissionImplementation(mission);
|
|
Check_Fpu();
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// StopImplementation
|
|
//#############################################################################
|
|
//
|
|
void
|
|
L4GaugeRenderer::ShutdownImplementation()
|
|
{
|
|
Check(this);
|
|
# if defined (LOCAL_TEST)
|
|
Tell("L4GaugeRenderer::ShutdownImplementation()\n");
|
|
# endif
|
|
|
|
// EVENTUALLY kill all interrupt-driven processes (if they exist)
|
|
|
|
GaugeRenderer::ShutdownImplementation();
|
|
Check_Fpu();
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// SuspendImplementation
|
|
//#############################################################################
|
|
//
|
|
void
|
|
L4GaugeRenderer::SuspendImplementation()
|
|
{
|
|
Check(this);
|
|
# if defined (LOCAL_TEST)
|
|
Tell("L4GaugeRenderer::SuspendImplementation()\n");
|
|
# endif
|
|
|
|
GaugeRenderer::SuspendImplementation();
|
|
Check_Fpu();
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// ResumeImplementation
|
|
//#############################################################################
|
|
//
|
|
void
|
|
L4GaugeRenderer::ResumeImplementation()
|
|
{
|
|
Check(this);
|
|
# if defined (LOCAL_TEST)
|
|
Tell("L4GaugeRenderer::ResumeImplementation()\n");
|
|
# endif
|
|
|
|
GaugeRenderer::ResumeImplementation();
|
|
Check_Fpu();
|
|
}
|
|
|
|
|
|
//
|
|
//#############################################################################
|
|
// ExecuteForeground
|
|
//#############################################################################
|
|
//
|
|
void
|
|
L4GaugeRenderer::ExecuteForeground()
|
|
{
|
|
Check(this);
|
|
//---------------------------------------
|
|
// We must call the ancestral method!
|
|
//---------------------------------------
|
|
GaugeRenderer::ExecuteForeground();
|
|
//---------------------------------------
|
|
// Process special effects
|
|
//---------------------------------------
|
|
if (graphicsDisplay != NULL)
|
|
{
|
|
Check(graphicsDisplay);
|
|
|
|
ProcessVideoEffects();
|
|
((SVGA16 *) graphicsDisplay)->UpdatePalette();
|
|
}
|
|
|
|
Check_Fpu();
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// ExecuteImplementation
|
|
//#############################################################################
|
|
//
|
|
Logical
|
|
L4GaugeRenderer::ExecuteBackgroundDisplayUpdate()
|
|
{
|
|
Check(this);
|
|
|
|
Logical
|
|
result = False; // False == 'all done'
|
|
|
|
//--------------------------------------------------------
|
|
// Update external annunciator
|
|
//
|
|
// Allow the plasma display to continue if there's
|
|
// more background time available.
|
|
//
|
|
// This method is still called if taskmode is set to
|
|
// 'foreground'. If it's called in 'foreground' mode,
|
|
// it really means that we're still waiting for foreground
|
|
// to begin processing, so we might as well update the
|
|
// external display (if needed).
|
|
//--------------------------------------------------------
|
|
if (externalDisplay != NULL)
|
|
{
|
|
Check(externalDisplay);
|
|
result = externalDisplay->Update(False); // returns 'False' if done
|
|
}
|
|
//-------------------------------------------------------
|
|
// Update display ONLY in 'copy' mode
|
|
//--------------------------------------------------------
|
|
if (taskMode == copy)
|
|
{
|
|
if (graphicsDisplay != NULL)
|
|
{
|
|
Check(graphicsDisplay);
|
|
|
|
if (graphicsDisplay->Update(False) == False)
|
|
{
|
|
//--------------------------------------------------------
|
|
// Tell executionImplementation that we're basically done.
|
|
//--------------------------------------------------------
|
|
taskMode = foreground;
|
|
}
|
|
else
|
|
{
|
|
result = True; // We're not done yet!
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// NotifyOfNewInterestingEntity
|
|
//#############################################################################
|
|
//
|
|
void
|
|
L4GaugeRenderer::NotifyOfNewInterestingEntity(
|
|
Entity *interesting_entity
|
|
)
|
|
{
|
|
SET_GAUGE_RENDERER();
|
|
Check(this);
|
|
Check(interesting_entity);
|
|
//----------------------------------------------
|
|
// Make sure gaugeImage exists for entity
|
|
//----------------------------------------------
|
|
Check(warehousePointer);
|
|
|
|
((L4Warehouse*)warehousePointer)->gaugeImageBin.Get(
|
|
interesting_entity->GetResourceID()
|
|
);
|
|
//----------------------------------------------
|
|
// Update gaugeRenderer
|
|
//----------------------------------------------
|
|
GaugeRenderer::NotifyOfNewInterestingEntity(interesting_entity);
|
|
Check_Fpu();
|
|
CLEAR_GAUGE_RENDERER();
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// NotifyOfBecomingUninterestingEntity
|
|
//#############################################################################
|
|
//
|
|
void
|
|
L4GaugeRenderer::NotifyOfBecomingUninterestingEntity(
|
|
Entity *uninteresting_entity
|
|
)
|
|
{
|
|
SET_GAUGE_RENDERER();
|
|
Check(this);
|
|
Check(uninteresting_entity);
|
|
//----------------------------------------------
|
|
// Remove gaugeImage if no longer used
|
|
//----------------------------------------------
|
|
Check(warehousePointer);
|
|
|
|
((L4Warehouse*)warehousePointer)->gaugeImageBin.Release(
|
|
uninteresting_entity->GetResourceID()
|
|
);
|
|
//----------------------------------------------
|
|
// Update gaugeRenderer
|
|
//----------------------------------------------
|
|
GaugeRenderer::NotifyOfBecomingUninterestingEntity(uninteresting_entity);
|
|
Check_Fpu();
|
|
CLEAR_GAUGE_RENDERER();
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// BuildGraphicsPort
|
|
//#############################################################################
|
|
|
|
//===========================================================================//
|
|
// RECONSTRUCTION SHADOW (source410 overrides CODE per build410.sh srcfor).
|
|
//
|
|
// Verbatim copy of CODE/RP/MUNGA_L4/L4GREND.CPP with ONE change: the port
|
|
// construction below builds a BTL4GraphicsPort instead of an L4GraphicsPort,
|
|
// to close the MFD-bleed artifact. Everything else is the archive's.
|
|
//
|
|
// THE ARTIFACT (operator report 2026-07-29: "artifacts bleeding over from the
|
|
// radar display into the MFDs ... I have noticed this in the BT411 builds as
|
|
// well, but does not happen in the original"):
|
|
//
|
|
// * The colour head is SIX BITS (L4GAUGE.CFG:4395, sec mask 0x003F) and the
|
|
// MFDs are bits 8-15 of the SAME 16-bit words -- one plane-packed
|
|
// framebuffer, so a colour-head defect can only surface as MFD garbage.
|
|
// * L4GraphicsPort::BuildSecondaryTranslation (L4VB16.CPP:5419) enumerates
|
|
// only the ACTIVE-bit combinations, filling 1<<numberOfBits entries of
|
|
// int translationTable[256] -- 64 for this port. Entries 64..255 are
|
|
// never written. Note the asymmetry: BuildAuxiliaryTranslation nests an
|
|
// IncrementInactive loop inside IncrementActive and so fills all 256,
|
|
// which is why the MONO MFD ports are safe by construction.
|
|
// * DrawPixelMap8 (L4VB16.CPP:2708) indexes that table with an 8-BIT source
|
|
// pixel and DrawPoint ORs the result in UNMASKED, so an out-of-range
|
|
// entry puts arbitrary bits into other ports' planes.
|
|
// * The colour head's own art carries out-of-range SENTINELS: AVACRIT.PCC
|
|
// is 94% <= 63 with 6% at exactly 231; BTSEC1.PCX is 99.8% <= 63 with
|
|
// 0.2% at 254. Drawn transparent they are skipped; drawn OPAQUE every
|
|
// pixel goes through the table.
|
|
//
|
|
// Why the original is PROBABLY clean (hypothesis, not proven): the port is
|
|
// heap-allocated, and an allocation that happens to arrive zeroed makes those
|
|
// reads colour 0 -- black, and invisible on a black head. Our heap has a
|
|
// different history. That would also explain why the artifact is
|
|
// INTERMITTENT and why the operator sees it in the BT411 builds too: it
|
|
// depends on heap CONTENTS, not on any drawing logic, and both reconstructions
|
|
// inherit the same latent read from the archive.
|
|
//
|
|
// The fix does not change drawing behaviour -- it only guarantees the
|
|
// out-of-range case draws colour 0, i.e. contributes no bits under DrawPoint's
|
|
// OR. That is the most conservative possible outcome and matches what the
|
|
// shipped binary is observed to do.
|
|
//
|
|
// THE OFFENDER IS NAMED (2026-07-29). BT_TRANS_PROVOKE (below) fills the
|
|
// tail with 0xFF00 instead of 0, which makes every out-of-range draw light
|
|
// all eight high-byte heads -- turning an invisible bug into a measurable
|
|
// one. On the boot cockpit it lights exactly 1030 pixels, and the mask
|
|
// splits into a glyph cluster plus ONE SOLID 13x52 BAR at screen
|
|
// (526,228)-(538,279).
|
|
//
|
|
// Decoding the art finds the source of that bar with no ambiguity left:
|
|
//
|
|
// BTSEC1.PCX, the colour head's own 480x640 background, contains exactly
|
|
// ONE out-of-range value in the whole image -- index 254, exactly 676
|
|
// pixels, a SOLID 52x13 RECTANGLE at (199,526)-(250,538).
|
|
//
|
|
// The sec port is configured at ROTATION 270 (L4GAUGE.CFG), which maps
|
|
// source (x,y) -> screen (y, 479-x). That places the rectangle at screen
|
|
// x 526..538, y 229..280. The measured mask is x 526..538, y 228..279.
|
|
// Same rectangle, to the pixel.
|
|
//
|
|
// So one uninitialised read produces BOTH reported symptoms, depending only
|
|
// on which bits the heap garbage happens to have set:
|
|
//
|
|
// low 6 bits set -> a coloured block on the COLOUR head. This is the
|
|
// operator's second report the same day, "that yellow
|
|
// orange artifact", a vertical bar beside the RANGE
|
|
// readout -- which is precisely where the rotated
|
|
// rectangle lands.
|
|
// high 8 bits set -> garbage in the MFD / ENG / COMM planes. This is the
|
|
// first report, "bleeding over ... into the MFDs".
|
|
//
|
|
// VERIFICATION STATUS:
|
|
//
|
|
// * MECHANISM: proven from source (asymmetric builders) AND live (the
|
|
// provoke control lights 1030 pixels that the fix zeroes).
|
|
// * OFFENDING ART: proven by decode (BTSEC1.PCX index 254, 676px, and the
|
|
// rotation arithmetic lands it on the reported spot).
|
|
// * NON-REGRESSIVE: gauge A/B, three boots (unfixed x2, fixed x1) score
|
|
// identically on every head -- Mfd1 132/0, Mfd2 37/7, Mfd3 23/0 --
|
|
// against a ~6px across-boot noise floor.
|
|
// * OPERATOR-CONFIRMED CURED, 2026-07-30. A full arena1 mission run on
|
|
// the pod rig (podrun.sh pod_render_norio, this build staged): "the
|
|
// artifact appears to be gone and the bleed from the control mode seems
|
|
// to be gone as well." Both reported symptoms, one run, no fault.
|
|
//
|
|
// The honest weight of that: it is ONE run of an INTERMITTENT,
|
|
// heap-content-dependent artifact, and a clean run is exactly what the
|
|
// UNFIXED build also produced on the gauge A/B rig. So it is not
|
|
// logically airtight on its own. What makes it convincing anyway is
|
|
// that the fix is deterministic by construction -- after it, no index
|
|
// in 64..255 can return anything but 0, whatever the heap holds -- and
|
|
// that the operator had been seeing the artifact repeatedly, across
|
|
// both this reconstruction and BT411. Further clean runs strengthen
|
|
// it; a recurrence would mean a SECOND source, not a failed fix.
|
|
//
|
|
// A first "fixed" capture appeared to REGRESS the MFDs by ~2700px. It was a
|
|
// bad grab (caught mid-draw); the rebuild-and-rerun matched the unfixed
|
|
// numbers exactly. Recorded here because the two-runs rule is what caught it.
|
|
//===========================================================================//
|
|
|
|
class BTL4GraphicsPort :
|
|
public L4GraphicsPort
|
|
{
|
|
public:
|
|
BTL4GraphicsPort(
|
|
Video16BitBuffered *graphics_display,
|
|
const char *name,
|
|
int rotation,
|
|
int bit_mask,
|
|
SVGA16::PaletteID palette_ID,
|
|
L4GraphicsPort::ChannelEnableID channel_enable
|
|
):
|
|
L4GraphicsPort(
|
|
graphics_display, name, rotation, bit_mask,
|
|
palette_ID, channel_enable
|
|
)
|
|
{
|
|
//
|
|
// SECONDARY ports only. The low byte non-zero is exactly the
|
|
// test BuildSecondaryTranslation asserts on
|
|
// (Verify((bitMask & 0xFF) != 0)); an AUXILIARY port fills all
|
|
// 256 entries itself and must not be touched.
|
|
//
|
|
if ((bit_mask & 0xFF) != 0)
|
|
{
|
|
int
|
|
first = 1 << numberOfBits,
|
|
i;
|
|
|
|
//
|
|
// POSITIVE CONTROL. Zero is the fix; it is also invisible,
|
|
// so on its own it cannot tell us whether the out-of-range
|
|
// path is ever TAKEN on a given screen. BT_TRANS_PROVOKE
|
|
// fills the same tail with every MFD bit instead: if the
|
|
// path fires, the MFD heads flood with garbage and the A/B
|
|
// scoreboard's "extra" columns explode. If they do not
|
|
// move, the sec port never sees an out-of-range index on
|
|
// that screen and the fix is inert there.
|
|
//
|
|
int
|
|
fill = (getenv("BT_TRANS_PROVOKE") != NULL) ? 0xFF00 : 0;
|
|
|
|
for (i = first; i < 256; ++i)
|
|
{
|
|
translationTable[i] = fill;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
//
|
|
void
|
|
L4GaugeRenderer::BuildGraphicsPort(
|
|
int port_number,
|
|
const char *name,
|
|
int rotation,
|
|
int bitAllocation,
|
|
SVGA16::PaletteID palette_ID,
|
|
L4GraphicsPort::ChannelEnableID enable_ID,
|
|
Palette8 *palette
|
|
)
|
|
{
|
|
Test_Tell("L4GaugeRenderer::BuildGraphicsPort\n");
|
|
Check(this);
|
|
|
|
if (graphicsDisplay != NULL)
|
|
{
|
|
Test_Tell("...non-NULL display\n");
|
|
Verify(port_number < maximumGraphicsPorts);
|
|
Verify(port_number >= 0);
|
|
Test_Tell("...port_number= " << port_number << "\n");
|
|
//
|
|
//---------------------------------------------------------
|
|
// If it already exists, delete it
|
|
//---------------------------------------------------------
|
|
//
|
|
L4GraphicsPort
|
|
*graphics_port = (L4GraphicsPort *) graphicsPort[port_number];
|
|
|
|
if (graphics_port != NULL)
|
|
{
|
|
Test_Tell("...deleting existing port\n");
|
|
Check(graphics_port);
|
|
Unregister_Object(graphics_port);
|
|
delete graphics_port;
|
|
}
|
|
//
|
|
//---------------------------------------------------------
|
|
// Attempt to create it
|
|
//---------------------------------------------------------
|
|
//
|
|
graphicsPort[port_number] = new BTL4GraphicsPort(
|
|
(Video16BitBuffered *) graphicsDisplay,
|
|
name,
|
|
rotation,
|
|
bitAllocation,
|
|
palette_ID,
|
|
enable_ID
|
|
);
|
|
|
|
graphics_port = (L4GraphicsPort *) graphicsPort[port_number];
|
|
|
|
Test_Tell("...new port at " << graphics_port << "\n");
|
|
//
|
|
//---------------------------------------------------------
|
|
// If created, register it and build the palette
|
|
//---------------------------------------------------------
|
|
//
|
|
if (graphics_port != NULL)
|
|
{
|
|
Check(graphics_port);
|
|
Register_Object(graphics_port);
|
|
//
|
|
// Set the palette for the port
|
|
// (Note: secondary uses lowest 8 bits)
|
|
//
|
|
if ((bitAllocation > 0) && (bitAllocation < 0x0100))
|
|
{
|
|
Test_Tell("...secondary\n");
|
|
graphics_port->SetSecondaryPalette(palette);
|
|
}
|
|
else
|
|
{
|
|
Test_Tell("...auxiliary\n");
|
|
graphics_port->SetAuxiliaryPalette();
|
|
}
|
|
}
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// RemapGraphicsPort
|
|
//#############################################################################
|
|
//
|
|
void
|
|
L4GaugeRenderer::RemapGraphicsPort(
|
|
int port_number,
|
|
SVGA16::PaletteID palette_ID,
|
|
L4GraphicsPort::ChannelEnableID enable_ID,
|
|
Palette8 *palette
|
|
)
|
|
{
|
|
Check(this);
|
|
Test_Tell("L4GaugeRenderer::RemapGraphicsPort\n");
|
|
|
|
if (graphicsDisplay != NULL)
|
|
{
|
|
Test_Tell("...non-NULL display\n");
|
|
Verify(port_number < maximumGraphicsPorts);
|
|
Verify(port_number >= 0);
|
|
Test_Tell("...port_number= " << port_number << "\n");
|
|
//---------------------------------------------------------
|
|
// If it exists, change the palette ID and set the palette
|
|
//---------------------------------------------------------
|
|
L4GraphicsPort
|
|
*graphics_port = (L4GraphicsPort *) graphicsPort[port_number];
|
|
|
|
if (graphics_port != NULL)
|
|
{
|
|
Check(graphics_port);
|
|
//
|
|
// Set the palette ID
|
|
//
|
|
graphics_port->SetPaletteID(palette_ID);
|
|
graphics_port->SetEnableID(enable_ID);
|
|
|
|
//
|
|
// Set the palette for the port
|
|
// (Note: secondary uses lowest 8 bits)
|
|
//
|
|
|
|
int
|
|
bit_mask = graphics_port->GetBitMask();
|
|
|
|
if (bit_mask & 0x00FF) // must be secondary
|
|
{
|
|
Test_Tell("...secondary\n");
|
|
Verify(palette != NULL);
|
|
graphics_port->SetSecondaryPalette(palette);
|
|
}
|
|
else
|
|
{
|
|
Test_Tell("...auxiliary\n");
|
|
graphics_port->SetAuxiliaryPalette();
|
|
}
|
|
}
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// BuildExternalGraphicsPort
|
|
//#############################################################################
|
|
//
|
|
void
|
|
L4GaugeRenderer::BuildExternalGraphicsPort(
|
|
int port_number,
|
|
const char *name,
|
|
int /*bitAllocation*/
|
|
)
|
|
{
|
|
Check(this);
|
|
Test_Tell(
|
|
"L4GaugeRenderer::BuildExternalGraphicsPort(" << port_number << ")\n"
|
|
);
|
|
|
|
if (externalDisplay != NULL)
|
|
{
|
|
Test_Tell("...non-NULL display\n");
|
|
Verify(port_number < maximumGraphicsPorts);
|
|
Verify(port_number >= 0);
|
|
Test_Tell("...port_number= " << port_number << "\n");
|
|
//
|
|
//---------------------------------------------------------
|
|
// If it already exists, delete it
|
|
//---------------------------------------------------------
|
|
//
|
|
L4GraphicsPort
|
|
*graphics_port = (L4GraphicsPort *) graphicsPort[port_number];
|
|
|
|
if (graphics_port != NULL)
|
|
{
|
|
Test_Tell("...deleting existing port\n");
|
|
Check(graphics_port);
|
|
Unregister_Object(graphics_port);
|
|
delete graphics_port;
|
|
}
|
|
//
|
|
//---------------------------------------------------------
|
|
// Attempt to create it
|
|
//---------------------------------------------------------
|
|
//
|
|
graphicsPort[port_number] = new L4BytePort(
|
|
(Video8BitBuffered *) externalDisplay,
|
|
name,
|
|
0 // rotation
|
|
);
|
|
|
|
graphics_port = (L4GraphicsPort *) graphicsPort[port_number];
|
|
|
|
Test_Tell("...new port at " << graphics_port << "\n");
|
|
//
|
|
//---------------------------------------------------------
|
|
// If created, register it
|
|
//---------------------------------------------------------
|
|
//
|
|
if (graphics_port != NULL)
|
|
{
|
|
Check(graphics_port);
|
|
Register_Object(graphics_port);
|
|
}
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// FadeToWhite, FadeToGame
|
|
//#############################################################################
|
|
//
|
|
void
|
|
L4GaugeRenderer::FadeToWhite(Scalar number_of_seconds)
|
|
{
|
|
Check(this);
|
|
Verify(number_of_seconds >= 0.0); // zero is allowed (instant change)
|
|
|
|
// cout << "L4GaugeRenderer::FadeToWhite(" << number_of_seconds << ")\n";
|
|
|
|
if (graphicsDisplay != NULL)
|
|
{
|
|
Check(graphicsDisplay);
|
|
((SVGA16 *)graphicsDisplay)->FadeToWhite(number_of_seconds);
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
void
|
|
L4GaugeRenderer::FadeToNormal(Scalar number_of_seconds)
|
|
{
|
|
Check(this);
|
|
Verify(number_of_seconds >= 0.0); // zero is allowed (instant change)
|
|
|
|
// cout << "L4GaugeRenderer::FadeToNormal(" << number_of_seconds << ")\n";
|
|
|
|
if (graphicsDisplay != NULL)
|
|
{
|
|
Check(graphicsDisplay);
|
|
((SVGA16 *)graphicsDisplay)->FadeToPalettes(number_of_seconds);
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// Configure
|
|
//#############################################################################
|
|
//
|
|
void
|
|
L4GaugeRenderer::ConfigureForModel(
|
|
const char *configuration_name,
|
|
Entity *entity
|
|
)
|
|
{
|
|
Check(this);
|
|
Check_Pointer(configuration_name);
|
|
|
|
Test_Tell(
|
|
"L4GaugeRenderer::ConfigureForModel(" <<
|
|
configuration_name << ", " <<
|
|
entity << ")\n"
|
|
);
|
|
|
|
Check(application);
|
|
Check(entity);
|
|
|
|
Mission
|
|
*mission = application->GetCurrentMission();
|
|
Check(mission);
|
|
|
|
const char
|
|
*model_name = mission->GetGameModel();
|
|
Check_Pointer(model_name);
|
|
|
|
char
|
|
full_name[64];
|
|
|
|
Str_Copy(full_name, model_name, sizeof(full_name));
|
|
Str_Cat(full_name, configuration_name, sizeof(full_name));
|
|
|
|
Test_Tell(
|
|
"L4GaugeRenderer::ConfigureForModel, full_name=" << full_name <<
|
|
")\n"
|
|
);
|
|
|
|
Configure(full_name, entity);
|
|
Check_Fpu();
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// SpecialEffect
|
|
//#############################################################################
|
|
//
|
|
void
|
|
L4GaugeRenderer::SpecialEffect(
|
|
VideoEffectType type,
|
|
Scalar duration
|
|
)
|
|
{
|
|
Check(this);
|
|
|
|
switch (type)
|
|
{
|
|
case scrambleVideo:
|
|
if (graphicsDisplay != NULL)
|
|
{
|
|
scrambleVideoFlag = True;
|
|
scrambleVideoTimeout = ((Scalar)Now()) + duration;
|
|
|
|
Check(graphicsDisplay);
|
|
((SVGA16*) graphicsDisplay)->FunkyVideo(True);
|
|
}
|
|
break;
|
|
|
|
// other modes go here
|
|
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
void
|
|
L4GaugeRenderer::ProcessVideoEffects()
|
|
{
|
|
Check(this);
|
|
Check(graphicsDisplay);
|
|
|
|
Time
|
|
now = Now();
|
|
|
|
if (scrambleVideoFlag)
|
|
{
|
|
if (now >= scrambleVideoTimeout)
|
|
{
|
|
scrambleVideoFlag = False;
|
|
((SVGA16*) graphicsDisplay)->FunkyVideo(False);
|
|
}
|
|
}
|
|
|
|
Check_Fpu();
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
// Profiling support
|
|
//--------------------------------------------------------------------
|
|
Scalar
|
|
L4GaugeRenderer::GetCurrentFramePercentage()
|
|
{
|
|
Check(this);
|
|
return Get_Frame_Percent_Used();
|
|
}
|
|
|
|
|
|
|
|
|