Files
TeslaRel410/restoration/source410/MUNGA/EXPTBL.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

626 lines
16 KiB
C++

# if !defined(MUNGA_HPP)
# include <munga.hpp>
# endif
#pragma hdrstop
# if !defined(EXPTBL_HPP)
# include <exptbl.hpp>
# endif
# if !defined(NAMELIST_HPP)
# include <namelist.hpp>
# endif
# if !defined(REGISTRY_HPP)
# include <registry.hpp>
# endif
# if !defined(APP_HPP)
# include <app.hpp>
# endif
# if !defined(EXPLODE_HPP)
# include <explode.hpp>
# endif
# if !defined(DAMAGE_HPP)
# include <damage.hpp>
# endif
# if !defined(NOTATION_HPP)
# include <notation.hpp>
# endif
# if !defined(FILEUTIL_HPP)
# include <fileutil.hpp>
# endif
# if !defined(RENDERER_HPP)
# include <renderer.hpp>
# endif
//##############################################################################
//###################### Class ExplosionTableEntry ########################
//##############################################################################
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
ExplosionTableEntry::CreateExplosion(
const EntityID &entity_hit_ID,
const EntityID &creating_entity_ID,
const Point3D &explode_position
)
{
Check(this);
Origin explode_origin(Origin::Identity);
explode_origin = explode_position;
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Create the explosion MakeMessage
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Explosion::MakeMessage exp_message(
Explosion::MakeMessageID,
sizeof(Explosion::MakeMessage),
RegisteredClass::ExplosionClassID,
EntityID::Null,
explosionResourceID,
Explosion::DefaultFlags,
explode_origin,
entity_hit_ID,
creating_entity_ID
);
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Calculate actual time based on time_delay
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Time when = Now();
when += timeDelay;
Check(application);
Registry *registry;
registry = application->GetRegistry();
Check_Pointer(registry);
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Create a MakeEntityMessage
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Registry::MakeEntityMessage *registry_message;
registry_message = Registry::MakeEntityMessage::Make(&exp_message);
Register_Object(registry_message);
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Post entity message to the registry
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
#if 0 // ECH 1/28/96
application->Post(
DefaultEventPriority,
registry,
registry_message,
when
);
#else
application->Post(
CreationEventPriority,
registry,
registry_message,
when
);
#endif
Unregister_Object(registry_message);
delete registry_message;
Check_Fpu();
}
//##############################################################################
// Construction/Destruction
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ExplosionTableEntry::ExplosionTableEntry(MemoryStream *exp_stream)
{
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Read in the data from the stream
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
*exp_stream >> damageLevel >> explosionResourceID >> graphicState;
*exp_stream >> timeDelay;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ExplosionTableEntry::~ExplosionTableEntry()
{
}
//##############################################################################
// Tool Support
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
ExplosionTableEntry::CreateStreamedExplosion(
ResourceFile *resource_file,
CString exp_file_name,
NotationFile *exp_file,
CString exp_page_name,
MemoryStream *explosion_stream
)
{
Check(exp_file);
//
//~~~~~~~~~~~~~~~~~~~~
// Get the DamageLevel
//~~~~~~~~~~~~~~~~~~~~
//
Scalar damage_level;
if (!exp_file->GetEntry(
exp_page_name,
"DamageLevel",
&damage_level
)
)
{
damage_level = -1.0f;
}
*explosion_stream << damage_level;
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Get the Explosion Model File
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
const char *explosion_model_file;
if (!exp_file->GetEntry(
exp_page_name,
"ExplosionModelFile",
&explosion_model_file
)
)
{
DEBUG_STREAM <<exp_file_name<<" : "<<exp_page_name<<" missing ExplosionModelFile!"<<endl << flush;
return False;
}
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Hunt for Model file in ResourceFile
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ResourceDescription *res;
res = resource_file->FindResourceDescription(
explosion_model_file,
ResourceDescription::ModelListResourceType
);
if (!res)
{
cout << exp_file_name << " cannot find " << explosion_model_file
<< " in resource file!\n";
return False;
}
//
//~~~~~~~~~~~~~~~~~~~~~~~~~
// Write out the resourceID
//~~~~~~~~~~~~~~~~~~~~~~~~~
//
*explosion_stream << res->resourceID;
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~
// Read in the Graphic State
//~~~~~~~~~~~~~~~~~~~~~~~~~~
//
const char *graphic_state_char;
Enumeration graphic_state;
if (exp_file->GetEntry(
exp_page_name,
"GraphicState",
&graphic_state_char
)
)
{
if (!strcmp(graphic_state_char, "Destroyed"))
{
graphic_state = DamageZone::DestroyedGraphicState;
}
else if (!strcmp(graphic_state_char, "Gone"))
{
graphic_state = DamageZone::GoneGraphicState;
}
}
else
{
graphic_state = DamageZone::ExistsGraphicState;
}
*explosion_stream << graphic_state;
//
//~~~~~~~~~~~~~~~~~~~~
// Get the TimeDelay
//~~~~~~~~~~~~~~~~~~~~
//
Scalar time_delay;
if (!exp_file->GetEntry(
exp_page_name,
"TimeDelay",
&time_delay
)
)
{
time_delay = 0.0f;
}
*explosion_stream << time_delay;
return True;
}
//##############################################################################
//######################## Class ExplosionTable ###########################
//##############################################################################
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
ExplosionTable::CrossedDamageLevelThreshold(
Scalar old_value,
Scalar new_value
)
{
ExplosionTableIterator iterator(this);
ExplosionTableEntry *current_exp;
while ((current_exp = iterator.ReadAndNext()) != NULL)
{
if (
(old_value < current_exp->GetDamageLevel()) &&
(new_value >= current_exp->GetDamageLevel())
)
{
return True;
}
}
return False;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ExplosionTableEntry*
ExplosionTable::SelectDamageLevelEntry(Scalar damage_level)
{
ExplosionTableIterator iterator(this);
ExplosionTableEntry *current_exp;
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Choose ExplosionTableEntry by damage Level
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
while ((current_exp = iterator.ReadAndNext()) != NULL)
{
if (damage_level < current_exp->GetDamageLevel())
{
Check_Fpu();
return current_exp;
}
}
Check_Fpu();
return NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ExplosionTableEntry*
ExplosionTable::SelectGraphicStateEntry(Enumeration graphic_state)
{
ExplosionTableIterator iterator(this);
ExplosionTableEntry *current_entry;
while((current_entry = iterator.ReadAndNext() ) != NULL)
{
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Display Explosion if an Entry Exists
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
if (current_entry->GetGraphicState() == graphic_state)
{
Check_Fpu();
return current_entry;
}
}
Check_Fpu();
return NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
ExplosionTable::CreateExplosion(
ExplosionTableEntry *explosion_entry,
const EntityID &entity_hit_ID,
const EntityID &creating_entity_ID,
Point3D explode_position
)
{
Check(this);
Check(explosion_entry);
explosion_entry->CreateExplosion(
entity_hit_ID,
creating_entity_ID,
explode_position
);
}
//##############################################################################
// Construction/Destruction
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ExplosionTable::ExplosionTable(MemoryStream *exp_stream):
TableOf<ExplosionTableEntry*, int>(NULL, True)
{
int entry_count;
*exp_stream >> entry_count;
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Create all of the ExplosionTableEntries
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ExplosionTableEntry *exp_entry;
for(int ii=0;ii<entry_count;++ii)
{
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Create a new ExplosionTableEntry
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
exp_entry = new ExplosionTableEntry(exp_stream);
Register_Object(exp_entry);
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Add the entry the the ExplosionTable
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
AddValue(exp_entry, ii);
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ExplosionTable::~ExplosionTable()
{
ExplosionTableIterator iterator(this);
iterator.DeletePlugs();
}
//##############################################################################
// Tool Support
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
ExplosionTable::CreateStreamedExplosionTable(
ResourceFile *resource_file,
CString model_name,
NotationFile *exp_file,
CString exp_filename,
MemoryStream *explosion_stream
)
{
Check(resource_file);
Check(exp_file);
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Make a namelist of all Explosion Entries
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
NameList *exp_namelist = exp_file->MakePageList();
Register_Object(exp_namelist);
if (!exp_namelist->EntryCount())
{
DEBUG_STREAM << exp_file << " is empty or missing \n"<<endl << flush;
Unregister_Object(exp_namelist);
delete exp_namelist;
return False;
}
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Write the Number of Explosion Entries for this Entity
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int entry_count = exp_namelist->EntryCount();
*explosion_stream << entry_count;
NameList::Entry *exp_entry = exp_namelist->GetFirstEntry();
while(exp_entry)
{
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// While more pages Add ExplosionEntries to the stream
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ExplosionTableEntry::CreateStreamedExplosion(
resource_file,
exp_filename,
exp_file,
exp_entry->GetName(),
explosion_stream
);
exp_entry = exp_entry->GetNextEntry();
}
Unregister_Object(exp_namelist);
delete exp_namelist;
return True;
}
//##########################################################################
//###################### EntityEffectWatcher #########################
//##########################################################################
//##############################################################################
// Construction/Destruction
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
EntityEffectWatcher::EntityEffectWatcher(
Entity *entity_watched
)
{
entityWatched = entity_watched;
entityWatched->AddEffectWatcher(this);
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Init array to hold previous damageLevel values
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Verify(entityWatched->damageZoneCount > 0);
oldDamageLevel = new Scalar[entityWatched->damageZoneCount];
Register_Pointer(oldDamageLevel);
for(int ii=0;ii<entityWatched->damageZoneCount;++ii)
{
oldDamageLevel[ii] = 0.0f;
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
EntityEffectWatcher::~EntityEffectWatcher()
{
Unregister_Pointer(oldDamageLevel);
delete[] oldDamageLevel;
}
//##############################################################################
// Execute
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
EntityEffectWatcher::Execute()
{
Check(this);
Check(entityWatched);
Check_Pointer(entityWatched->damageZones);
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Check the flags to see if any damageZones
// have been modified since the last Execute was called
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
DamageZone *current_zone;
ExplosionTable *exp_table;
ExplosionTableEntry *exp_entry(NULL);
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Traverse damageZones looking for Changes
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
for (int ii=0;ii<entityWatched->damageZoneCount; ++ii)
{
current_zone = entityWatched->damageZones[ii];
Check(current_zone);
ResourceDescription::ResourceID exp_res;
exp_table = current_zone->GetExplosionTable();
Check(exp_table);
//
//~~~~~~~~~~~~~~~~~~~
// Check Damage Level
//~~~~~~~~~~~~~~~~~~~
//
if (current_zone->DamageLevelChanged())
{
exp_entry = exp_table->SelectDamageLevelEntry(current_zone->damageLevel);
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// If delta was large enough signal an update
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
if (
exp_table->CrossedDamageLevelThreshold(
oldDamageLevel[ii],
current_zone->damageLevel
) &&
(entityWatched->GetInstance() == Entity::MasterInstance)
)
{
entityWatched->ForceUpdate(Entity::DamageZoneUpdateModelFlag);
}
oldDamageLevel[ii] = current_zone->damageLevel;
}
//
//~~~~~~~~~~~~~~~~~~~~
// Check Graphic State
//~~~~~~~~~~~~~~~~~~~~
//
if (current_zone->GraphicStateChanged())
{
exp_entry = exp_table->SelectGraphicStateEntry(
current_zone->GetGraphicState()
);
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Always update for a graphic state change
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
if (entityWatched->GetInstance() == Entity::MasterInstance)
{
entityWatched->ForceUpdate(Entity::DamageZoneUpdateModelFlag);
}
}
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Notify RendererManager of Any Change
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
if (exp_entry)
{
Check(exp_entry);
exp_res = exp_entry->GetExplosionResourceID();
Check(application);
Check(application->GetRendererManager());
application->GetRendererManager()->StartEntityEffect(
entityWatched,
current_zone,
exp_res
);
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Reset the DamageZone::changedFlags if
// I am a master and no update occurred OR
// if I am a replicant
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
if (
(
(entityWatched->GetInstance() == Entity::MasterInstance) &&
!entityWatched->DamageZoneUpdateModelFlagSet()
) ||
(
(entityWatched->GetInstance() == Entity::ReplicantInstance)
)
)
{
current_zone->ResetChangedFlags();
}
}
exp_entry = NULL;
}
Check_Fpu();
}
//##############################################################################
// Test Support
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
EntityEffectWatcher::TestInstance() const
{
Check(entityWatched);
return True;
}