Files
TeslaRel410/restoration/source410/MUNGA_L4/L4APP.CPP
T
CydandClaude Fable 5 5b35eb973c 4.10 reconstruction: BTL4OPT.EXE links clean and BOOTS to the first staged brick
The reconstructed tree now produces a runnable binary with the authentic
1995 toolchain (BC4.52 / tlink32 / DPMI32):

- BTL4.CPP main TU reconstructed from the 4.11 Ghidra decomp (FUN_0040109c)
  + the 4.10 binary's own string pool + surviving RPL4TOOL.CPP house style;
  probe_main.cpp scaffold retired (BTL4.NOTES.md documents every decoded call)
- L4NET.CPP staged: L4NetworkManager ctor/dtor + 10 vtable-pulled virtuals
  (standalone-benign ones no-op, network ones Fail loudly) + NetNub client
  globals (Net_Common_Ptr=NULL routes L4File to its plain-DOS path)
- build410.sh: libs now built in the AUTHENTIC makefile member order
  (MUNGA.MAK / mungal4.mak / BT.MAK / BTL4.MAK). Order is load-bearing:
  tlink emits static-init records in module pull order, and alphabetical
  order booted into a null-vptr crash (IcomManager::ClassDerivations
  constructing before parent NetworkClient::ClassDerivations). Also fixed
  stage_link to the proven 32-bit lib set (SOSDBXC+SOSMBXC, no WATTCPLG)
- BOXTREE.HPP MemoryBlock unify, BTL4GRND notify stubs, remaining engine
  backfills (audio/gauge/resource/stream TUs) that closed the deep ledger

Smoke test (DOSBox-X + 32RTM, copy of the pod BT tree, our exe swapped in):
  BattleTech v4.10
  BTL4Application::BTL4Application
  l4net.cpp(22): L4NetworkManager -- l4net.cpp not yet reconstructed
Static init, main, -egg parse, BTL4.RES load (version 1.0.6 check passes),
ApplicationManager and the BTL4Application ctor chain all execute real
reconstructed code; boot halts at the first staged Fail() as designed.
Next brick: the real l4net.cpp body.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 19:05:53 -05:00

877 lines
23 KiB
C++

# if !defined(MUNGAL4_HPP)
# include <mungal4.hpp>
# endif
#pragma hdrstop
# if !defined(CAMSHIP_HPP)
# include <camship.hpp>
# endif
# if !defined(APPMSG_HPP)
# include <appmsg.hpp>
# endif
# if !defined(L4APP_HPP)
# include <l4app.hpp>
# endif
# if !defined(L4CTRL_HPP)
# include <l4ctrl.hpp>
# endif
# if !defined(L4ICOM_HPP)
# include <l4icom.hpp>
# endif
# if !defined(L4VIDEO_HPP)
# include <l4video.hpp>
# endif
# if !defined(L4AUDRND_HPP)
# include <l4audrnd.hpp>
# endif
# if !defined(L4GREND_HPP)
# include <l4grend.hpp>
# endif
# if !defined(L4NET_HPP)
# include <l4net.hpp>
# endif
# if !defined(L4MPPR_HPP)
# include <l4mppr.hpp>
# endif
# if !defined(PLAYER_HPP)
# include <player.hpp>
# endif
# if !defined(MISSION_HPP)
# include <mission.hpp>
# endif
L4Application*&
l4_application = (L4Application*&)application;
const Receiver::HandlerEntry
L4Application::MessageHandlerEntries[]=
{
MESSAGE_ENTRY(L4Application, RunMission),
MESSAGE_ENTRY(L4Application, StopMission),
MESSAGE_ENTRY(L4Application, LightsOut),
MESSAGE_ENTRY(L4Application, KeyCommand)
};
L4Application::MessageHandlerSet
L4Application::MessageHandlers(ELEMENTS(L4Application::MessageHandlerEntries), L4Application::MessageHandlerEntries, Application::MessageHandlers);
//#############################################################################
// Virtual Data support
//
Derivation
L4Application::ClassDerivations(Application::ClassDerivations, "L4Application");
L4Application::SharedData
L4Application::DefaultData(
L4Application::ClassDerivations,
L4Application::MessageHandlers
);
//##########################################################################
//################## L4Application Static Data #######################
//##########################################################################
Logical L4Application::seeSolids = False;
CString L4Application::eggNotationFileName;
unsigned long L4Application::networkCommonFlatAddress;
int L4Application::missionReviewMode = 0;
CString L4Application::rioPlaybackFileName;
CString L4Application::rioRecordingFileName;
//
//#############################################################################
// ParseCommandLine
//#############################################################################
//
Logical
L4Application::ParseToken(
int *arguement,
int argc,
char *argv[]
)
{
Check_Pointer(argv);
if (!stricmp(argv[*arguement], "-egg"))
{
if ((*arguement+1) < argc && argv[*arguement+1][0] != '-')
{
eggNotationFileName = argv[++(*arguement)];
}
else
{
DEBUG_STREAM << "\nEgg file not specified after -egg\n" << flush << flush;
return False;
}
}
else if (!stricmp(argv[*arguement], "-net"))
{
if ((*arguement+1) < argc && argv[*arguement+1][0] != '-')
{
networkCommonFlatAddress = atol(argv[++(*arguement)]);
}
else
{
DEBUG_STREAM << "\nAddress not specified after -net\n" << flush;
return False;
}
}
else if (!stricmp(argv[*arguement], "-r"))
{
if ((*arguement+1) < argc && argv[*arguement+1][0] != '-')
{
rioRecordingFileName = argv[++(*arguement)];
CString playback = GetRIOPlaybackFileName();
if (playback && strlen(playback))
{
DEBUG_STREAM << "\nPlayback already specified!\n" << flush;
return False;
}
}
else
{
DEBUG_STREAM << "\nRecording file not specified after -r\n" << flush;
return False;
}
}
else if (!stricmp(argv[*arguement], "-p"))
{
if ((*arguement+1) < argc && argv[*arguement+1][0] != '-')
{
rioPlaybackFileName = argv[++(*arguement)];
CString recording = GetRIORecordingFileName();
if (recording && strlen(recording))
{
DEBUG_STREAM << "\nRecording already specified!\n" << flush;
return False;
}
}
else
{
DEBUG_STREAM << "\nPlayback file not specified after -egg\n" << flush;
return False;
}
}
else if (!stricmp(argv[*arguement], "-solids"))
{
seeSolids = True;
}
else if (
!stricmp(argv[*arguement], "-h") || !stricmp(argv[*arguement], "-help")
)
{
DEBUG_STREAM << "\n" << argv[0] <<
" -egg <filename> -net <memory_address> -solids -h -help\n";
return False;
}
return True;
}
//
//#############################################################################
// ParseCommandLine
//#############################################################################
//
Logical
L4Application::ParseCommandLine(
int argc,
char *argv[],
TokenParser parser
)
{
Check_Pointer(argv);
//
// Process options
//
seeSolids = False;
eggNotationFileName = "";
networkCommonFlatAddress = NULL;
missionReviewMode = 0;
if (argc > 1)
{
for (int i = 1; i < argc; ++i)
{
if (!stricmp(argv[i], "-mr"))
{
missionReviewMode = 1;
} else if (!(*parser)(&i, argc, argv))
{
return False;
}
}
}
return True;
}
//
//#############################################################################
// TestInstance
//#############################################################################
//
Logical
L4Application::TestInstance() const
{
if (!IsDerivedFrom(ClassDerivations))
{
return False;
}
return True;
}
//
//#############################################################################
// L4Application
//#############################################################################
//
L4Application::L4Application(
ResourceFile *resource_file,
ApplicationID application_ID,
ClassID class_ID,
SharedData &shared_data
):
Application(resource_file, application_ID, class_ID, shared_data)
{
divisionParameters = getenv("DPLARG");
}
//
//#############################################################################
// Initialize
//#############################################################################
//
void
L4Application::Initialize()
{
Check(this);
InitializeTillConsole();
//
// Create the console host and socket
//
L4NetworkManager *net_mgr = GetNetworkManager();
net_mgr->CreateConsoleHost();
}
//
//#############################################################################
// Initialize
//#############################################################################
//
void
L4Application::InitializeTillConsole()
{
Check(this);
Application::Initialize();
}
//
//#############################################################################
// MakeModeManager
//#############################################################################
//
ModeManager*
L4Application::MakeModeManager()
{
return new ModeManager((ModeMask)ModeManager::ModeAlwaysActive); // Normally called at top level!!
}
//
//#############################################################################
// MakeNetworkManager
//#############################################################################
//
NetworkManager*
L4Application::MakeNetworkManager()
{
return new L4NetworkManager;
}
//
//#############################################################################
// MakeControlsManager
//#############################################################################
//
ControlsManager*
L4Application::MakeControlsManager()
{
LBE4ControlsManager *manager = new LBE4ControlsManager();
Check(manager);
manager->keyboardGroup[LBE4ControlsManager::KeyboardPC].Add(ModeManager::ModeAlwaysActive, this, KeyCommandMessageID, this);
return manager;
}
//
//#############################################################################
// MakeIntercomManager
//#############################################################################
//
IcomManager*
L4Application::MakeIntercomManager()
{
L4IcomManager
*manager = new L4IcomManager();
Check(manager);
return manager;
}
//
//#############################################################################
// MakeVideoRenderer
//#############################################################################
//
VideoRenderer*
L4Application::MakeVideoRenderer()
{
if (divisionParameters)
{
return
new DPLRenderer(
DefaultRendererRate,
MaxRendererComplexity,
DefaultRendererPriority,
VisualInterestType,
1
);
}
return NULL;
}
//
//#############################################################################
// MakeAudioRenderer
//#############################################################################
//
AudioRenderer*
L4Application::MakeAudioRenderer()
{
/* char *blaster1, *blaster2;
blaster1 = getenv(FRONT_CARD_ENV_VAR);
blaster2 = getenv(REAR_CARD_ENV_VAR);
if (blaster1 != NULL && blaster2 != NULL)
{
return new L4AudioRenderer(DefaultRendererRate, False);
}
return NULL;*/
return new L4AudioRenderer(DefaultRendererRate, False);
}
//
//#############################################################################
// MakeGaugeRenderer
//#############################################################################
//
GaugeRenderer*
L4Application::MakeGaugeRenderer()
{
char *mode_string = getenv("L4GAUGE");
if (mode_string != NULL)
{
return new L4GaugeRenderer;
}
return NULL;
}
//
//#############################################################################
// Terminate
//#############################################################################
//
void
L4Application::Terminate()
{
Check(this);
//
//--------------------------------------------------------------------------
// Turn off illumination
//--------------------------------------------------------------------------
//
PilotIllumination(False);
//
// Call inherited method
//
Application::Terminate();
}
//
//#############################################################################
// ~L4Application
//#############################################################################
//
L4Application::~L4Application()
{
}
//
//#############################################################################
// MakeAndLinkViewpointEntity
//#############################################################################
//
Entity*
L4Application::MakeAndLinkViewpointEntity(Entity::MakeMessage* message)
{
Check(this);
Check(message);
Application::MakeAndLinkViewpointEntity(message);
//
//--------------------------------------------------------------------------
// Turn on lights for pilot
// (turned off by RunMissionMessageHandler)
//--------------------------------------------------------------------------
//
PilotIllumination(True);
//
//--------------------------------------------------------------------------
// Return viewpoint entity
//--------------------------------------------------------------------------
//
return GetViewpointEntity();
}
//
//#############################################################################
// MakeViewpointEntity
//#############################################################################
//
Entity*
L4Application::MakeViewpointEntity(Entity::MakeMessage *message)
{
Entity *viewing_entity;
viewing_entity = NULL;
switch(message->classToCreate)
{
case CameraShipClassID:
viewing_entity = CameraShip::Make((CameraShip::MakeMessage*)message);
Check(viewing_entity);
CameraShip *viewing_camera = Cast_Object(CameraShip*, viewing_entity);
Check(viewing_camera);
//
// Create controls mapping object
//
CameraControlsMapper
*camera_mapper;
Verify(GetControlsManager() != NULL);
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);
LBE4ControlsManager* controls = GetControlsManager();
Check(controls);
switch (controls->primaryControlType)
{
case LBE4ControlsManager::PrimaryThrustMaster:
Tell("L4Application::MakeViewpointEntity, using ThrustMaster\n");
camera_mapper =
new CameraThrustmasterMapper(
viewing_camera,
CameraShip::ControlsMapperSubsystem,
&control_subsystem_resource
);
Register_Object(camera_mapper);
viewing_camera->SetMappingSubsystem(camera_mapper);
break;
case LBE4ControlsManager::PrimaryRIO:
Tell("L4Application::MakeViewpointEntity, using RIO\n");
camera_mapper =
new CameraRIOMapper(
viewing_camera,
CameraShip::ControlsMapperSubsystem,
&control_subsystem_resource
);
Register_Object(camera_mapper);
viewing_camera->SetMappingSubsystem(camera_mapper);
break;
default:
Fail("L4Application::MakeViewpointEntity, *** NO MAPPER! ***\n");
break;
}
//
//-------------------------------------------
// Create gauges for this entity
//-------------------------------------------
//
if (GetGaugeRenderer() != NULL)
{
L4GaugeRenderer *gauge_renderer = GetGaugeRenderer();
Check(gauge_renderer);
gauge_renderer->ConfigureForModel(
"Init",
viewing_entity
);
}
break;
}
return viewing_entity;
}
//
//#############################################################################
// RunMissionMessageHandler
//#############################################################################
//
void
L4Application::RunMissionMessageHandler(RunMissionMessage *message)
{
Check(this);
Verify(message->messageID == RunMissionMessageID);
//
//--------------------------------------------------------------------------
// If the application is already running then ignore this message
//--------------------------------------------------------------------------
//
switch (GetApplicationState())
{
case RunningMission:
break;
//
//-------------------------------------
// Start the playback if it is required
//-------------------------------------
//
case LaunchingMission:
{
LBE4ControlsManager *controls = GetControlsManager();
Check(controls);
if (controls->IsRecording())
{
controls->StartRecording();
}
else if (controls->IsPlayingBack())
{
controls->StartPlayback();
}
}
break;
case WaitingForLaunch:
PilotIllumination(False);
break;
default:
Fail("Application::RunMissionMessageHandler - Not ready to run!\n");
break;
}
//
//--------------------------------------------------------------------------
// Call previous handler
//--------------------------------------------------------------------------
//
Application::RunMissionMessageHandler(message);
}
//
//#############################################################################
// StopMissionMessageHandler
//#############################################################################
//
void
L4Application::StopMissionMessageHandler(StopMissionMessage *message)
{
Check(this);
Verify(message->messageID == StopMissionMessageID);
//
//--------------------------------------------------------------------------
// Call inherited handler
//--------------------------------------------------------------------------
//
Application::StopMissionMessageHandler(message);
//
//--------------------------------------------------------------------------
// Turn on illumination
//--------------------------------------------------------------------------
//
PilotIllumination(True);
//
//--------------------------------------------------------------------------
// Post a delayed message to ourselves to turn off the illumination
//--------------------------------------------------------------------------
//
Receiver::Message
lights_out_message(LightsOutMessageID, sizeof(Receiver::Message));
Time
event_time;
event_time = Now();
event_time += 30.0f;
Post(
LowEventPriority, // priority
this, // target
&lights_out_message, // message
event_time // when
);
}
//
//#############################################################################
// KeyCommandMessageHandler
//#############################################################################
//
void
L4Application::KeyCommandMessageHandler(
ReceiverDataMessageOf<ControlsKey> *message
)
{
Check(this);
Check(message);
switch (message->dataContents)
{
//--------------------------------------------
// FrameDump from Division card to Targa file
//--------------------------------------------
case PCK_ALT_F:
{
DPLRenderer *dpl_renderer = l4_application->GetVideoRenderer();
if (dpl_renderer)
{
Check(dpl_renderer);
dpl_renderer->DPLFrameDump(True); // True indicates antialiased
}
break;
}
//------------------------------------
// Report current free memory in card
//------------------------------------
case PCK_ALT_K:
{
//STUBBED: HEAP RB 1/20/07
/*DPLRenderer *dpl_renderer = l4_application->GetVideoRenderer();
if (dpl_renderer)
{
Check(dpl_renderer);
dpl_renderer->DPLReportFreeMemory(DEBUG_STREAM) << flush;
}
DEBUG_STREAM << UserHeap::MainStorage.GetTotalHeapLeft() << '('
<< UserHeap::MainStorage.GetBiggestHeapLeft()
<< " contiguous) bytes of MUNGA core left\n"
<< UserHeap::MainStorage.GetHeapUsed() << " bytes of heap used\n";
#if defined(USE_MEMORY_ANALYSIS)
DEBUG_STREAM << UserHeap::MainStorage.GetLowestTotalHeapLeft()
<< " bytes minimum available so far\n"
<< UserHeap::MainStorage.GetMostHeapUsed()
<< " bytes maximum used so far\n";
#endif
MemoryBlockBase::UsageReport();*/
break;
}
//------------------------------------
// Report current event queue
//------------------------------------
case 'E':
{
Check(application);
application->DumpEventQueue();
break;
}
//---------------------------------------
// Report performance statistics (Alt-?)
//---------------------------------------
case PCK_ALT_SLASH:
{
DPLRenderer *dpl_renderer = l4_application->GetVideoRenderer();
if (dpl_renderer)
{
Check(dpl_renderer);
dpl_renderer->DPLReportPerfStats(DEBUG_STREAM);
}
break;
}
//--------------------------
// Toggle Wireframe display
//--------------------------
case PCK_ALT_W:
{
DPLRenderer *dpl_renderer = l4_application->GetVideoRenderer();
if (dpl_renderer)
{
Check(dpl_renderer);
dpl_renderer->DPLToggleWireframe();
}
break;
}
//--------------------------
// Toggle "Predator-vision"
//--------------------------
case PCK_ALT_V:
{
DPLRenderer *dpl_renderer = l4_application->GetVideoRenderer();
if (dpl_renderer)
{
Check(dpl_renderer);
dpl_renderer->DPLTogglePVision();
}
break;
}
//--------------------------------------------------------------
// Toggle transparency dither pattern between random and static
//--------------------------------------------------------------
case PCK_ALT_R:
//-----------------------------
// Report position of eyepoint
//-----------------------------
case PCK_ALT_P:
DEBUG_STREAM << "Function net yet enabled.\n" << flush;
break;
//------------------------------
// Pass event on to Application
//------------------------------
default:
Application::KeyCommandMessageHandler(message);
break;
}
}
//
//#############################################################################
// LightsOutMessageHandler
//#############################################################################
//
#if DEBUG_LEVEL == 0
void
L4Application::LightsOutMessageHandler(Receiver::Message */*message*/)
{
#else
void
L4Application::LightsOutMessageHandler(Receiver::Message *message)
{
Check(this);
Verify(message->messageID == LightsOutMessageID);
#endif
//
//--------------------------------------------------------------------------
// Turn off illumination
//--------------------------------------------------------------------------
//
PilotIllumination(False);
}
//
//#############################################################################
// TeslaCoil
//#############################################################################
//
void
L4Application::TeslaCoil(Logical light_on)
{
Check(controlsManager);
if (light_on)
{
((LBE4ControlsManager *) controlsManager)->SetLamp(
LBE4ControlsManager::LampTesla1,
RIO::solid + RIO::state1Bright + RIO::state2Bright
);
((LBE4ControlsManager *) controlsManager)->SetLamp(
LBE4ControlsManager::LampTesla2,
RIO::solid + RIO::state1Bright + RIO::state2Bright
);
((LBE4ControlsManager *) controlsManager)->SetLamp(
LBE4ControlsManager::LampTesla3,
RIO::solid + RIO::state1Bright + RIO::state2Bright
);
}
else
{
((LBE4ControlsManager *) controlsManager)->SetLamp(LBE4ControlsManager::LampTesla1, RIO::solid + RIO::state1Off + RIO::state2Off);
((LBE4ControlsManager *) controlsManager)->SetLamp(LBE4ControlsManager::LampTesla2, RIO::solid + RIO::state1Off + RIO::state2Off);
((LBE4ControlsManager *) controlsManager)->SetLamp(LBE4ControlsManager::LampTesla3, RIO::solid + RIO::state1Off + RIO::state2Off);
}
}
//
//#############################################################################
// PilotIllumination
//#############################################################################
//
void
L4Application::PilotIllumination(Logical light_on)
{
Check(controlsManager);
TeslaCoil(light_on);
if (light_on)
{
//--------------------------------------------
// Turn on floor light
//--------------------------------------------
((LBE4ControlsManager *) controlsManager)->SetLamp(
LBE4ControlsManager::LampFloor,
RIO::solid + RIO::state1Bright + RIO::state2Bright
);
//--------------------------------------------
// Ramp all aux screens to white
//--------------------------------------------
L4GaugeRenderer *gauge_renderer = GetGaugeRenderer();
if (gauge_renderer != NULL)
{
Check(gauge_renderer);
gauge_renderer->FadeToWhite(1.5);
}
}
else
{
//--------------------------------------------
// Turn off floor light
//--------------------------------------------
((LBE4ControlsManager *) controlsManager)->SetLamp(
LBE4ControlsManager::LampFloor,
RIO::solid + RIO::state1Off + RIO::state2Off
);
//--------------------------------------------
// Ramp all aux screens to normal
//--------------------------------------------
L4GaugeRenderer *gauge_renderer = GetGaugeRenderer();
if (gauge_renderer != NULL)
{
Check(gauge_renderer);
gauge_renderer->FadeToNormal(1.5);
}
}
}
#ifdef TEST_CLASS
# include "l4app.tcp"
#endif