Starting the MR port, since the pods want it too. Four changes, and one correction of my own making. A release Fail now says what failed. It expanded to a bare abort() that threw the message away, and because the compiler merges identical cold paths, every Fail in a function became one anonymous stub - a crash named the function and nothing else. It now prints message, file and line. It also writes that line to rpl4-fail.log and closes the file. rpl4.log is std::cout with an ofstream's streambuf swapped in, and the text written there did NOT survive the exit however it was flushed; rather than guess where it stopped, the one line that matters goes to its own file, and fclose is a promise it reached the disk. Its own file because rpl4.log is already open for writing and Windows will not share it. -pb plays a spool back without recording a new one. Mode 2 has been in RPL4.CPP all along - one spool file instead of two, no spooling application - with no way to ask for it. -mr starts a recorder alongside the playback and that recorder is the half that cannot cope away from a pod bay. Which is the third fix: L4SpoolingNetworkManager::StartConnecting walks the hosts named in the egg, and called host->GetHostID() on whatever FindHost returned. FindHost answers NULL for a host that is in the egg but not connected - impossible in an arcade, ordinary everywhere else - and it crashed before the mission could start. The table is read back one pair per egg host in egg order, so a missing host cannot be skipped without shifting every entry after it; it writes the pair, says so, and carries on. And RPL4.CPP chose between the playback application and an idle one in silence. For the first few seconds those look identical from outside, so a spool that failed to load was indistinguishable from one that had not started yet. It now says which, and why not. The correction: I reported a failure in L4AudioRenderer::Initialize on audiomr.ini's clipping_radius. That was mine. I was running cdb from the source tree, so the game looked for audio\audiomr.ini beside the debugger rather than beside itself and found nothing. With the working directory right, audio initialises fine and always did. Where it stands: -pb loads last.spl - "2973012 bytes to play" - creates RPL4PlaybackApplication and runs without crashing. Whether it puts the race on the screen is the next thing to look at, and that wants eyes rather than a log. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
263 lines
6.4 KiB
C++
263 lines
6.4 KiB
C++
#include "munga.h"
|
|
#pragma hdrstop
|
|
|
|
#include "controls.h"
|
|
#include "gaugrend.h"
|
|
//#include "windows.h"
|
|
|
|
#if defined(__BCPLUSPLUS__)
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
#if DEBUG_LEVEL>0
|
|
int _matherr(struct exception *a)
|
|
{
|
|
switch (a->type)
|
|
{
|
|
case DOMAIN:
|
|
DEBUG_STREAM << "Domain" << std::flush;
|
|
break;
|
|
case SING:
|
|
DEBUG_STREAM << "Singularity" << std::flush;
|
|
break;
|
|
case UNDERFLOW:
|
|
DEBUG_STREAM << "Underflow" << std::flush;
|
|
break;
|
|
case OVERFLOW:
|
|
DEBUG_STREAM << "Overflow" << std::flush;
|
|
break;
|
|
case TLOSS:
|
|
DEBUG_STREAM << "Loss of Precision" << std::flush;
|
|
break;
|
|
}
|
|
DEBUG_STREAM << " error occurred in " << a->name << endl << std::flush;
|
|
DEBUG_STREAM << "arg1 = " << a->arg1 << endl << std::flush;
|
|
DEBUG_STREAM << "arg2 = " << a->arg2 << endl << std::flush;
|
|
DEBUG_STREAM << "retval = " << a->retval << endl << std::flush << std::flush;
|
|
return 0;
|
|
}
|
|
#else
|
|
int _matherr(struct exception *)
|
|
{
|
|
_fpreset();
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
extern "C" void _pure_error_()
|
|
{
|
|
Fail("Pure virtual function called");
|
|
}
|
|
#endif
|
|
|
|
|
|
//
|
|
//#############################################################################
|
|
// Fpu_Ok
|
|
//
|
|
// returns True if fpu has no errors, False if errors found
|
|
//#############################################################################
|
|
//
|
|
int
|
|
Fpu_Ok()
|
|
{
|
|
unsigned int s =
|
|
_status87() &
|
|
(
|
|
SW_INVALID |
|
|
// SW_DENORMAL |
|
|
SW_ZERODIVIDE |
|
|
SW_OVERFLOW
|
|
// SW_UNDERFLOW |
|
|
//VERIFY: SW_STACKFAULT
|
|
);
|
|
if (s)
|
|
{
|
|
DEBUG_STREAM << "ERROR: FPU status=" << std::flush;
|
|
|
|
if (s & SW_INVALID)
|
|
{
|
|
DEBUG_STREAM << "invalid " << std::flush;
|
|
}
|
|
if (s & SW_DENORMAL)
|
|
{
|
|
DEBUG_STREAM << "denormalized " << std::flush;
|
|
}
|
|
if (s & SW_ZERODIVIDE)
|
|
{
|
|
DEBUG_STREAM << "zeroDivide " << std::flush;
|
|
}
|
|
if (s & SW_OVERFLOW)
|
|
{
|
|
DEBUG_STREAM << "overflow " << std::flush;
|
|
}
|
|
if (s & SW_UNDERFLOW)
|
|
{
|
|
DEBUG_STREAM << "underflow " << std::flush;
|
|
}
|
|
/*if (s & SW_STACKFAULT)
|
|
{
|
|
DEBUG_STREAM << "stackFault" << std::flush;
|
|
}*/
|
|
DEBUG_STREAM << "\n" << std::flush << std::flush;
|
|
return False;
|
|
}
|
|
else
|
|
{
|
|
return True;
|
|
}
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
static Logical AlreadyFailed=False;
|
|
|
|
void Verify_Failed(char *Message, char *File, int Line)
|
|
{
|
|
if (!AlreadyFailed)
|
|
{
|
|
AlreadyFailed = True;
|
|
|
|
DEBUG_STREAM << File << "(" << Line << "): Failed " << Message << "\n" << std::flush;
|
|
#if defined(USE_PROFILE_ANALYSIS)
|
|
Analysis_Named_Status();
|
|
#endif
|
|
DEBUG_STREAM << "Failing to debugger\n" << std::flush << std::flush;
|
|
SystemClock::timer.Shutdown();
|
|
ControlsManager::Shutdown();
|
|
GaugeRenderer::EmergencyShutdown();
|
|
*(int*)(0xFFFFFFFF) = 0;
|
|
}
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
//
|
|
// The release build's Fail. Says what went wrong before it goes.
|
|
//
|
|
// Terminates exactly as the old bare abort() did - same call, same
|
|
// 0xC0000409 - so no caller, script or crash handler sees a change. The
|
|
// only difference is the line in the log naming the check that failed and
|
|
// where it lives.
|
|
//
|
|
// The emergency shutdowns are worth doing before the process ends even
|
|
// though it is ending: the gauge renderer and the controls manager both
|
|
// hold hardware, and a display left mid-mode is a nuisance to whoever has
|
|
// to pick the machine up afterwards - which in a pod bay is not the
|
|
// person who was playing.
|
|
//
|
|
void
|
|
Fail_With_Message(const char *Message, const char *File, int Line)
|
|
{
|
|
if (!AlreadyFailed)
|
|
{
|
|
AlreadyFailed = True;
|
|
|
|
DEBUG_STREAM << "FAIL " << File << "(" << Line << "): " << Message
|
|
<< "\n" << std::flush;
|
|
|
|
//
|
|
// And again, somewhere that cannot be lost.
|
|
//
|
|
// rpl4.log is std::cout with its streambuf swapped for an ofstream's
|
|
// (see WinMain), and the text written above did NOT survive to disk
|
|
// across the exit - flushed as far as the stream and no further.
|
|
// Rather than guess at where it stops, write the one line that
|
|
// matters to its own file and close it: fclose is a promise the
|
|
// bytes are on the disk, and a file opened and closed inside this
|
|
// function cannot be left dangling by whatever happens next.
|
|
//
|
|
// Its own file rather than an append to rpl4.log because that one is
|
|
// already open for writing and Windows will not share it.
|
|
//
|
|
FILE *record = fopen("rpl4-fail.log", "w");
|
|
|
|
if (record != NULL)
|
|
{
|
|
fprintf(record, "FAIL %s(%d): %s\n", File, Line, Message);
|
|
fclose(record);
|
|
}
|
|
|
|
//
|
|
// Hardware last, and after the record is safely written: the gauge
|
|
// renderer and the controls manager both hold devices, and a display
|
|
// left mid-mode is a nuisance for whoever picks the machine up - in
|
|
// a pod bay, not the person who was playing. If one of these faults
|
|
// on the way down, the failure has already been recorded.
|
|
//
|
|
SystemClock::timer.Shutdown();
|
|
ControlsManager::Shutdown();
|
|
GaugeRenderer::EmergencyShutdown();
|
|
}
|
|
abort();
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
void
|
|
Fail_To_Debugger(char *Message, char *File, int Line)
|
|
{
|
|
if (!AlreadyFailed)
|
|
{
|
|
AlreadyFailed = True;
|
|
|
|
DEBUG_STREAM << File << "(" << Line << "): " << Message << "\n" << std::flush;
|
|
#if defined(USE_PROFILE_ANALYSIS)
|
|
Analysis_Named_Status();
|
|
#endif
|
|
DEBUG_STREAM << "Failing to debugger\n" << std::flush << std::flush;
|
|
SystemClock::timer.Shutdown();
|
|
ControlsManager::Shutdown();
|
|
GaugeRenderer::EmergencyShutdown();
|
|
*(int*)(0xFFFFFFFF) = 0;
|
|
}
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
#if defined(USE_SIGNATURE)
|
|
Signature::Signature()
|
|
{
|
|
#if DEBUG_LEVEL>0
|
|
mark = magic;
|
|
#endif
|
|
}
|
|
|
|
Signature::~Signature()
|
|
{
|
|
#if DEBUG_LEVEL>0
|
|
mark = noMagic;
|
|
#endif
|
|
}
|
|
|
|
int
|
|
Is_Signature_Bad(const volatile Signature *p)
|
|
{
|
|
#if DEBUG_LEVEL>0
|
|
if (p->mark == Signature::magic)
|
|
{
|
|
return Signature::ok;
|
|
}
|
|
else if (p->mark == Signature::noMagic)
|
|
{
|
|
return Signature::destroyed;
|
|
}
|
|
else
|
|
{
|
|
return Signature::corrupted;
|
|
}
|
|
#else
|
|
return False;
|
|
#endif
|
|
}
|
|
#endif
|