The canopy-eye confirming run (5th post-Torso-fix run with vRIO live) faulted with the old signature during the load drain -- four clean runs then one fault. The two-runs rule keeps earning its keep. The BT_MAP_LOG audit now prints the whole resolved contract, not just NULLs. The L4 list carries 13 direct mappings: mapper stick/throttle/reverse/looks (ids 3/4/6/10-12), Torso torsoCenter (id 14, matching the donor), and TriggerState (id 19 = the known 0x13 binding) on six weapons. Every destination resolves and every size is right -- the 8-byte joystick write lands in an 8-byte ControlsJoystick, buttons are ints into ints. One type wart noted, not fixed: MechWeapon binds TriggerState onto fireImpulse, a Scalar -- a ButtonGroup direct writes an int bit-pattern, so a real trigger press stores 1.4e-45f. Size-safe, but the fire FSM will never read a real button press as a pull; fire worked in tests only via the BT_FORCE_FIRE hook, which writes a proper float. The RIO trigger needs an int home for TriggerState. Surviving fault theories, in current order: an emulator/DPMI-host serial-IRQ interaction whose probability guest timing merely modulates (the fault's constants -- fixed host EIP, fixed cr2, always during the load drain -- fit this better than the corruption story ever did, since the smash point never moved across builds where our BSS moved); a second corruption source outside the direct mappings; sampling noise. riostreak.sh is measuring the post-fix rate over four consecutive runs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
579 lines
15 KiB
C++
579 lines
15 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;
|
|
}
|
|
else if (
|
|
map_entry[map_index].mappingType
|
|
== ControlsMapping::DirectMapping
|
|
)
|
|
{
|
|
//
|
|
// Print the RESOLVED contract too. The Torso lesson: a
|
|
// non-NULL pointer can still be the WRONG member if our
|
|
// enum order differs from the authentic one -- a
|
|
// ScalarGroup direct writes raw floats through whatever
|
|
// this resolves to. The subsystem name + id list is what
|
|
// gets diffed against the donor's decompiled enums, class
|
|
// by class.
|
|
//
|
|
DEBUG_STREAM << "[map] dir sub="
|
|
<< (int)map_entry[map_index].subsystemID
|
|
<< " cls=" << (int)map_subsystem->GetClassID()
|
|
<< " id=" << (int)map_entry[map_index].attributeID
|
|
<< " grp=" << (int)map_entry[map_index].controlsGroup
|
|
<< " elem=" << (int)map_entry[map_index].controlsElement
|
|
<< "\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;
|
|
}
|