User + old-timers were right; the earlier "no voice exists" verdict was wrong. The authored mech audio has state watchers on Entity.SimulationState==3/4 (the binary's one-cell mech+0x40) that start sequence notes 29,16,40 -- two klaxon hits then Warnings01 zone 8 (key 40-41) = the "reverse disabled" voice line the testers remember. The port's cell split (graphicAlarm vs engine simulationState, gotcha #23) meant the trigger value never arrived. - mechdmg: mirror gimp 3/4 into SetSimulationState at the leg-half crossing (guarded: never stomps disabled/fall/dead states). Voice verified playing end-to-end on the bench: SetupPatch bank2 patch113 note=40 -> Warnings01_z7.wav. - mech.cpp: BT_GIMP_SAFE_BASE_READ on all seven Simulation::ReadUpdateRecord sites -- gimp is monotonic per life; a record captured pre-gimp must not stomp the cell (the loopback otherwise perpetuates the stale value and restarts the warning on every damage event). - diagnostics (all env-gated): [statefire]/[startreq]/[animind]/[gimp-sim]/ [simstomp]/[indstomp] + StateIndicator::DebugAudioWatcherCount + raised spatial-log caps. These traced the whole chain and PROVED no SetState path stomps the cell. OPEN (follow-up): a RAW writer (bypasses SetState entirely; invisible to the indicator-level trap) resets the cell between damage events -- under the bench's 1 Hz metronome harness it restarted the sequence before the 1.8 s voice note; sporadic real-play damage is unaffected (one edge -> full sequence). Needs a cdb write-watchpoint session; candidates: a recon raw +0x2c-equivalent write or a struct copy spanning it. Also decoded en route: the AnimationState triggers on the limp states play EngineShiftRev01 (the downshift foley) -- working, and NOT the voice. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
404 lines
9.5 KiB
C++
404 lines
9.5 KiB
C++
#pragma once
|
|
|
|
#include "state.h"
|
|
#include "receiver.h"
|
|
#include <intrin.h> // _ReturnAddress (the #78 simstomp diag)
|
|
#include "time.h"
|
|
#include "resource.h"
|
|
|
|
class Simulation__SharedData;
|
|
class Simulation__IndexData;
|
|
struct Simulation__IndexEntry;
|
|
class Simulation__AttributeIndexSet;
|
|
class MemoryStream;
|
|
|
|
//##########################################################################
|
|
//################# Simulation::UpdateRecord #########################
|
|
//##########################################################################
|
|
|
|
struct Simulation__UpdateRecord
|
|
{
|
|
public:
|
|
size_t recordLength;
|
|
Word subsystemID;
|
|
Word recordID;
|
|
Time timeStamp;
|
|
Enumeration simulationState;
|
|
};
|
|
|
|
//##########################################################################
|
|
//####################### Simulation #################################
|
|
//##########################################################################
|
|
|
|
class Simulation:
|
|
public Receiver
|
|
{
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Shared Data Support
|
|
//
|
|
public:
|
|
typedef Simulation__SharedData SharedData;
|
|
SharedData*
|
|
GetSharedData();
|
|
|
|
static Derivation *GetClassDerivations();
|
|
|
|
static SharedData DefaultData;
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Construction and Destruction Support
|
|
//
|
|
protected:
|
|
Simulation(
|
|
ClassID class_ID,
|
|
SharedData &shared_data
|
|
);
|
|
|
|
public:
|
|
~Simulation();
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Attribute Support
|
|
//
|
|
public:
|
|
typedef Enumeration AttributeID;
|
|
typedef Simulation__IndexData IndexData;
|
|
typedef Simulation__IndexEntry IndexEntry;
|
|
typedef Simulation__AttributeIndexSet AttributeIndexSet;
|
|
typedef int Simulation::*AttributePointer;
|
|
|
|
enum {
|
|
AnyAttributeID = 0,
|
|
SimulationStateAttributeID,
|
|
NextAttributeID
|
|
};
|
|
|
|
static const AttributePointer NullAttribute;
|
|
|
|
void*
|
|
GetAttributePointer(AttributeID attribute);
|
|
void*
|
|
GetAttributePointer(const char* attribute_name);
|
|
|
|
private:
|
|
static const IndexEntry AttributePointers[];
|
|
|
|
protected:
|
|
//static AttributeIndexSet AttributeIndex
|
|
static AttributeIndexSet& GetAttributeIndex();
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Simulation Support
|
|
//
|
|
public:
|
|
typedef void
|
|
(Simulation::*Performance)(Scalar time_slice);
|
|
typedef void
|
|
(Simulation::*Encore)();
|
|
typedef Simulation__UpdateRecord UpdateRecord;
|
|
|
|
void
|
|
SetPerformance(Performance performance)
|
|
{Check(this); activePerformance = performance;}
|
|
void
|
|
Perform(Scalar time_slice)
|
|
{
|
|
Check(this);
|
|
(this->*activePerformance)(time_slice);
|
|
}
|
|
|
|
virtual void
|
|
PerformAndWatch(
|
|
const Time& till,
|
|
MemoryStream *update_stream
|
|
);
|
|
|
|
void
|
|
DoNothingOnce(Scalar time_slice);
|
|
void
|
|
DoNothing(Scalar time_slice);
|
|
|
|
void
|
|
SetLastPerformance(const Time& when)
|
|
{Check(this); Check(&when); lastPerformance = when;}
|
|
void
|
|
RequestEncore(Encore encore);
|
|
|
|
virtual void
|
|
ReadUpdateRecord(UpdateRecord *message);
|
|
virtual void
|
|
WriteUpdateRecord(
|
|
UpdateRecord *message,
|
|
int update_model
|
|
);
|
|
void
|
|
WriteSimulationUpdate(MemoryStream *update_stream);
|
|
|
|
enum {
|
|
DefaultUpdateModelBit=0,
|
|
NextUpdateModelBit
|
|
};
|
|
|
|
enum {
|
|
DefaultUpdateModelFlag = 1<<DefaultUpdateModelBit
|
|
};
|
|
|
|
void
|
|
ForceUpdate(Word model=DefaultUpdateModelFlag)
|
|
{Check(this); updateModel |= model;}
|
|
|
|
protected:
|
|
Time
|
|
lastPerformance;
|
|
Time
|
|
lastUpdate;
|
|
Word
|
|
updateModel;
|
|
|
|
Performance
|
|
activePerformance;
|
|
|
|
//##########################################################################
|
|
// Flag Support
|
|
//
|
|
public:
|
|
enum {
|
|
DelayWatchersBit,
|
|
DontExecuteBit,
|
|
NextBit
|
|
};
|
|
enum {
|
|
DelayWatchersFlag = 1<<DelayWatchersBit,
|
|
DontExecuteFlag = 1<<DontExecuteBit
|
|
};
|
|
|
|
LWord simulationFlags;
|
|
|
|
void
|
|
SetWatcherDelay()
|
|
{Check(this); simulationFlags |= DelayWatchersFlag;}
|
|
void
|
|
ClearWatcherDelay()
|
|
{Check(this); simulationFlags &= ~DelayWatchersFlag;}
|
|
Logical
|
|
AreWatchersDelayed()
|
|
{Check(this); return (simulationFlags & DelayWatchersFlag) != 0;}
|
|
|
|
void
|
|
NeverExecute()
|
|
{Check(this); simulationFlags |= DontExecuteFlag;}
|
|
void
|
|
ExecuteOnUpdate()
|
|
{Check(this); simulationFlags |= DontExecuteFlag;}
|
|
void
|
|
AlwaysExecute()
|
|
{Check(this); simulationFlags &= ~DontExecuteFlag;}
|
|
Logical
|
|
IsReplicantExecutable()
|
|
{
|
|
Check(this);
|
|
return
|
|
(simulationFlags&DontExecuteFlag) == 0
|
|
|| lastUpdate >= lastPerformance;
|
|
}
|
|
Logical
|
|
IsNonReplicantExecutable()
|
|
{Check(this); return (simulationFlags&DontExecuteFlag) == 0;}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// State support
|
|
//
|
|
public:
|
|
enum {
|
|
DefaultState = 0,
|
|
StateCount
|
|
};
|
|
|
|
unsigned
|
|
GetSimulationState()
|
|
{Check(this); return simulationState.GetState();}
|
|
unsigned
|
|
GetOldSimulationState()
|
|
{Check(this); return simulationState.GetOldState();}
|
|
void
|
|
SetSimulationState(unsigned new_state)
|
|
{Check(this);
|
|
// #78 DIAG: trap the gimp-cell downgrade (3/4 -> <=1) with the
|
|
// caller's return address (symbolize via tools/symcrash.py).
|
|
if (getenv("BT_AUDIO_SPATIAL")) {
|
|
unsigned _cur = simulationState.GetState();
|
|
if ((_cur == 3 || _cur == 4) && new_state != _cur) {
|
|
static int s_ds=0; if (s_ds++<40)
|
|
DEBUG_STREAM << "[simstomp] " << _cur << "->" << new_state
|
|
<< " this=" << (void*)this
|
|
<< " ra=" << _ReturnAddress()
|
|
<< "\n" << std::flush; }
|
|
}
|
|
simulationState.SetState(new_state);}
|
|
|
|
StateIndicator
|
|
simulationState;
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Watcher Support
|
|
//
|
|
public:
|
|
void
|
|
AddAudioWatcher(Component *watcher)
|
|
{Check(&audioWatcherSocket);audioWatcherSocket.Add(watcher);}
|
|
void
|
|
AddVideoWatcher(Component *watcher)
|
|
{Check(&videoWatcherSocket);videoWatcherSocket.Add(watcher);}
|
|
void
|
|
AddGaugeWatcher(Component *watcher)
|
|
{Check(&gaugeWatcherSocket);gaugeWatcherSocket.Add(watcher);}
|
|
|
|
void
|
|
AddEffectWatcher(Component *watcher)
|
|
{Check(&effectWatcherSocket); effectWatcherSocket.Add(watcher);}
|
|
|
|
void
|
|
ExecuteWatchers();
|
|
|
|
int // DEBUG (BT_AUDIO_LOG): how many audio watchers are registered on this sim
|
|
DebugAudioWatcherCount();
|
|
|
|
private:
|
|
SChainOf<Component*>
|
|
audioWatcherSocket;
|
|
SChainOf<Component*>
|
|
videoWatcherSocket;
|
|
SChainOf<Component*>
|
|
gaugeWatcherSocket;
|
|
SChainOf<Component*>
|
|
effectWatcherSocket;
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Test Support
|
|
//
|
|
public:
|
|
Logical
|
|
TestInstance() const;
|
|
static Logical
|
|
TestClass();
|
|
};
|
|
|
|
//##########################################################################
|
|
//################### Simulation::IndexEntry #########################
|
|
//##########################################################################
|
|
|
|
struct Simulation__IndexEntry
|
|
{
|
|
Enumeration
|
|
entryID;
|
|
const char *
|
|
entryName;
|
|
Simulation::AttributePointer
|
|
entryAddress;
|
|
};
|
|
|
|
#define ATTRIBUTE_ENTRY(class,name,attribute)\
|
|
{\
|
|
class::name##AttributeID,\
|
|
#name,\
|
|
(Simulation::AttributePointer) &class::attribute\
|
|
}
|
|
|
|
//##########################################################################
|
|
//################# Simulation::AttributeIndexSet ####################
|
|
//##########################################################################
|
|
|
|
class Simulation__AttributeIndexSet:
|
|
public Receiver::InheritanceSet
|
|
{
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Construction and Destruction
|
|
//
|
|
public:
|
|
Simulation__AttributeIndexSet(
|
|
Simulation::AttributeID count,
|
|
const Simulation::IndexEntry index_table[],
|
|
const Simulation::AttributeIndexSet &inheritance
|
|
)
|
|
{Build(count, index_table, &inheritance);}
|
|
|
|
Simulation__AttributeIndexSet(
|
|
Simulation::AttributeID count,
|
|
const Simulation::IndexEntry index_table[]
|
|
)
|
|
{Build(count, index_table, NULL);}
|
|
|
|
Simulation__AttributeIndexSet()
|
|
{attributeIndex = NULL; entryCount = 0;}
|
|
|
|
~Simulation__AttributeIndexSet();
|
|
|
|
protected:
|
|
void
|
|
Build(
|
|
Simulation::AttributeID count,
|
|
const Simulation::IndexEntry index_table[],
|
|
const Simulation::AttributeIndexSet *inheritance
|
|
);
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// AttributeIndexSet Functionality
|
|
//
|
|
protected:
|
|
Simulation::IndexEntry
|
|
*attributeIndex;
|
|
|
|
public:
|
|
Simulation::AttributePointer
|
|
Find(Simulation::AttributeID attribute) const
|
|
{
|
|
Check(this);
|
|
Verify(attribute > 0);
|
|
if (attribute<=entryCount)
|
|
return attributeIndex[attribute-1].entryAddress;
|
|
else
|
|
return Simulation::NullAttribute;
|
|
}
|
|
Simulation::AttributePointer
|
|
Find(const char* attribute_name) const;
|
|
const Simulation::IndexEntry*
|
|
FindEntry(const char* attribute_name) const;
|
|
|
|
static const Simulation::AttributeIndexSet
|
|
NullSet;
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Test Support
|
|
//
|
|
public:
|
|
static Logical
|
|
TestClass();
|
|
};
|
|
|
|
//##########################################################################
|
|
//################### Simulation::SharedData #########################
|
|
//##########################################################################
|
|
|
|
class Simulation__SharedData:
|
|
public Receiver::SharedData
|
|
{
|
|
public:
|
|
Simulation__SharedData(
|
|
Derivation* derivation,
|
|
Receiver::MessageHandlerSet &message_handlers,
|
|
Simulation::AttributeIndexSet &attribute_index,
|
|
int state_count
|
|
):
|
|
Receiver::SharedData(derivation, message_handlers),
|
|
activeAttributeIndex(&attribute_index),
|
|
stateCount(state_count)
|
|
{}
|
|
|
|
Simulation::AttributeIndexSet* activeAttributeIndex;
|
|
int stateCount;
|
|
};
|
|
|
|
inline Simulation::SharedData*
|
|
Simulation::GetSharedData()
|
|
{return Cast_Object(SharedData*,sharedData);}
|