Files
TeslaRel410/restoration/source410/BT_L4/BTL4APP.CPP
T
CydandClaude Fable 5 8231bee58c BT410 5.3.68: THE LIVE-RIO FAULT IS FIXED -- it and the TM crash were one defect
The [map] audit named it in one run: subsystem 17 = Torso, attribute ids 12/13
unresolved.  The donor's decompiled torso.hpp carries the authentic enum with
binary offsets -- ids 3..15, including StickPosition(9), TorsoUp/Down/
Left/Right(10-13), TorsoCenter(14), MotionState(15).  Our table stopped at
seven entries with MotionState at id 9, on the strength of a comment claiming
the rest were messages.

The streamed control mappings bind BY ID as direct WRITE destinations, so the
truncation produced two different crashes from one cause: in TM mode the
button ids 12/13 resolved NULL (the boot write-fault the moment the joystick
polled), and in RIO mode the analog id 9 resolved to our motionState
StateIndicator -- every analog packet from a live vRIO wrote raw floats over a
watcher-socketed object, and the corrupted chains walked into unmapped memory
~30-40s later.  That was the 'intermittent' pod fault at the fixed DPMI-host
address.  vRIO down = no analog = no corruption, which is why the norio conf
launched: every observation from the whole hunt drops out of this mechanism.

Fix: the full 13-entry donor table; five int command members carved from the
dynamicsState reserve (class size unchanged); ctor zeros them.

Verified, two agreeing runs each way: TM mode launches with a clean audit and
the Thrustmaster joystick driving the torso (stickY=0.907 -> torsoElev 0.349);
RIO mode with vRIO STREAMING launches, zero faults, weapons cycling.
pod_render_rec is no longer poisoned and the norio workaround is obsolete.

The audit-before-the-archive-call pattern (BT_MAP_LOG walks the same streamed
table CreateStreamedMappings consumes, naming what will not resolve) turned a
two-day intermittent-corruption hunt into a one-run lookup.  The streamed RES
tables are binding contracts on our attribute enums.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-29 14:41:52 -05:00

557 lines
14 KiB
C++

//===========================================================================//
// File: rp4lbe4.cc //
// Project: MUNGA Brick: Red Planet LBE Application //
// Contents: //
//---------------------------------------------------------------------------//
// Date Who Modification //
// -------- --- ---------------------------------------------------------- //
// //
//---------------------------------------------------------------------------//
// Copyright (C) 1994, Virtual World Entertainment, Inc. //
// All Rights reserved worldwide //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL //
//===========================================================================//
#include <btl4.hpp>
#pragma hdrstop
#if !defined(BTL4APP_HPP)
# include <btl4app.hpp>
#endif
#if !defined(BTREG_HPP)
# include <btreg.hpp>
#endif
#if !defined(APPMGR_HPP)
# include <appmgr.hpp>
#endif
#if !defined(BTL4VID_HPP)
# include <btl4vid.hpp>
#endif
#if !defined(BTL4ARND_HPP)
# include <btl4arnd.hpp>
#endif
#if !defined(BTL4GRND_HPP)
# include <btl4grnd.hpp>
#endif
#if !defined(BTL4MODE_HPP)
# include <btl4mode.hpp>
#endif
#if !defined(BTL4MSSN_HPP)
# include <btl4mssn.hpp>
#endif
#if !defined(MECH_HPP)
# include <mech.hpp>
#endif
#if !defined(CAMSHIP_HPP)
# include <camship.hpp>
#endif
#if !defined(L4MPPR_HPP)
# include <l4mppr.hpp>
#endif
#if !defined(L4CTRL_HPP)
# include <l4ctrl.hpp>
#endif
//
//#############################################################################
//#############################################################################
//
const Receiver::HandlerEntry
BTL4Application::MessageHandlerEntries[]=
{
MESSAGE_ENTRY(BTL4Application, RunMission),
MESSAGE_ENTRY(BTL4Application, StopMission)
};
BTL4Application::MessageHandlerSet
BTL4Application::MessageHandlers(
ELEMENTS(BTL4Application::MessageHandlerEntries),
BTL4Application::MessageHandlerEntries,
L4Application::MessageHandlers
);
//
//#############################################################################
//#############################################################################
//
Derivation
BTL4Application::ClassDerivations(
L4Application::ClassDerivations,
"BTL4Application"
);
BTL4Application::SharedData
BTL4Application::DefaultData(
BTL4Application::ClassDerivations,
BTL4Application::MessageHandlers
);
//
//#############################################################################
//#############################################################################
//
BTL4Application::BTL4Application(
ResourceFile *resource_file,
ClassID class_ID,
SharedData &shared_data
):
L4Application(
resource_file,
BTL4,
class_ID,
shared_data
)
{
DEBUG_STREAM << "BTL4Application::BTL4Application" << endl;
}
//
//#############################################################################
//#############################################################################
//
BTL4Application::~BTL4Application()
{
}
//
//#############################################################################
// MakeRegistry
//#############################################################################
//
Registry*
BTL4Application::MakeRegistry()
{
Check(this);
return new BTRegistry(GetResourceFile());
}
//
//#############################################################################
// MakeVideoRenderer
//#############################################################################
//
VideoRenderer*
BTL4Application::MakeVideoRenderer()
{
Check(this);
//
// Only build a video renderer when there is a division card
//
if (!divisionParameters)
{
return NULL;
}
return
new BTL4VideoRenderer(
DefaultRendererRate,
MaxRendererComplexity,
DefaultRendererPriority,
VisualInterestType,
1
);
}
//
//#############################################################################
// MakeAudioRenderer
//#############################################################################
//
AudioRenderer*
BTL4Application::MakeAudioRenderer()
{
Check(this);
//
// Both sound cards must be present
//
if (!getenv("AWE_FRONT") || !getenv("AWE_REAR"))
{
return NULL;
}
return
new BTL4AudioRenderer(
GetApplicationManager()->GetFrameRate(),
GetMissionReviewMode()
);
}
//
//#############################################################################
// MakeModeManager
//#############################################################################
//
ModeManager*
BTL4Application::MakeModeManager()
{
Check(this);
return new BTL4ModeManager(BTL4ModeManager::ModeInitial);
}
//
//#############################################################################
// MakeControlsManager
//#############################################################################
//
ControlsManager*
BTL4Application::MakeControlsManager()
{
Check(this);
LBE4ControlsManager
*controls_manager = new LBE4ControlsManager;
Check_Pointer(controls_manager);
controls_manager->keyboardGroup[LBE4ControlsManager::KeyboardPC].Add(
ModeManager::ModeAlwaysActive,
this,
KeyCommandMessageID,
this
);
return controls_manager;
}
//
//#############################################################################
// MakeGaugeRenderer
//#############################################################################
//
GaugeRenderer*
BTL4Application::MakeGaugeRenderer()
{
Check(this);
//
// Only build the gauge renderer on a machine with gauge hardware
//
if (!getenv("L4GAUGE"))
{
return NULL;
}
return new BTL4GaugeRenderer;
}
//
//#############################################################################
// MakeMission
//#############################################################################
//
Mission*
BTL4Application::MakeMission(
NotationFile *notation_file,
ResourceFile *resources
)
{
Check(this);
return new BTL4Mission(notation_file, resources);
}
//
//#############################################################################
// MakeViewpointEntity
//#############################################################################
//
Entity*
BTL4Application::MakeViewpointEntity(Entity::MakeMessage *message)
{
Check(this);
LBE4ControlsManager
*controls_manager = GetControlsManager();
Entity
*viewing_entity = NULL;
char
*primary_mapping_name = NULL;
switch (message->classToCreate)
{
case CameraShipClassID:
//===============================================================
// Create Camera ship
//===============================================================
{
CameraShip
*viewing_camera =
CameraShip::Make((CameraShip::MakeMessage*)message);
Check(viewing_camera);
viewing_entity = viewing_camera;
//------------------------------------------------------------
// Create the controls mapping subsystem resource
//------------------------------------------------------------
CameraControlsMapper::SubsystemResource
control_subsystem_resource;
Str_Copy(
control_subsystem_resource.subsystemName,
"ControlsMapper",
sizeof(control_subsystem_resource.subsystemName)
);
control_subsystem_resource.classID = TrivialSubsystemClassID;
control_subsystem_resource.subsystemModelSize =
sizeof(control_subsystem_resource);
CameraControlsMapper
*camera_mapper;
switch (controls_manager->primaryControlType)
{
case LBE4ControlsManager::PrimaryThrustMaster:
camera_mapper =
new CameraThrustmasterMapper(
viewing_camera,
0,
&control_subsystem_resource
);
primary_mapping_name = "Thrustmaster";
break;
case LBE4ControlsManager::PrimaryRIO:
camera_mapper =
new CameraRIOMapper(
viewing_camera,
0,
&control_subsystem_resource
);
primary_mapping_name = "L4";
break;
default:
camera_mapper =
new CameraL4Mapper(
viewing_camera,
0,
&control_subsystem_resource
);
break;
}
//------------------------------------------------------------
// Attach the mapping subsystem to the entity
//------------------------------------------------------------
viewing_camera->SetMappingSubsystem(camera_mapper);
}
break;
case MechClassID:
//===============================================================
// Create Mech
//===============================================================
{
Mech
*viewing_mech = Mech::Make((Mech::MakeMessage*)message);
Check(viewing_mech);
viewing_entity = viewing_mech;
//------------------------------------------------------------
// Control deadbanding
//------------------------------------------------------------
controls_manager->SetJoystickDeadBand(0.05f);
controls_manager->SetThrottleDeadBand(0.05f);
controls_manager->SetPedalsDeadBand(0.05f);
MechControlsMapper
*mech_mapper;
switch (controls_manager->primaryControlType)
{
case LBE4ControlsManager::PrimaryThrustMaster:
mech_mapper =
new MechThrustmasterMapper(
viewing_mech,
0,
"ControlsMapper"
);
primary_mapping_name = "Thrustmaster";
break;
case LBE4ControlsManager::PrimaryRIO:
mech_mapper =
new MechRIOMapper(
viewing_mech,
0,
"ControlsMapper"
);
primary_mapping_name = "L4";
break;
default:
DEBUG_STREAM << "*****Mech has no controls mapping!*****";
Fail("BTL4Application::MakeViewpointEntity");
mech_mapper = NULL;
break;
}
//------------------------------------------------------------
// Attach the mapping subsystem to the entity
//------------------------------------------------------------
viewing_mech->SetMappingSubsystem(mech_mapper);
viewing_mech->StartCollisionAssistant();
}
break;
}
//-------------------------------------------------------------------
// Load the control mappings for the entity
//-------------------------------------------------------------------
if (primary_mapping_name)
{
ResourceDescription
*description =
application->GetResourceFile()->SearchList(
viewing_entity->GetResourceID(),
ResourceDescription::ControlMappingsListResourceType
);
if (description)
{
description =
GetResourceFile()->SearchList(
description->resourceID,
primary_mapping_name
);
if (description)
{
description->Lock();
//
// MAPPING AUDIT (env BT_MAP_LOG) -- walk the same streamed
// table CreateStreamedMappings is about to consume and name
// every DirectMapping whose attribute does not resolve.
//
// For a DirectMapping the archive code registers
// subsystem->GetAttributePointer(attributeID) as the WRITE
// DESTINATION of a controls instance; an attribute our
// reconstruction has not published resolves NULL, and the
// first device poll that fires the control writes through
// it -- the Thrustmaster-mode boot crash at
// ControlsInstanceDirectOf<int>::Update+0xE (write to
// [eax]=NULL, guest 00485E1A). RIO mode never trips it only
// because the RIO's analog stream arrives by a different
// path. This audit turns that crash into a named list of
// missing attribute publications.
//
if (getenv("BT_MAP_LOG"))
{
const int
*words = (const int *)description->resourceAddress;
int
map_count = *words;
const ControlsMapping
*map_entry = (const ControlsMapping *)(words + 1);
int
map_index;
DEBUG_STREAM << "[map] " << map_count
<< " streamed mappings for '" << primary_mapping_name
<< "'\n" << flush;
for (map_index = 0; map_index < map_count; map_index++)
{
Simulation
*map_subsystem =
viewing_entity->GetSimulation(
map_entry[map_index].subsystemID);
void
*map_attribute = NULL;
if (
map_subsystem != NULL &&
map_entry[map_index].mappingType
== ControlsMapping::DirectMapping
)
{
map_attribute =
map_subsystem->GetAttributePointer(
map_entry[map_index].attributeID);
}
if (
map_entry[map_index].mappingType
== ControlsMapping::DirectMapping &&
(map_subsystem == NULL || map_attribute == NULL)
)
{
DEBUG_STREAM << "[map] BAD direct: idx="
<< map_index
<< " group=" << (int)map_entry[map_index].controlsGroup
<< " elem=" << (int)map_entry[map_index].controlsElement
<< " subsysID=" << (int)map_entry[map_index].subsystemID
<< " attrID=" << (int)map_entry[map_index].attributeID
<< (map_subsystem == NULL
? " (NO SUBSYSTEM)" : " (attr NULL)")
<< "\n" << flush;
}
}
}
GetControlsManager()->CreateStreamedMappings(
viewing_entity,
(int*)description->resourceAddress
);
description->Unlock();
}
}
}
//-------------------------------------------------------------------
// Configure the gauges for the entity
//-------------------------------------------------------------------
if (GetGaugeRenderer())
{
GetGaugeRenderer()->ConfigureForModel("Init", viewing_entity);
}
return viewing_entity;
}
//
//#############################################################################
// RunMissionMessageHandler
//#############################################################################
//
void
BTL4Application::RunMissionMessageHandler(RunMissionMessage *message)
{
DEBUG_STREAM << "BTL4Application::RunMissionMessageHandler" << endl;
GetModeManager()->AddModeMask(BTL4ModeManager::ModePlasmaDisplay);
DEBUG_STREAM << "Turning Plasma Score Display On" << endl;
L4Application::RunMissionMessageHandler(message);
}
//
//#############################################################################
// StopMissionMessageHandler
//#############################################################################
//
void
BTL4Application::StopMissionMessageHandler(StopMissionMessage *message)
{
GetModeManager()->RemoveModeMask(BTL4ModeManager::ModePlasmaDisplay);
L4Application::StopMissionMessageHandler(message);
DEBUG_STREAM << "Turning Plasma Score Display Off" << endl;
}