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>
587 lines
27 KiB
C++
587 lines
27 KiB
C++
//===========================================================================//
|
|
// File: mechrecon.hpp //
|
|
// Project: BattleTech Brick: Entity Manager //
|
|
// Contents: Reconstruction support shim for the "mech core" family //
|
|
// (mech / mech2..4 / mechsub / mechdmg / dmgtable). //
|
|
//---------------------------------------------------------------------------//
|
|
// This header is NOT part of the shipped game. It supplies the small set of //
|
|
// reconstruction helpers, decompiler-artifact globals and proxy stand-in //
|
|
// types that the recovered mech-core bodies reference but the modern engine //
|
|
// headers do not provide under those exact names. It exists only so the //
|
|
// reconstructed translation units COMPILE against the WinTesla engine; the //
|
|
// proxies preserve the call structure of the recovered code (no logic is //
|
|
// deleted). Behaviour-equivalent semantics are filled where trivial. //
|
|
// //
|
|
// MUST be included AFTER <bt.hpp> / the engine headers (so Scalar, Quaternion //
|
|
// Vector3D, AffineMatrix, CString, Subsystem ... are already declared). //
|
|
//===========================================================================//
|
|
|
|
#if !defined(MECHRECON_HPP)
|
|
# define MECHRECON_HPP
|
|
|
|
#include <string.h>
|
|
#include <stddef.h>
|
|
#include <stdlib.h>
|
|
#include <math.h>
|
|
// Quaternion / Vector3D / AffineMatrix used below (kIdentityQuat etc.). Include
|
|
// directly so mechrecon.hpp is self-sufficient regardless of the includer's order
|
|
// (myomers.cpp pulls this via mechsub.hpp BEFORE bt.hpp). ROTATION.hpp DEFINES
|
|
// Quaternion (affnmtrx.hpp only forward-declares it).
|
|
#include <ROTATION.hpp>
|
|
#include <affnmtrx.hpp>
|
|
|
|
//===========================================================================//
|
|
// Universal decompiler-artifact value.
|
|
//
|
|
// Stands in for the result of an unresolved FUN_/helper artifact. Converts
|
|
// to any scalar, pointer or default-constructible type so the recovered
|
|
// expression typechecks.
|
|
//===========================================================================//
|
|
struct Recon
|
|
{
|
|
Recon() {}
|
|
template<class T> Recon(const T &) {}
|
|
template<class T> operator T() const { return T(); }
|
|
};
|
|
|
|
//===========================================================================//
|
|
// Memory shim -- the decomp's "Memory::Allocate / Memory::Free" map onto the
|
|
// engine's per-object MemoryBlock allocators; for the offline reconstruction a
|
|
// plain heap allocation is behaviour-equivalent.
|
|
//===========================================================================//
|
|
namespace Memory
|
|
{
|
|
inline void *Allocate(size_t bytes) { return ::operator new(bytes); }
|
|
inline void *AllocateArray(size_t bytes){ return ::operator new(bytes); }
|
|
inline void Free(void *where) { ::operator delete(where); }
|
|
}
|
|
|
|
inline void *Alloc(size_t bytes) { return ::operator new(bytes); }
|
|
inline void *AllocateArray(size_t bytes){ return ::operator new(bytes); }
|
|
|
|
//===========================================================================//
|
|
// Ref-count / name helpers (decomp artifacts).
|
|
//===========================================================================//
|
|
template<class A> inline void *NewRefCount(A) { return 0; }
|
|
inline void *NewRefCounted() { return 0; }
|
|
template<class A,class B> inline void BindName(A, B) {}
|
|
template<class A,class B,class C> inline void BindName(A, B, C) {}
|
|
template<class A> inline void ReleaseRefCounted(A) {}
|
|
template<class A> inline void ZeroBlock(A, int) {}
|
|
template<class A,class B> inline void Assign(A, B) {}
|
|
template<class A> inline Recon NewTableEntry(A) { return Recon(); }
|
|
|
|
//===========================================================================//
|
|
// Small dynamic string used by the resource-table builders
|
|
// ( TextString("Layer") + itoa(n) ).
|
|
//===========================================================================//
|
|
struct TextString
|
|
{
|
|
char buf[64];
|
|
TextString() { buf[0] = 0; }
|
|
TextString(const char *s) { buf[0] = 0; if (s) { strncpy(buf, s, 63); buf[63] = 0; } }
|
|
TextString &operator+=(const char *s) { if (s) strncat(buf, s, 63 - strlen(buf)); return *this; }
|
|
TextString operator+ (const char *s) const { TextString t(*this); t += s; return t; }
|
|
operator const char *() const { return buf; }
|
|
};
|
|
|
|
//===========================================================================//
|
|
// Misc free-function artifacts (variadic -> Recon).
|
|
//===========================================================================//
|
|
template<class...A> inline Recon WorldTransform(A&&...) { return Recon(); }
|
|
template<class...A> inline Recon ResourceFind(A&&...) { return Recon(); }
|
|
template<class...A> inline Recon LoadSkeleton(A&&...) { return Recon(); }
|
|
template<class...A> inline int ResourceManagerHas(A&&...) { return 0; }
|
|
template<class...A> inline void GenerateFaultDefault(A&&...) {}
|
|
// NOTE: renamed from Random() to avoid colliding with the engine's
|
|
// `extern RandomGenerator Random;` (RANDOM.h). Reconstructed code that wants the
|
|
// original zero-arg unit-random (FUN_00408050 -> Scalar in [0,1)) uses RandomUnit().
|
|
// Backed by the engine RNG: `RandomGenerator::operator Scalar()` returns a value
|
|
// in [0,1) (RANDOM.h), exactly matching FUN_00408050's contract. Variadic tail
|
|
// kept so call sites (dmgtable: roll; theta*TwoPi) bind unchanged; Scalar return
|
|
// satisfies the dmgtable Scalar consumers.
|
|
#include <RANDOM.hpp> // fwd/ -> engine RANDOM.h: extern RandomGenerator Random;
|
|
template<class...A> inline Scalar RandomUnit(A&&...) { return (Scalar)Random; }
|
|
|
|
//===========================================================================//
|
|
// String helpers (decomp inlined strcpy/strcat/stricmp etc.).
|
|
//===========================================================================//
|
|
inline void Strcpy(char *d, const char *s) { while ((*d++ = *s++) != 0) {} }
|
|
inline void Strcat(char *d, const char *s) { while (*d) ++d; while ((*d++ = *s++) != 0) {} }
|
|
inline int Strcmp(const char *a, const char *b) { return strcmp(a, b); }
|
|
|
|
// Env gate with a DEFAULT: the authentic reconstructed paths (gait cutover/state
|
|
// machine, collision+gravity, real controls) are ON by default; set the var to
|
|
// "0" to fall back to the historical bring-up path (e.g. BT_GAIT_SM=0).
|
|
inline int BTEnvOn(const char *name, int on_by_default)
|
|
{
|
|
const char *v = getenv(name);
|
|
return v ? (v[0] != '0') : on_by_default;
|
|
}
|
|
|
|
// THE AUTHENTIC GROUND MODEL gate (task #15 -- the 1995 probe/snap/response
|
|
// ground block, ground-model-decode workflow). ONE function so the Mech ctor
|
|
// (template lift, mech.cpp), the per-frame ground block and the ProcessCollision
|
|
// override (mech4.cpp) can never disagree: every TU reads the same env exactly
|
|
// once. DEFAULT ON (verification passed: grass/arena/dbase census + anti-ladder
|
|
// soak + slope tracking + render-side screenshots + combat & heapcheck
|
|
// regressions -- see the task-#15 ground commit). BT_GROUND_REAL=0 falls back
|
|
// to the bring-up gravity+push-out+clamp baseline.
|
|
inline int GroundReal()
|
|
{
|
|
static const int v = BTEnvOn("BT_GROUND_REAL", 1);
|
|
return v;
|
|
}
|
|
inline int GroundLog()
|
|
{
|
|
static const int v = getenv("BT_GROUND_LOG") ? 1 : 0;
|
|
return v;
|
|
}
|
|
inline int Streq (const char *a, const char *b) { return strcmp(a, b) == 0; }
|
|
|
|
//===========================================================================//
|
|
// Multi-level status/alarm indicator.
|
|
//
|
|
// The recovered code names this AlarmIndicator and drives it with a level
|
|
// count ctor + SetLevel(level). In the WinTesla engine the equivalent is
|
|
// StateIndicator (SetState / GetState). We provide a thin proxy that exposes
|
|
// BOTH spellings so the recovered call sites compile unchanged.
|
|
//===========================================================================//
|
|
struct ReconAlarm
|
|
{
|
|
unsigned level;
|
|
ReconAlarm() : level(0) {}
|
|
ReconAlarm(int n) : level(0) { (void)n; }
|
|
ReconAlarm(unsigned n) : level(0) { (void)n; }
|
|
ReconAlarm &operator=(const ReconAlarm &) { return *this; }
|
|
void SetLevel(int n) { level = (unsigned)n; }
|
|
void SetState(unsigned n){ level = n; }
|
|
unsigned GetLevel() const { return level; }
|
|
unsigned GetState() const { return level; }
|
|
int GetLevelCount() const { return 0; }
|
|
};
|
|
|
|
//===========================================================================//
|
|
// Filtered (running-average) scalar -- decomp uses Initialize(n, value).
|
|
//===========================================================================//
|
|
struct ReconFiltered
|
|
{
|
|
ReconFiltered() {}
|
|
template<class...A> void Initialize(A&&...) {}
|
|
template<class...A> void Add(A&&...) {}
|
|
Scalar Value() const { return Scalar(0); }
|
|
// static-style accumulator verbs: FilteredScalar::Push(filter, sample) etc.
|
|
template<class...A> static void Push(A&&...) {}
|
|
template<class...A> static Scalar Sum(A&&...) { return Scalar(0); }
|
|
template<class...A> static Scalar Average(A&&...) { return Scalar(0); }
|
|
};
|
|
|
|
typedef unsigned int uint;
|
|
|
|
//===========================================================================//
|
|
// Universal word-cell proxy + word accessor.
|
|
//
|
|
// The recovered mech-core bodies address the object as an int[] and assign
|
|
// raw words by index ( this[0xNN] = ... ). Pointer-subscripting `this`
|
|
// cannot be made an assignable scalar lvalue, so each `this[0xNN]` is
|
|
// rewritten to Wword(0xNN), which hands back a distinct, universally
|
|
// assignable/convertible cell (one per word index). This preserves the
|
|
// per-offset write structure of the decomp while compiling against the
|
|
// modern engine; the cell is behaviour-neutral storage for the offline
|
|
// reconstruction (it is NOT aliased onto the real member at that offset).
|
|
//===========================================================================//
|
|
struct BTVal
|
|
{
|
|
Scalar max, min, field8; // named sub-fields used by the decomp
|
|
|
|
BTVal() : max(0), min(0), field8(0) {}
|
|
template<class T> BTVal(const T &) : max(0), min(0), field8(0) {}
|
|
|
|
BTVal &operator=(const BTVal &) { return *this; } // real copy-assign
|
|
template<class T> BTVal &operator=(const T &) { return *this; }
|
|
template<class T> operator T() const { return T(); }
|
|
|
|
// member-call passthrough (this[]-proxy + pose-member verbs)
|
|
template<class...A> BTVal &Construct(A&&...) { return *this; }
|
|
template<class...A> BTVal &LinkTo(A&&...) { return *this; }
|
|
template<class...A> BTVal &SetIdentity(A&&...) { return *this; }
|
|
template<class...A> BTVal &Mul(A&&...) { return *this; }
|
|
template<class...A> BTVal &Copy(A&&...) { return *this; }
|
|
|
|
// arithmetic kept closed over BTVal (avoids operator T() ambiguity)
|
|
friend BTVal operator+(const BTVal &, const BTVal &) { return BTVal(); }
|
|
friend BTVal operator-(const BTVal &, const BTVal &) { return BTVal(); }
|
|
BTVal &operator+=(const BTVal &) { return *this; }
|
|
template<class T> BTVal &operator+=(const T &) { return *this; }
|
|
|
|
// comparisons (exact-match friends beat the templated operator T())
|
|
friend bool operator==(const BTVal &, const BTVal &) { return false; }
|
|
friend bool operator!=(const BTVal &, const BTVal &) { return false; }
|
|
friend bool operator==(const BTVal &, int) { return false; }
|
|
friend bool operator!=(const BTVal &, int) { return false; }
|
|
friend bool operator==(int, const BTVal &) { return false; }
|
|
friend bool operator!=(int, const BTVal &) { return false; }
|
|
};
|
|
|
|
inline BTVal &Wword(int i) { static BTVal bank[0x400]; return bank[i & 0x3ff]; }
|
|
|
|
static Vector3D ZeroVector; // neutral zero vector (decomp ZeroVector datum)
|
|
|
|
//===========================================================================//
|
|
// Affine-matrix proxy -- adds the SetRotation / FromRotation / FromQuaternion
|
|
// spellings the decomp uses (the engine AffineMatrix expresses these through
|
|
// operator= / dedicated helpers).
|
|
//===========================================================================//
|
|
struct ReconMatrix
|
|
{
|
|
AffineMatrix m;
|
|
ReconMatrix() {}
|
|
template<class T> ReconMatrix &operator=(const T &) { return *this; }
|
|
template<class...A> void FromRotation(A&&...) {}
|
|
// P3 GAIT CUTOVER: backed by the real engine AffineMatrix (was no-op stubs, so
|
|
// IntegrateMotion's world-step committed nothing). Identity == BuildIdentity;
|
|
// FromQuaternion/SetRotation == AffineMatrix::operator=(const Quaternion&) (the
|
|
// engine's build-rotation-from-quaternion). Signatures match every call site
|
|
// (mech4.cpp IntegrateMotion + Simulate).
|
|
static void SetRotation(ReconMatrix *dst, const Quaternion *q) { dst->m = *q; }
|
|
static void FromQuaternion(ReconMatrix *dst, const Quaternion *q) { dst->m = *q; }
|
|
static void Identity(ReconMatrix *dst) { dst->m.BuildIdentity(); }
|
|
operator AffineMatrix &() { return m; }
|
|
};
|
|
|
|
//===========================================================================//
|
|
// SequenceController -- the BT-specific keyframe animation player embedded in the
|
|
// Mech as legAnimation (@0x65c) / bodyAnimation (@0x6bc). RECONSTRUCTED from the
|
|
// binary (part_003.c: ctor @00427768, SelectSequence @004277a8, Advance @0042790c,
|
|
// Reset @004283b8). NOT in the RP411 engine. Plays a gait clip: walks keyframes,
|
|
// interpolates each joint's rotation, writes it through the engine Joint API
|
|
// (same as gyro/torso), and returns the root-translation distance advanced.
|
|
// Modelled as a plain struct (accessed only by name; no Plug base needed). The
|
|
// keyframeCount/keyframeTimes/keyframeData accessors are preserved for mech3's
|
|
// LoadLocomotionClips/MeasureClipStride; full spec in btbuild/P3_LOCOMOTION.md.
|
|
//===========================================================================//
|
|
class Mech; // owner (forward decl -- mechrecon.hpp precedes the Mech class)
|
|
|
|
struct SequenceController
|
|
{
|
|
// rootTranslation entry (binary 0xC/frame); ".stride" == the .z the gait reads.
|
|
struct Keyframe { Scalar x, y, stride; };
|
|
|
|
// --- clip data parsed by SelectSequence (keyframe accessors read by mech3) ---
|
|
int keyframeCount; // frameCount (binary +0x14)
|
|
int jointCount; // animated joints (binary +0x18)
|
|
int *jointIndices; // keyframe->skeleton map (binary +0x1c)
|
|
Scalar *keyframeTimes; // frameTimes[frameCount] (binary +0x24)
|
|
void *keyframeBase; // keyframe pose data base (binary +0x28)
|
|
void *keyframeCursor; // current pose cursor (binary +0x2c)
|
|
Keyframe *keyframeData; // rootTranslations[] (binary +0x34)
|
|
|
|
// --- runtime state / links ---
|
|
int currentFrame; // (binary +0x3c)
|
|
Scalar currentTime; // clip playback time (binary +0x54)
|
|
void *jointSubsystem; // resolved owner joint subsystem (binary +0x38)
|
|
Mech *owner; // (binary +0x40)
|
|
void *clipResource; // locked clip resource handle (binary +0x44)
|
|
void *cbCode; // finished-callback code ptr (binary +0x48)
|
|
unsigned cbArg2, cbArg3; // finished-callback context words (binary +0x4c/0x50)
|
|
|
|
SequenceController()
|
|
: keyframeCount(0), jointCount(0), jointIndices(0), keyframeTimes(0),
|
|
keyframeBase(0), keyframeCursor(0), keyframeData(0), currentFrame(0),
|
|
currentTime(0.0f), jointSubsystem(0), owner(0), clipResource(0),
|
|
cbCode(0), cbArg2(0), cbArg3(0) {}
|
|
|
|
void Init(Mech *owner_mech); // @00427768 (ctor logic)
|
|
void SelectSequence(int clip_id, void *cb, unsigned a2, unsigned a3); // @004277a8
|
|
Scalar Advance(Scalar time_slice, int move_joints); // @0042790c
|
|
void Reset(int loop); // @004283b8
|
|
~SequenceController(); // @004278d4 (release clip)
|
|
};
|
|
typedef SequenceController ReconSeq;
|
|
|
|
//===========================================================================//
|
|
// Controls motion source -- the controls subsystem the Mech reads its live
|
|
// commanded ground speed from (this[0x128] -> +0x128).
|
|
//===========================================================================//
|
|
struct ReconMotionSource
|
|
{
|
|
Scalar commandedSpeed; // +0x128
|
|
};
|
|
|
|
// Gait-state name table ( <AnimationNames> + state*0x3c ); display data only.
|
|
static char AnimationNames[0x1e * 0x3c] = { 0 };
|
|
|
|
//===========================================================================//
|
|
// MechSubsystem-coupled damage zone view.
|
|
//
|
|
// The recovered MechSubsystem code addresses its DamageZone through a small
|
|
// set of fields (structure level, per-facing armour, structure reference and
|
|
// the zone's own alarm) that are reconstruction names rather than the modern
|
|
// engine DamageZone member spellings; this proxy carries them so the bodies
|
|
// compile. damageZone is stored through it.
|
|
//===========================================================================//
|
|
struct ReconDamageZone
|
|
{
|
|
Scalar structureLevel;
|
|
Scalar structureReference;
|
|
Scalar armour[5];
|
|
ReconAlarm alarm;
|
|
};
|
|
|
|
//===========================================================================//
|
|
// Subsystem proxy + critical-subsystem chain (DistributeCriticalHit support).
|
|
//===========================================================================//
|
|
struct ReconSub
|
|
{
|
|
template<class...A> Recon GetName(A&&...) { return Recon(); }
|
|
template<class...A> void TakeDamage(A&&...) {}
|
|
};
|
|
|
|
struct CriticalEntry
|
|
{
|
|
ReconSub *sub;
|
|
ReconSub *Subsystem() { return sub; }
|
|
};
|
|
|
|
struct CriticalChain
|
|
{
|
|
template<class A> CriticalChain(A) {}
|
|
int CountOfType(int) { return 1; }
|
|
CriticalEntry *First() { return 0; }
|
|
CriticalEntry *Next() { return 0; }
|
|
};
|
|
|
|
//===========================================================================//
|
|
// Callable returned by the "base vtable slot 0x18" thunk: (*BaseSlot0x18())(...)
|
|
//===========================================================================//
|
|
struct ReconCallable
|
|
{
|
|
template<class...A> Recon operator()(A&&...) { return Recon(); }
|
|
};
|
|
inline ReconCallable *BaseSlot0x18() { static ReconCallable c; return &c; }
|
|
|
|
//===========================================================================//
|
|
// Resource-table proxies (dmgtable).
|
|
//
|
|
// The recovered damage-table classes drive their entry pools through a small
|
|
// verb set (Init / Insert / Lookup / count) and a forward iterator
|
|
// (First / Next / Current) that differ from the modern TableOf/TableIterator
|
|
// API, so the table members are declared with these proxies instead.
|
|
//===========================================================================//
|
|
template<class T> struct ReconTable
|
|
{
|
|
int count;
|
|
ReconTable() : count(0) {}
|
|
void Init() { count = 0; }
|
|
template<class...A> void Insert(A&&...) {}
|
|
template<class...A> void *Lookup(A&&...) const { return 0; }
|
|
};
|
|
|
|
struct ReconTableIter
|
|
{
|
|
template<class A> ReconTableIter(const A &) {}
|
|
void *First() { return 0; }
|
|
void *Next() { return 0; }
|
|
void *Current() { return 0; }
|
|
};
|
|
|
|
//===========================================================================//
|
|
// Generic object stream + notation-line + skeleton proxies (dmgtable parsers).
|
|
//===========================================================================//
|
|
struct GenericObjectStream
|
|
{
|
|
template<class...A> void Read(A&&...) {}
|
|
template<class...A> void Write(A&&...) {}
|
|
};
|
|
|
|
struct NotationLine
|
|
{
|
|
NotationLine *Next() { return 0; }
|
|
const char *Format() { return ""; }
|
|
const char *Value() { return ""; }
|
|
int Count() { return 0; }
|
|
template<class...A> void Parse(A&&...) {}
|
|
};
|
|
|
|
struct Skeleton
|
|
{
|
|
int SegmentCount() { return 1; }
|
|
};
|
|
|
|
template<class...A> inline NotationLine *ReconGetPage(A&&...) { static NotationLine l; return &l; }
|
|
template<class...A> inline Recon ReconDir(A&&...) { return Recon(); }
|
|
template<class A,class B> inline int ReconSegIndex(A, B) { return 0; }
|
|
|
|
//===========================================================================//
|
|
// Vector math namespace artifact ( Vector::Add / AddScaled / Scale / Dot ... ).
|
|
// (The decomp emits free vector ops under a "Vector" qualifier; the engine uses
|
|
// Vector3D member ops. Behaviour-neutral stand-ins for the reconstruction.)
|
|
//===========================================================================//
|
|
namespace Vector
|
|
{
|
|
template<class...A> inline void Add(A&&...) {}
|
|
template<class...A> inline void AddScaled(A&&...) {}
|
|
template<class...A> inline void Scale(A&&...) {}
|
|
template<class...A> inline void Copy(A&&...) {}
|
|
template<class...A> inline void Subtract(A&&...) {}
|
|
template<class...A> inline Scalar Dot(A&&...) { return Scalar(0); }
|
|
template<class...A> inline Scalar Sum(A&&...) { return Scalar(0); }
|
|
}
|
|
|
|
//
|
|
// Quaternion free-function artifacts (the decomp writes these as
|
|
// Quaternion::Identity(dst,&id) / Quaternion::IntegrateInto(...) -- calling the
|
|
// engine's static const Identity datum as a function; routed to free shims).
|
|
//
|
|
// P3 GAIT CUTOVER: backed by the real engine Quaternion (were no-op stubs).
|
|
// ReconQuatIdentity(q, &id) -> q = Quaternion::Identity
|
|
// ReconQuatIntegrate(out,in,d) -> out = in integrated by angular delta d
|
|
// (engine Quaternion::Add(source, Vector3D) == FUN_00409f58)
|
|
// All callers pass (Quaternion*,Quaternion*) / (Quaternion*,Quaternion*,Vector3D*).
|
|
inline void ReconQuatIdentity(Quaternion *q, const Quaternion * /*id*/) { *q = Quaternion::Identity; }
|
|
inline void ReconQuatIntegrate(Quaternion *out, const Quaternion *in, const Vector3D *delta) { out->Add(*in, *delta); }
|
|
template<class...A> inline Recon ReconQuatSlerp(A&&...) { return Recon(); }
|
|
template<class...A> inline Recon ComputeImpactDamage(A&&...) { return Recon(); }
|
|
|
|
//
|
|
// Raw FUN_ helper artifacts referenced in code (vector/matrix/transform glue).
|
|
//
|
|
template<class...A> inline Recon FUN_00408644(A&&...) { return Recon(); }
|
|
// FUN_00408744 (part_000.c:8331) -- rotate a 3-vector by a matrix's 3x3 rotation:
|
|
// out[i] = v[0]*M(i,0) + v[1]*M(i,1) + v[2]*M(i,2) (row i . v).
|
|
// Backed by the engine AffineMatrix column basis: GetFromAxis(j) returns the "from"
|
|
// axis = column j = (M(0,j),M(1,j),M(2,j)), so out = v.x*colX + v.y*colY + v.z*colZ
|
|
// reproduces the raw row-dot formula EXACTLY (out[i] = sum_j v[j]*M(i,j)). This is
|
|
// IntegrateMotion's world-step: worldVelocity = bodyOrientation * localVelocity. The
|
|
// same GetFromAxis convention the drive facing uses (mech4.cpp), so it is sign-consistent.
|
|
inline Vector3D *FUN_00408744(Vector3D *out, const Scalar *v, ReconMatrix *m)
|
|
{
|
|
Vector3D colX, colY, colZ;
|
|
m->m.GetFromAxis(X_Axis, &colX);
|
|
m->m.GetFromAxis(Y_Axis, &colY);
|
|
m->m.GetFromAxis(Z_Axis, &colZ);
|
|
out->x = v[0]*colX.x + v[1]*colY.x + v[2]*colZ.x;
|
|
out->y = v[0]*colX.y + v[1]*colY.y + v[2]*colZ.y;
|
|
out->z = v[0]*colX.z + v[1]*colY.z + v[2]*colZ.z;
|
|
return out;
|
|
}
|
|
template<class...A> inline Recon FUN_00408848(A&&...) { return Recon(); }
|
|
template<class...A> inline Recon FUN_00433ed4(A&&...) { return Recon(); }
|
|
template<class...A> inline Recon FUN_0040e5f0(A&&...) { return Recon(); }
|
|
template<class...A> inline Recon FUN_0040e36c(A&&...) { return Recon(); }
|
|
template<class...A> inline Recon FUN_00406ff8(A&&...) { return Recon(); }
|
|
|
|
// Free vector op + pose/notify artifacts used unqualified in mech4.
|
|
template<class...A> inline Scalar Dot(A&&...) { return Scalar(0); }
|
|
template<class...A> inline Recon Build(A&&...) { return Recon(); }
|
|
template<class...A> inline void ReconBeginPose(A&&...) {}
|
|
template<class...A> inline void ReconEndPose(A&&...) {}
|
|
template<class...A> inline int ReconIsDerived(A&&...) { return 0; }
|
|
|
|
//
|
|
// Damage notification message (DamageMessage msg; DamageMessage::Build(&msg,...)).
|
|
//
|
|
struct DamageMessage
|
|
{
|
|
template<class...A> static void Build(A&&...) {}
|
|
};
|
|
|
|
// Geometry / pose constants.
|
|
static Quaternion kIdentityQuat;
|
|
static Vector3D kZeroVector;
|
|
static Scalar _DAT_004ac048 = 0;
|
|
static Scalar _DAT_004ab9c8 = 0;
|
|
static Scalar _DAT_004ab9d0 = 0;
|
|
static Scalar _DAT_004ab9d4 = 0;
|
|
static Recon vtable_0050bb90;
|
|
|
|
//===========================================================================//
|
|
// Subsystem-roster chain proxy.
|
|
//
|
|
// The Mech keeps several ChainOf<Subsystem*> capability views. The engine
|
|
// ChainOf has no default ctor / SetOwner / Construct / iterate-in-place verbs
|
|
// that the recovered ctor uses, so the roster members are declared with this
|
|
// proxy instead.
|
|
//===========================================================================//
|
|
template<class T> struct ReconChain
|
|
{
|
|
ReconChain() {}
|
|
ReconChain(void *) {}
|
|
template<class...A> void SetOwner(A&&...) {}
|
|
template<class...A> void Construct(A&&...) {}
|
|
template<class...A> void Reset(A&&...) {}
|
|
void Add(T) {}
|
|
T First() { return T(); }
|
|
T Next() { return T(); }
|
|
};
|
|
|
|
//===========================================================================//
|
|
// Debug-stream artifact ( DebugStream << x << endl; ).
|
|
//===========================================================================//
|
|
struct ReconStream
|
|
{
|
|
template<class T> ReconStream &operator<<(const T &) { return *this; }
|
|
void Flush() {}
|
|
void Emit() {}
|
|
};
|
|
|
|
//===========================================================================//
|
|
// Resolve-able connection slot proxy (e.g. the power-bus / death-effect slot).
|
|
//===========================================================================//
|
|
struct ReconSlot
|
|
{
|
|
template<class...A> Recon Resolve(A&&...) { return Recon(); }
|
|
template<class...A> void LinkTo(A&&...) {}
|
|
template<class...A> void Construct(A&&...){}
|
|
};
|
|
static ReconStream DebugStream;
|
|
static int endl = 0;
|
|
|
|
//===========================================================================//
|
|
// Type aliases consumed by the recovered headers. Defining the guard here
|
|
// suppresses the (now superseded) in-header alias blocks.
|
|
//===========================================================================//
|
|
#if !defined(BT_RECON_TYPE_ALIASES)
|
|
# define BT_RECON_TYPE_ALIASES
|
|
typedef ReconMatrix Matrix34; // orientation matrix proxy
|
|
typedef ReconAlarm AlarmIndicator; // multi-level status indicator
|
|
typedef ReconFiltered FilteredScalar; // running-average scalar
|
|
#endif
|
|
|
|
//===========================================================================//
|
|
// Decompiler-artifact globals actually referenced by mech-core code.
|
|
// * .data pointers used in address arithmetic -> char*
|
|
// * scalar tuning constants -> Scalar
|
|
// * opaque label/vtable/handle operands -> Recon
|
|
//===========================================================================//
|
|
static char *DAT_004efc94 = 0; // TheApp base (App*)
|
|
static Scalar DAT_005209d0 = 0; // FLT_MAX seed
|
|
static Scalar DAT_0052140c = 1; // runtime frame time scale (divisor)
|
|
static char DAT_004e0f8c[1] = { 0 }; // "" empty string literal
|
|
static Scalar _DAT_004ab9cc = 0;
|
|
|
|
//===========================================================================//
|
|
// Shared empty handler / attribute sets used to satisfy the 4-arg
|
|
// Simulation::SharedData ctor for the reconstructed DefaultData objects (the
|
|
// shipped game passes each class's own generated sets; an empty shared set is
|
|
// structurally correct for the offline reconstruction).
|
|
//===========================================================================//
|
|
static Receiver::MessageHandlerSet ReconMessageHandlers;
|
|
static Simulation::AttributeIndexSet ReconAttributeIndex;
|
|
|
|
static Recon PTR_FUN_0050cfa8; // Mech vtable
|
|
static Recon PTR_LAB_0050c0e8; // copy Performance
|
|
static Recon PTR_LAB_0050c0f4; // master Performance
|
|
|
|
// Animation SelectSequence operand triples (label + 2 floats per gait clip).
|
|
static Recon PTR_LAB_0050d6f0, DAT_0050d6f4, DAT_0050d6f8;
|
|
static Recon PTR_LAB_0050d6fc, DAT_0050d700, DAT_0050d704;
|
|
static Recon PTR_LAB_0050d708, DAT_0050d70c, DAT_0050d710;
|
|
static Recon PTR_LAB_0050d714, DAT_0050d718, DAT_0050d71c;
|
|
static Recon PTR_LAB_0050d720, DAT_0050d724, DAT_0050d728;
|
|
static Recon PTR_LAB_0050d72c, DAT_0050d730, DAT_0050d734;
|
|
static Recon PTR_LAB_0050d738, DAT_0050d73c, DAT_0050d740;
|
|
static Recon PTR_LAB_0050d744, DAT_0050d748, DAT_0050d74c;
|
|
|
|
#endif // MECHRECON_HPP
|