Clean, self-contained extraction of the BattleTech-specific work from the
reverse-engineering workspace -- engine + game + content + build, with nothing
from Red Planet or the raw archive dumps. Builds green (Win32) and runs the
single-player drive->animate->target->fire->damage->destroy loop out of the box.
Layout:
engine/ MUNGA + MUNGA_L4 shared 2007 engine, carrying our BT render/loader
work (bgfload/L4D3D/L4VIDEO: BSL bit-slice decode, LOD/ground/shadow
models) + image codec; the minimal rp/ headers the audio HAL needs
game/ reconstructed BT logic + surviving-original BT source + fwd shims
+ WinMain launcher
content/ full runtime tree (BTL4.RES, VIDEO/, GAUGE/, AUDIO/, eggs, BTDPL.INI)
docs/ format specs + reconstruction ledgers
reference/ raw Ghidra pseudocode (recon source-of-truth) + decomp exporter
tools/ MP console emulator + map/resource scanners
One top-level CMake builds munga_engine lib + bt410_l4 game lib + btl4.exe.
All paths relativized (186 fwd shims + ~437 CMake abs paths -> repo-relative);
DXSDK is the one external, overridable via -DDXSDK. Verified: builds to a
byte-identical 2.27MB exe and runs combat (TARGET DESTROYED, 0 crashes) against
the bundled content.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
846 lines
16 KiB
C++
846 lines
16 KiB
C++
#include "munga.h"
|
|
#pragma hdrstop
|
|
|
|
#include "tree.h"
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~ TreeNode ~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
//
|
|
//###########################################################################
|
|
// TreeNode
|
|
//###########################################################################
|
|
//
|
|
TreeNode::TreeNode(
|
|
Tree *tree,
|
|
Plug *plug
|
|
):
|
|
Link(tree, plug)
|
|
{
|
|
less = NULL;
|
|
greater = NULL;
|
|
parent = NULL;
|
|
}
|
|
|
|
//
|
|
//###########################################################################
|
|
// ~TreeNode
|
|
//###########################################################################
|
|
//
|
|
TreeNode::~TreeNode()
|
|
{
|
|
Tree *tree = Cast_Object(Tree*, socket);
|
|
|
|
Check(this);
|
|
Check(tree);
|
|
|
|
//
|
|
//-------------------------------------------
|
|
// Notify iterators that deletion is occuring
|
|
//-------------------------------------------
|
|
//
|
|
tree->SendIteratorMemo(PlugRemoved, this);
|
|
|
|
//
|
|
//-----------------------------------
|
|
// Tell the tree to release this node
|
|
//-----------------------------------
|
|
//
|
|
tree->SeverFromTreeNode(this);
|
|
|
|
//
|
|
//------------------------------------------
|
|
// Remove this link from any plug references
|
|
//------------------------------------------
|
|
//
|
|
ReleaseFromPlug();
|
|
|
|
//
|
|
//-------------------------------------------------------------
|
|
// Tell the node to release this link. Note that this link
|
|
// is not referenced by the plug or the chain at this point in
|
|
// time.
|
|
//-------------------------------------------------------------
|
|
//
|
|
if (tree->GetReleaseNode() != NULL)
|
|
{
|
|
Check(tree->GetReleaseNode());
|
|
tree->GetReleaseNode()->ReleaseLinkHandler(tree, plug);
|
|
}
|
|
}
|
|
|
|
//
|
|
//###########################################################################
|
|
// TestInstance
|
|
//###########################################################################
|
|
//
|
|
Logical
|
|
TreeNode::TestInstance() const
|
|
{
|
|
Link::TestInstance();
|
|
|
|
if (less != NULL)
|
|
{
|
|
Check_Signature(less);
|
|
}
|
|
if (greater != NULL)
|
|
{
|
|
Check_Signature(greater);
|
|
}
|
|
if (parent != NULL)
|
|
{
|
|
Check_Signature(parent);
|
|
}
|
|
return True;
|
|
}
|
|
|
|
//
|
|
//###########################################################################
|
|
// SetupTreeLinks
|
|
//###########################################################################
|
|
//
|
|
void
|
|
TreeNode::SetupTreeLinks(
|
|
TreeNode *less,
|
|
TreeNode *greater,
|
|
TreeNode *parent
|
|
)
|
|
{
|
|
this->less = less;
|
|
this->greater = greater;
|
|
this->parent = parent;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~ Tree ~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
//
|
|
//###########################################################################
|
|
// Tree
|
|
//###########################################################################
|
|
//
|
|
Tree::Tree(
|
|
Node *node,
|
|
Logical has_unique_entries
|
|
):
|
|
SortedSocket(node, has_unique_entries)
|
|
{
|
|
root = NULL;
|
|
}
|
|
|
|
//
|
|
//###########################################################################
|
|
// ~Tree
|
|
//###########################################################################
|
|
//
|
|
Tree::~Tree()
|
|
{
|
|
SetReleaseNode(NULL);
|
|
while (root != NULL)
|
|
{
|
|
Unregister_Object(root);
|
|
delete root;
|
|
}
|
|
}
|
|
|
|
//
|
|
//###########################################################################
|
|
// TestInstance
|
|
//###########################################################################
|
|
//
|
|
Logical
|
|
Tree::TestInstance() const
|
|
{
|
|
SortedSocket::TestInstance();
|
|
|
|
//
|
|
// Check root if not null
|
|
//
|
|
if (root != NULL)
|
|
{
|
|
Check(root);
|
|
}
|
|
return True;
|
|
}
|
|
|
|
//
|
|
//###########################################################################
|
|
// AddImplementation
|
|
//###########################################################################
|
|
//
|
|
void
|
|
Tree::AddImplementation(
|
|
Plug *plug
|
|
)
|
|
{
|
|
AddValueImplementation(plug, NULL);
|
|
}
|
|
|
|
//
|
|
//###########################################################################
|
|
// AddValueImplementation
|
|
//###########################################################################
|
|
//
|
|
void
|
|
Tree::AddValueImplementation(
|
|
Plug *plug,
|
|
const void *value
|
|
)
|
|
{
|
|
Check(this);
|
|
Check(plug);
|
|
|
|
/*
|
|
* Verify that value has not been added
|
|
*/
|
|
Verify(HasUniqueEntries() ? SearchForValue(value) == NULL : (Logical)True);
|
|
|
|
/*
|
|
* Make new tree node
|
|
*/
|
|
TreeNode *node;
|
|
|
|
node = MakeTreeNode(plug, value);
|
|
Register_Object(node);
|
|
|
|
/*
|
|
* Add to the tree and send iterators memo to
|
|
* update pointers
|
|
*/
|
|
AddTreeNode(node);
|
|
SendIteratorMemo(PlugAdded, node);
|
|
}
|
|
|
|
//
|
|
//###########################################################################
|
|
// FindImplementation
|
|
//###########################################################################
|
|
//
|
|
Plug*
|
|
Tree::FindImplementation(
|
|
const void *value
|
|
)
|
|
{
|
|
TreeNode *node;
|
|
|
|
if ((node = SearchForValue(value)) != NULL)
|
|
{
|
|
Check(node);
|
|
return node->GetPlug();
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
//###########################################################################
|
|
// MakeTreeNode
|
|
//###########################################################################
|
|
//
|
|
TreeNode*
|
|
Tree::MakeTreeNode(
|
|
Plug*,
|
|
const void*
|
|
)
|
|
{
|
|
Fail("Tree::MakeTreeNode - Shoule never reach here");
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
//###########################################################################
|
|
// CompareTreeNodes
|
|
//###########################################################################
|
|
//
|
|
int
|
|
Tree::CompareTreeNodes(
|
|
TreeNode*,
|
|
TreeNode*
|
|
)
|
|
{
|
|
Fail("Tree::CompareTreeNodes - Shoule never reach here");
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
//###########################################################################
|
|
// CompareValueToTreeNode
|
|
//###########################################################################
|
|
//
|
|
int
|
|
Tree::CompareValueToTreeNode(
|
|
const void*,
|
|
TreeNode*
|
|
)
|
|
{
|
|
Fail("Tree::CompareValueToTreeNode - Shoule never reach here");
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
//###########################################################################
|
|
// AddTreeNode
|
|
//###########################################################################
|
|
//
|
|
void
|
|
Tree::AddTreeNode(
|
|
TreeNode *newNode
|
|
)
|
|
{
|
|
Check(this);
|
|
Check(newNode);
|
|
|
|
/*
|
|
* If root is NULL this is the first item
|
|
*/
|
|
if (root == NULL)
|
|
{
|
|
newNode->SetupTreeLinks(NULL, NULL, NULL);
|
|
root = newNode;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Search for insertion point
|
|
*/
|
|
TreeNode *node;
|
|
|
|
node = root;
|
|
while (node != NULL)
|
|
{
|
|
Check(node);
|
|
|
|
if (CompareTreeNodes(newNode, node) < 0)
|
|
{
|
|
if (node->less == NULL)
|
|
{
|
|
newNode->SetupTreeLinks(NULL, NULL, node);
|
|
node->less = newNode;
|
|
break;
|
|
}
|
|
else
|
|
node = node->less;
|
|
}
|
|
else
|
|
{
|
|
if (node->greater == NULL)
|
|
{
|
|
newNode->SetupTreeLinks(NULL, NULL, node);
|
|
node->greater = newNode;
|
|
break;
|
|
}
|
|
else
|
|
node = node->greater;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
//###########################################################################
|
|
// SeverFromTreeNode
|
|
//###########################################################################
|
|
//
|
|
void
|
|
Tree::SeverFromTreeNode(
|
|
TreeNode *node
|
|
)
|
|
{
|
|
Check(this);
|
|
Check(node);
|
|
|
|
if (node->greater == NULL)
|
|
{
|
|
if (node->less == NULL)
|
|
{
|
|
//
|
|
//--------------------------------------------------------------------
|
|
// The node has no subtrees
|
|
//--------------------------------------------------------------------
|
|
//
|
|
if (node == root)
|
|
{
|
|
//
|
|
// Tree is now empty, set root to null
|
|
//
|
|
root = NULL;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Set appropiate branch to NULL
|
|
//
|
|
Check(node->parent);
|
|
if (node->parent->less == node)
|
|
node->parent->less = NULL;
|
|
else
|
|
node->parent->greater = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
//--------------------------------------------------------------------
|
|
// The node has a less subtree
|
|
//--------------------------------------------------------------------
|
|
//
|
|
Check(node->less);
|
|
|
|
if (node == root)
|
|
{
|
|
//
|
|
// Node is root... Set subtree to new root
|
|
//
|
|
root = node->less;
|
|
node->less->parent = NULL;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Node is not root
|
|
// Set parents pointer to subtree
|
|
//
|
|
Check( node->parent );
|
|
if (node->parent->less == node)
|
|
{
|
|
node->parent->less = node->less;
|
|
}
|
|
else
|
|
{
|
|
node->parent->greater = node->less;
|
|
}
|
|
//
|
|
// Set subtree parent to node parent
|
|
//
|
|
node->less->parent = node->parent;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (node->less == NULL)
|
|
{
|
|
//
|
|
//--------------------------------------------------------------------
|
|
// The node has a greater subtree
|
|
//--------------------------------------------------------------------
|
|
//
|
|
Check( node->greater );
|
|
if (node == root)
|
|
{
|
|
//
|
|
// Node is root
|
|
// Set subtree to new root
|
|
//
|
|
root = node->greater;
|
|
//
|
|
// Set new root to have null parent
|
|
//
|
|
node->greater->parent = NULL;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Node is not root
|
|
// Set parents pointer to subtree
|
|
//
|
|
Check( node->parent );
|
|
if (node->parent->less == node)
|
|
node->parent->less = node->greater;
|
|
else
|
|
node->parent->greater = node->greater;
|
|
//
|
|
// Set subtree parent to node parent
|
|
//
|
|
node->greater->parent = node->parent;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
//--------------------------------------------------------------------
|
|
// The node has lesser and greater sub-trees
|
|
//--------------------------------------------------------------------
|
|
//
|
|
TreeNode *successor;
|
|
|
|
Check(node->less);
|
|
Check(node->greater);
|
|
|
|
successor = node->greater;
|
|
while (successor->less != NULL)
|
|
{
|
|
successor = successor->less;
|
|
Check(successor);
|
|
}
|
|
|
|
//
|
|
// Set successor's parent to subtree
|
|
//
|
|
Check(successor->parent);
|
|
if (successor->parent->less == successor)
|
|
successor->parent->less = successor->greater;
|
|
else
|
|
successor->parent->greater = successor->greater;
|
|
|
|
//
|
|
// Set successor's subtree to parent
|
|
//
|
|
if (successor->greater != NULL)
|
|
{
|
|
Check(successor->greater);
|
|
successor->greater->parent = successor->parent;
|
|
}
|
|
|
|
//
|
|
// Place at node
|
|
//
|
|
successor->parent = node->parent;
|
|
successor->less = node->less;
|
|
successor->greater = node->greater;
|
|
|
|
if (root == node)
|
|
{
|
|
//
|
|
// Node was root
|
|
//
|
|
root = successor;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Set nodes parent to successor
|
|
//
|
|
Check(successor->parent);
|
|
if (successor->parent->less == node)
|
|
successor->parent->less = successor;
|
|
else
|
|
successor->parent->greater = successor;
|
|
}
|
|
|
|
//
|
|
// Set subtrees parent to successor
|
|
//
|
|
if (successor->greater != NULL)
|
|
{
|
|
Check(successor->greater);
|
|
successor->greater->parent = successor;
|
|
}
|
|
if (successor->less != NULL)
|
|
{
|
|
Check(successor->less);
|
|
successor->less->parent = successor;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
//###########################################################################
|
|
// SearchForValue
|
|
//###########################################################################
|
|
//
|
|
TreeNode*
|
|
Tree::SearchForValue(
|
|
const void *value
|
|
)
|
|
{
|
|
TreeNode *node;
|
|
int ret;
|
|
|
|
node = root;
|
|
while (node != NULL)
|
|
{
|
|
Check(node);
|
|
if ((ret = CompareValueToTreeNode(value, node)) == 0)
|
|
break;
|
|
node = (ret < 0) ? node->less : node->greater;
|
|
}
|
|
return node;
|
|
}
|
|
|
|
//
|
|
//###########################################################################
|
|
// TreeIterator
|
|
//###########################################################################
|
|
//
|
|
TreeIterator::TreeIterator(Tree *tree):
|
|
SortedIterator(tree)
|
|
{
|
|
First();
|
|
}
|
|
|
|
//
|
|
//###########################################################################
|
|
//###########################################################################
|
|
//
|
|
TreeIterator::~TreeIterator()
|
|
{
|
|
}
|
|
|
|
//
|
|
//###########################################################################
|
|
// TestInstance
|
|
//###########################################################################
|
|
//
|
|
Logical
|
|
TreeIterator::TestInstance() const
|
|
{
|
|
SortedIterator::TestInstance();
|
|
|
|
if (currentNode != NULL)
|
|
{
|
|
Check(currentNode);
|
|
}
|
|
return True;
|
|
}
|
|
|
|
//
|
|
//###########################################################################
|
|
// First
|
|
//###########################################################################
|
|
//
|
|
void
|
|
TreeIterator::First()
|
|
{
|
|
TreeNode *node;
|
|
|
|
node = Cast_Object(Tree*, socket)->root;
|
|
if (node != NULL)
|
|
{
|
|
Check(node);
|
|
while (node->less != NULL)
|
|
{
|
|
node = node->less;
|
|
Check(node);
|
|
}
|
|
}
|
|
currentNode = node;
|
|
}
|
|
|
|
//
|
|
//###########################################################################
|
|
// Last
|
|
//###########################################################################
|
|
//
|
|
void
|
|
TreeIterator::Last()
|
|
{
|
|
//
|
|
// Should never reach here
|
|
//
|
|
#ifdef __BCPLUSPLUS__
|
|
#pragma warn -ccc
|
|
Verify(False);
|
|
#pragma warn +ccc
|
|
#endif
|
|
}
|
|
|
|
//
|
|
//###########################################################################
|
|
// Next
|
|
//###########################################################################
|
|
//
|
|
void
|
|
TreeIterator::Next()
|
|
{
|
|
TreeNode *node;
|
|
|
|
if ((node = currentNode) == NULL)
|
|
return;
|
|
|
|
Check(node);
|
|
if (node->greater != NULL)
|
|
{
|
|
node = node->greater;
|
|
Check(node);
|
|
while (node->less != NULL)
|
|
{
|
|
node = node->less;
|
|
Check(node);
|
|
}
|
|
currentNode = node;
|
|
return;
|
|
}
|
|
|
|
currentNode = NULL;
|
|
while (node->parent != NULL)
|
|
{
|
|
Check(node->parent);
|
|
if (node == node->parent->less)
|
|
{
|
|
currentNode = node->parent;
|
|
return;
|
|
}
|
|
node = node->parent;
|
|
Check(node);
|
|
}
|
|
}
|
|
|
|
//
|
|
//###########################################################################
|
|
// Previous
|
|
//###########################################################################
|
|
//
|
|
void
|
|
TreeIterator::Previous()
|
|
{
|
|
//
|
|
// Should never reach here
|
|
//
|
|
#ifdef __BCPLUSPLUS__
|
|
#pragma warn -ccc
|
|
Verify(False);
|
|
#pragma warn +ccc
|
|
#endif
|
|
}
|
|
|
|
#if 0
|
|
//
|
|
//###########################################################################
|
|
// ReadAndNextImplementation
|
|
//###########################################################################
|
|
//
|
|
void
|
|
*TreeIterator::ReadAndNextImplementation()
|
|
{
|
|
void *plug;
|
|
|
|
if ((plug = GetCurrentImplementation()) != NULL)
|
|
{
|
|
Next();
|
|
}
|
|
return plug;
|
|
}
|
|
#endif
|
|
|
|
#if 0
|
|
//
|
|
//###########################################################################
|
|
// ReadAndPreviousImplementation
|
|
//###########################################################################
|
|
//
|
|
void
|
|
*TreeIterator::ReadAndPreviousImplementation()
|
|
{
|
|
#ifdef __BCPLUSPLUS__
|
|
#pragma warn -ccc
|
|
Verify(False);
|
|
#pragma warn +ccc
|
|
#endif
|
|
return(NULL);
|
|
}
|
|
#endif
|
|
|
|
//
|
|
//###########################################################################
|
|
// GetCurrentImplementation
|
|
//###########################################################################
|
|
//
|
|
void
|
|
*TreeIterator::GetCurrentImplementation()
|
|
{
|
|
if (currentNode != NULL)
|
|
{
|
|
Check(currentNode);
|
|
return currentNode->GetPlug();
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
//###########################################################################
|
|
// GetSize
|
|
//###########################################################################
|
|
//
|
|
CollectionSize
|
|
TreeIterator::GetSize()
|
|
{
|
|
TreeIterator iterator(Cast_Object(Tree*, socket));
|
|
CollectionSize i = 0;
|
|
|
|
while (iterator.ReadAndNextImplementation() != NULL)
|
|
{
|
|
i++;
|
|
}
|
|
return(i);
|
|
}
|
|
|
|
#if 0
|
|
//
|
|
//###########################################################################
|
|
// GetNthImplementation
|
|
//###########################################################################
|
|
//
|
|
void
|
|
*TreeIterator::GetNthImplementation(
|
|
CollectionSize index
|
|
)
|
|
{
|
|
CollectionSize i = 0;
|
|
void *plug;
|
|
|
|
First();
|
|
while ((plug = GetCurrentImplementation()) != NULL)
|
|
{
|
|
if (i == index)
|
|
return plug;
|
|
Next();
|
|
i++;
|
|
}
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
//###########################################################################
|
|
// Remove
|
|
//###########################################################################
|
|
//
|
|
void
|
|
TreeIterator::Remove()
|
|
{
|
|
if (currentNode != NULL)
|
|
{
|
|
Unregister_Object(currentNode);
|
|
delete currentNode;
|
|
}
|
|
}
|
|
|
|
//
|
|
//###########################################################################
|
|
// FindImplementation
|
|
//###########################################################################
|
|
//
|
|
Plug*
|
|
TreeIterator::FindImplementation(
|
|
const void *value
|
|
)
|
|
{
|
|
TreeNode *node;
|
|
|
|
if ((node = Cast_Object(Tree*, socket)->SearchForValue(value)) != NULL)
|
|
{
|
|
Check(node);
|
|
return (currentNode = node)->GetPlug();
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
//###########################################################################
|
|
// ReceiveMemo
|
|
//###########################################################################
|
|
//
|
|
void
|
|
TreeIterator::ReceiveMemo(
|
|
IteratorMemo memo,
|
|
void *content
|
|
)
|
|
{
|
|
if (memo == PlugRemoved)
|
|
{
|
|
if (content == currentNode)
|
|
Next();
|
|
}
|
|
}
|
|
|
|
#if defined(TEST_CLASS)
|
|
# include "tree.tcp"
|
|
#endif
|