#include "pch.hpp"
//===========================================================================//
// File:	 Exceptions.cpp													 //
// Project:  GameOS					                                         //
// Contents: Math and Processor Exceptions									 //
//---------------------------------------------------------------------------//
// Date      Who    Modification                                             //
//																			 //
//---------------------------------------------------------------------------//
// Copyright (C) 1999 Microsoft Corp.						                 //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//
#include "ErrorHandler.hpp"
#include "Windows.hpp"
#include "Registry.hpp"
#include "DirectX.hpp"
#include "DXRasterizer.hpp"
#include "MemoryManager.hpp"
#include "Sound Renderer.hpp"
#include "ControlManager.hpp"
#include "LocalizationManager.hpp"
#include "Time.hpp"
#include "Texture Manager.hpp"
#include "Debugger.hpp"
#include "Profiler.hpp"
#include "VideoPlayback.hpp"
#include "Music.hpp"
#include "time.hpp"

// for memory tracking
extern HGOSHEAP DefaultHeap;
extern DWORD InUpdateRenderers;		// True when in 'Update Renderers'
extern bool gNoDialogs;
extern DWORD gNoFPU;


DWORD DisableErrors=0;
EXCEPTION_RECORD SavedExceptRec;
CONTEXT SavedContext;
CONTEXT OldSavedContext;
char* ErrorExceptionText;
volatile char* ExceptionAttempt="";
bool MathExceptions=0;
bool MathSinglePrecision=0;
char* OldText;
int OldFlags;
char* Symbol="";
char* Location="";
BYTE* GotScreenImage=0;					// Pointer to buffer containing screen image (always 24 bit bmp)
int AllowDebugButton=0;
volatile int ProcessingError=0;			// Renentrancy test flag for assert error routine
IgnoreAddress*	pIgnore=0;
DWORD NumberFTOL;
int	eWidth;
int eDevice;
int	eHeight;
int eBPP;
int	eRefresh;
int eMode;
int eHardware;
extern DWORD CurrentRefreshRate;


//
// Current control word state.
//
volatile WORD FPUControl=0x27f;			// Current FPU control word
WORD FPUDefault=0x27f;					// Default FPU control word

extern CONTEXT WatchDogContext;
extern bool FromWatchDog;



//
// Globals for passing a file and line
//
char* gosErrorFile;
DWORD gosErrorLine;


extern "C" {
//
// Flags Bits:
//
// 1	- Continue not possible
// 2    - Do not display registers
// 4	- gosASSERT
// 8	- VERIFY
// 16	- Exception handler
// 32   - Dump
//
//
//
// This function triggers the exception handler dialog box.
//
//
int __stdcall ErrorHandler( int Flags, char* Text )
{
//
// If AllowFail is true, just return
//
	if( AllowFail )
		return 0;
//
// Check for reentrant error handling
//
	ProcessingError++;
//
// Look for C++ static classes that are failing in their destructor
//
	if( AfterExit==status )
	{
		MessageBox( hWindow, Text, "Error after GameOS exit!", MB_OK );
		FatalExit( 0 );
	}
//
// Error before machine even setup yet?
//
	if( BeforeInit==status )
	{
		MessageBox( hWindow, Text, "Error before system initialized", MB_OK );
		status=AfterExit;
		exit(1);
	}
//
// Try hard to display message, exit cleanly or just exit!
//
	if( ProcessingError>=5 )
	{
		status=AfterExit;
		exit(1);					// 5th time - exit
	}
	if( ProcessingError==4 )
	{
		DestroyImageHlp();			// 4th time - just cleanup imagehlp and exit
		status=AfterExit;
		exit(1);
	}
	if( ProcessingError==3 )			// 3rd try and cleanup and exit
		ExitGameOS();
	if( ProcessingError==2 )			// 2nd time in exception handler - tell user re-entered
	{
		if( !AllowFail )
		{
			static char rBuffer[128];
			if( ProcessingError==1 && (ErrorFlags & (gos_ErrorNoRegisters + gos_ErrorMessage + gos_ErrorStop + gos_ErrorAssert + gos_ErrorVerify)) )
			{
				wsprintf(rBuffer,"Nested exception! - Cause: '%s'\n\nError: \"%s\"",ExceptionAttempt,Text );
			}
			else
			{
				wsprintf(rBuffer,"Nested exception! - Cause: '%s'\n\nError: \"%s\" at 0x%x",ExceptionAttempt,Text,SavedContext.Eip );
			}
			ExceptionAttempt="MessageBox";
			ShowCursor(TRUE);																											
			MessageBox(hWindow,rBuffer,"Fatal Error",MB_OK);
			ShowCursor(FALSE);																	
			SavedContext=OldSavedContext;
			Flags=OldFlags;
			Text=OldText;
		}
	}
//
// Save the floating point state incase invalid
//
	static char FPUState[128];
	_asm fnsave FPUState;
//
// Copy type of error into globals (so dialog can access)
//
	ErrorFlags=Flags;
	ErrorExceptionText=Text;
//
// If from a watchdog timer, use it's context
//
	static STACKFRAME sf;
	if( FromWatchDog )
	{
		SavedContext=WatchDogContext;
		ExceptionAttempt="Watchdog stack walk";
		InitStackWalk( &sf, &SavedContext );
		FromWatchDog=0;
	}
	else
	{
//
// If from an assert, create call stack pointer
//
		if( ProcessingError==1 && (ErrorFlags & (gos_ErrorNoRegisters + gos_ErrorMessage + gos_ErrorStop + gos_ErrorAssert + gos_ErrorVerify)) )
		{
			SavedContext.Eip=(DWORD)ErrorHandler;
			_asm
			{
				push eax
				lea eax,SavedContext
				mov [eax]+CONTEXT.Ebp,ebp 
				mov [eax]+CONTEXT.Esp,esp
				pop eax
			}
		}
//
// Setup the stack walking routine in ImageHlp (Requires SavedContext setup correctly)
//
		ExceptionAttempt="InitStackWalk";
		InitStackWalk( &sf, &SavedContext );
//
// Walk the stack once
//
		ExceptionAttempt="WalkStack1";
		WalkStack( &sf );
//
// When called by an assert must walk the stack once to where the call occured
//
		if( ErrorFlags & (gos_ErrorDump + gos_ErrorMessage + gos_ErrorStop + gos_ErrorAssert + gos_ErrorVerify) )
		{
			ExceptionAttempt="WalkStack2";
			SavedContext.Eip=WalkStack( &sf );
		}
//
// When coming from a DUMP message, walk stack past dump functuon
//
		if( ErrorFlags & (gos_ErrorPop + gos_ErrorMessage + gos_ErrorDump + gos_ErrorStop + gos_ErrorAssert + gos_ErrorVerify ))
		{
			ExceptionAttempt="WalkStack3";
			SavedContext.Eip=WalkStack( &sf );
		}
//
// Walk stack past two functuons
//
		if( ErrorFlags & (gos_ErrorPop2+gos_ErrorFileLine) )
		{
			ExceptionAttempt="WalkStack4";
			SavedContext.Eip=WalkStack( &sf );
		}
	}
//
// Store exception address in case of recursion
//
	if( ProcessingError==1 )
	{
		OldText=Text;
		OldFlags=Flags;
		OldSavedContext=SavedContext;
	}
//
// See if this address should be ignored
//
	if( pIgnore )
	{
		IgnoreAddress* pThis=pIgnore;
		while( pThis )
		{
			if( pThis->Address==SavedContext.Eip )
			{
				ProcessingError=0;
				_asm frstor FPUState; // Restore the floating point state before returning
				return 0;
			}
			pThis=pThis->pNext;
		}
	}
//
// Get information about where assert or error occured
//
	static char BufferSym[MAX_PATH];
	ExceptionAttempt="GetSymbolFromAddress";
	Symbol=GetSymbolFromAddress( BufferSym, SavedContext.Eip );

	static char BufferLoc[MAX_PATH];
	IMAGEHLP_LINE pline;
	ExceptionAttempt="GetLocationFromAddress";
	Location=GetLocationFromAddress( &pline, BufferLoc, SavedContext.Eip );
//
// Append routine that caused error?
//
	if( ErrorFlags&gos_ErrorAppendRoutine )
	{
		static char NewTitle[256];

		if( Symbol && *Symbol!=0 )
			sprintf( NewTitle, "%s in %s", Text, Symbol );
		else
			sprintf( NewTitle, "%s at %s", Text, Hex8Number(SavedContext.Eip) );

		Text=NewTitle;
	}
//
// Show what caused error
//
	FixedLengthString Message(MAX_MESSAGE_SIZE);
	FixedLengthString MessageTitle(256);
	if( ErrorFlags&gos_ErrorDump )
	{
		MessageTitle << ErrorTitle << " : " << Text << "\n";
	}
	else
		ErrorTitle="ERROR";

	if( ErrorFlags&gos_ErrorAssert )
	{
		ErrorTitle="ASSERT";
		MessageTitle << "ASSERT    : " << Text << "\n";
	} else
	if( ErrorFlags&gos_ErrorVerify )
	{
		ErrorTitle="VERIFY";
		MessageTitle << "VERIFY    : " << Text << "\n";
	} else
	if( ErrorFlags&gos_ErrorException )
	{
		ErrorTitle="EXCEPTION";
		MessageTitle << "EXCEPTION : " << Text << "\n";
	} else
	if( ErrorFlags&gos_ErrorStop )
	{
		ErrorTitle="STOP";
		MessageTitle << "STOP      : " << Text << "\n";
	} else
	if( ErrorFlags&gos_ErrorMessage )
	{
		MessageTitle << Text << "\n";
	} 
	Message << MessageTitle;
//
// If user specified file/line don't show where in code
//
	if( ErrorFlags&gos_ErrorFileLine ) 
	{
//
// If a file and line were given as valid, try and display them
//
		if( gosErrorFile && *gosErrorFile )
		{
			Message << "File      : " << gosErrorFile << "(" << (int)gosErrorLine << ")\n";
			char BufferLine[MAX_LINE_LENGTH];
			if( GetLineFromFile( BufferLine, gosErrorFile, gosErrorLine ) )
				Message << "Line      : " << BufferLine;
		}
	}
	else
	{
//
// Show location
//
		if( SavedContext.Eip )
			Message << "Address   : " << Hex8Number(SavedContext.Eip) << "\n";
//
// Show symbol and Location if PDB found
//	
		if( Symbol && *Symbol!=0 )
			Message << "Location  : " << Symbol << "\n";
		if( Location && *Location!=0 )
			Message << "File      : " << Location << "\n";
//
// On a debug build and an exception, show actual line if possible (Not on STOP)
//
		#ifdef _DEBUG
			ExceptionAttempt="GetLineFromFile";
			static char BufferLine[MAX_LINE_LENGTH];
			if( GetLineFromFile( BufferLine, pline.FileName, pline.LineNumber ) )
				Message << "Line      : " << BufferLine ;
		#endif
	}
//
// Get any game information (Application specific) if the client wants it listed first
//
	if( ErrorFlags & gos_ErrorGameInfoFirst )
	{
		ExceptionAttempt="GetGameDetails";
		GetGameDetails( Message , ErrorFlags);
	}
//
// OutputDebugString filename and line number so the debugger can double click on it
//
	FixedLengthString ODS(512);
	if( ErrorFlags&gos_ErrorException )
		ODS << "\n" << Location << " : " << ErrorTitle << " : " << Text << "\n\n";
	else
	{
		if( ErrorFlags&gos_ErrorMessage )
		{
			ODS << "\n" << Text << "\n\n";
		}
		else
		{
			if( ErrorFlags&gos_ErrorFileLine ) 
			{
				if (gosErrorFile && strlen(gosErrorFile) != 0)
					ODS << "\n" << gosErrorFile << "(" << (int)gosErrorLine << "): " << Text << "\n\n";
			}
			else
			{
				if( Location && *Location!=0 )
				{
					ODS << "\n" << Location << " : (" << Hex8Number(SavedContext.Eip) << ") - " << ErrorTitle << " : " << Text << "\n\n";
				}
				else
				{
					ODS << "\n" << ErrorTitle << " : " << Text << " - (" << Hex8Number(SavedContext.Eip) << ")\n\n";
				}
			}
		}
	}
//
// Also spew to disk file or to debugger if enabled
//
	if( ProcessingError==1 )
	{
		if( !AllowFail )							// SPEW Allowfail errors to log, but not to OutputDebugString
		{
			ExceptionAttempt="OutputDebugString";
			OutputDebugString( ODS );
		}
		ExceptionAttempt="Spew Error";
		if( DebugDisplay==DbgS_Spew || hSpewOutput!=INVALID_HANDLE_VALUE )
		{
			Spew( ODS );
		}
	}
//
// Turn off spews until out of error dialog
//
	SpewSilence=1;
//
// Stop counting the time in the game
//
	StartPauseTime();
//
// Remember screen mode info
//
	eMode=0;
	eHardware=HardwareRenderer;
	eDevice=Environment.FullScreenDevice;
	eWidth=Environment.screenWidth;
	eHeight=Environment.screenHeight;
	eRefresh=CurrentRefreshRate;
	eBPP=Environment.bitDepth;
	if(Environment.fullScreen && hWindow )
		eMode=1;
//
// Get Processor information (Call stack, registers..)
//
	if( 0==(gos_ErrorFileLine&ErrorFlags) )
	{
		ExceptionAttempt="GetProcessorDetails";
		GetProcessorDetails( &sf, Message );
	}
//
// Get machine information (User, hardware, memory...)
//
	ExceptionAttempt="GetMachineDetails";
	GetMachineDetails( Message );
//
// Grab a copy of the backbuffer (may need for save to disk)
//
	if( ProcessingError==1 && hWindow && !(Environment.Renderer==3 && InUpdateRenderers) )
	{
		DWORD tempgScreenBMP=gScreenBMP;
		gScreenBMP=1;
		ExceptionAttempt="GrabScreenImage";
		GotScreenImage=GrabScreenImage(BackBufferSurface, 0, 0);
		gScreenBMP=tempgScreenBMP;
	}
	else
		GotScreenImage=0;
//
// Return from full screen mode if enabled and enable cursor again
//
	if( eMode )
	{
		ExceptionAttempt="EnterWindowMode";
		EnterWindowMode();						// Hide SPEW output when returning to window mode (make debug info easier to read!)
	}
	ShowCursor(TRUE);																											
//
// Pause sound and video
//
	ExceptionAttempt="VideoPause";
	VideoManagerPause();
	ExceptionAttempt="SoundPause";
	SoundRendererPause();
//
// Get hardware information from DirectDraw and DirectSound
//
	ExceptionAttempt="GetDirectXDetails";
	GetDirectXDetails( Message );
//
// Get audio/video codec details
//
	ExceptionAttempt="GetInstallAudioVideoCodecs";
	GetInstalledAudioVideoCodecs( Message );
//
// Get any game information (Application specific)
//
	if( !(ErrorFlags & gos_ErrorGameInfoFirst) )
	{
		ExceptionAttempt="GetGameDetails";
		GetGameDetails( Message , ErrorFlags);
	}
//
// Log stuff
//
	ExceptionAttempt="Logrun";
	LogRun( MessageTitle );
//
// Make sure DirectInput unaquired.
//
	ExceptionAttempt="ControlsPause";
	CMUnacquireControls();
//
// Errors are not valid in other threads
//
	if( ThreadId!=GetCurrentThreadId() )
	{
		MessageBox( hWindow,"Error not in main thread","Error", MB_OK );
	}
//
// Am I skipping all dialogs?
//
	if( gNoDialogs )
	{
		if( 0!=(ErrorFlags&gos_ErrorNoContinue) || 0!=(ErrorFlags&gos_ErrorRetry) )
			goto UserExit;
		goto UserContinue;
	}
//
// Display simple error dialog
//
	ErrorMessageTitle=MessageTitle;
	ErrorMessage=Message;
	ExceptionAttempt="SimpleDialogBox";
	DoSimpleDialog();
//
// Simple dialog exit?
//
	if( ErrorReturn==-1 )
		goto UserExit;
//
// Simple dialog continue?
//
	if( ErrorReturn==1 )
		goto UserContinue;
//
// Simple dialog 'More Detail...' - Show detailed error dialog
//
	ExceptionAttempt="DetailedDialogBox";
	DoDetailedDialog();
//
// If a captured screen image present or log files, destroy them.
//
	ExceptionAttempt="Freeing";
	if(GotScreenImage)
	{
		free(GotScreenImage);
		GotScreenImage=0;
	}
//
// Continue or Replay - return a false value
//
	if( ErrorReturn==IDC_CONTINUE || ErrorReturn==IDC_REPLAY )
	{
		goto UserContinue;
	}
//
// Debug - returns a true value
//
	if( ErrorReturn==IDC_DEBUG )
	{
		if( LaunchDebugger )				// If program not currently being debugged, launch debugger
		{
			ExceptionAttempt="TriggerDebugger";
			TriggerDebugger();
		}
		SpewSilence=0;
		ProcessingError=0;
//
// Restore the floating point state incase invalid
//
		_asm frstor FPUState;
		return 1;
	}
//
// Ignore - never stop at this address again
//
	if( ErrorReturn==IDC_IGNORE )
	{
		SpewSilence=0;
		IgnoreAddress* pThis=(IgnoreAddress*)gos_Malloc(sizeof(IgnoreAddress), DefaultHeap);
		pThis->Address=SavedContext.Eip;
		if( pIgnore )
			pThis->pNext=pIgnore;
		else
			pThis->pNext=0;
		pIgnore=pThis;
		goto UserContinue;
	}
//
// Exit from GameOS
//
UserExit:
	ExceptionAttempt="Freeing";
	if(GotScreenImage)
	{
		free(GotScreenImage);
		GotScreenImage=0;
	}
	if( ThreadId!=GetCurrentThreadId() )
	{
		TerminateGame=1;
		return 0;
	}
	if( ProcessingError==1 )
	{
		ExceptionAttempt="ExitGOS";
		ExitGameOS();
	}
	else
	{
		ExceptionAttempt="DestroyImageHlp";
		DestroyImageHlp();
		status=AfterExit;
		exit(1);
	}
	return 0;

//
// Continue - return to program
//
UserContinue:
	SpewSilence=0;
	ExceptionAttempt="Freeing";
	if(GotScreenImage)
	{
		free(GotScreenImage);
		GotScreenImage=0;
	}
//
// Continue sound, video and controls
//
	if( ErrorReturn!=IDC_REPLAY )
	{
		ExceptionAttempt="VideoContinue";
		VideoManagerContinue();
		ExceptionAttempt="SoundContinue";
		SoundRendererContinue();
	}
//
// Restart counting the time in the game
//
	EndPauseTime();
//
// Restore the floating point state incase invalid
//
	_asm frstor FPUState;
//
// Turn off windows cursor again
//
	ShowCursor(FALSE);				// Turn off cursor again
	ProcessingError=0;
	if( eMode )						// Re-enter full screen mode?
		EnterFullScreenMode();
//
// If restart gameos, do it now
//
	if( ErrorReturn==IDC_REPLAY )
	{
		if( HitScrollLock )
		{
			ForceReStart=1;
		}
		else
		{
			RestartGameOS();
		}
	}
	return 0;
}

} // extern "C" end







pFTOL*	ListOfFTOL=0;
DWORD	FoundFTOL=0;


//
// This stores a linked list of all float to longs that have occured this frame - it does not store duplicates
//
void DebugFTOL()
{
	DWORD Heap[6];
	WalkStack( Heap, 5, 2 );

	pFTOL* pThis=ListOfFTOL;
	pFTOL* pLast=pThis;

	while( pThis )
	{
		pLast=pThis;
		if( 0==memcmp( Heap, pThis->Heap, sizeof(Heap) ) )
			return;
		pThis=pThis->pNext;
	}

	pThis=(pFTOL*)gos_Malloc( sizeof(pFTOL) );

	memcpy( pThis->Heap, Heap, sizeof(Heap) );
	pThis->pNext=0;

	FoundFTOL++;
	
	if( pLast )
	{
		pLast->pNext=pThis;
	}
	else
	{
		ListOfFTOL=pThis;
	}
}

//
// At the end of a frame where _ftol() data has been collected - SPEW it out
//
void ShowFTOL()
{
//
// Destroy any FTOL list
//
	SPEW(( 0,"Found %d unique occurrences of _ftol() in current frame", FoundFTOL ));
	SPEW(( 0," " ));

	while( ListOfFTOL )
	{
		pFTOL* pThis=ListOfFTOL;
		ListOfFTOL=ListOfFTOL->pNext;

		for( int t0=0; t0<5; t0++ )
		{
			if( pThis->Heap[t0] )
				SPEW(( 0, DecodeAddress( pThis->Heap[t0], false ) ));
		}

		SPEW(( 0," " ));

		gos_Free( pThis );
	}

	FoundFTOL=0;
}











//
//
// Override float to long, so we can count how many are being executed
//
//
#ifdef _DEBUG
extern "C" long _cdecl _ftol(float arg)
{
//
// Count number that occur
//
	NumberFTOL++;
//
// Is the debugger keeping a history?
//
	if( gShowFTOLUseage==1 )
	{
		DebugFTOL();
	}
//
// Generate the actual float to long
//
	double temp;
	int result;
	__asm
	{
		fld st(0)
		fistp QWORD PTR temp
		fild QWORD PTR temp
		mov edx,DWORD PTR temp+4
		mov eax,DWORD PTR temp
		test eax,eax
		je maybe_zero
not_zero:
		fsubp st(1),st
		test edx,edx
		jns positive

		fstp DWORD PTR temp
		mov ecx,DWORD PTR temp
		xor ecx,80000000h
		add ecx,7FFFFFFFh
		adc eax,0
		mov result,eax
		jmp end
positive:
		fstp DWORD PTR temp
		mov ecx,DWORD PTR temp
		add ecx,7FFFFFFFh
		sbb eax,0
		mov result,eax
		jmp end
maybe_zero:
		test edx,7FFFFFFFh
		jnz not_zero
		fstp st(0)
		fstp st(0)
		mov result,eax
end:
	}
	return result;
}
#endif


//
// This function enables or disables the math exception handler
//
// Default precision mode is 53  bits.
// Single precision mode is 24 bits.
//
// When exceptions are enabled, INVALID, ZERODIVIDE cause exceptions
//
void __stdcall gos_MathExceptions( bool EnableExceptions, bool SinglePrecision )
{
	#ifndef LAB_ONLY
	EnableExceptions=0;
	#endif
	MathExceptions=EnableExceptions;
	MathSinglePrecision=SinglePrecision;

	FPUControl=0x27f;						// 2f2 allows overflow as well
	
	if( 0==gNoFPU )
	{
		if( EnableExceptions )
			FPUControl&=0xfffa;					// fa = INVALID+ZERODIVIDE, fb=ZERODIVIDE only
		if( SinglePrecision )
			FPUControl&=0xfcff;
	}

	_asm fninit;							// Initialize floating point registers
	_asm fldcw FPUControl;					// Load new precision and exception mask
}
//
// Returns the current state of exceptions and precision
//
void __stdcall gos_GetMathExceptions( bool* Exceptions, bool* SinglePrecision )
{
	if( Exceptions )
		*Exceptions=MathExceptions;
	if( SinglePrecision )
		*SinglePrecision=MathSinglePrecision;
}





//
// Called often (on SPEW) to check math mode still valid
//
void CheckPrecision()
{
	volatile WORD temp;
	_asm fstcw temp;
	if( !ProcessingError && MathExceptions && ((temp&0xff)!=0x7a) )			// 72 for overflow
		STOP(( "Math exceptions have been reset" ));
	if( !ProcessingError && MathSinglePrecision && ((temp&0xff00)!=0x0000) )
		STOP(( "Math precision has been reset" ));
}




//
// Any exceptions generated in the program come here.
//
long _stdcall ProcessException( EXCEPTION_POINTERS* ep )
{
#ifdef _DEBUG
    static bool s_bContinue = false;

    if (s_bContinue)
        return EXCEPTION_CONTINUE_EXECUTION;
#endif
	SavedExceptRec=*ep->ExceptionRecord;	// Get exception details
	SavedContext=*ep->ContextRecord;
	AllowFail=0;
	int result=ErrorHandler( gos_ErrorException+gos_ErrorAppendRoutine+(SavedExceptRec.ExceptionCode==EXCEPTION_GUARD_PAGE ? 0 : gos_ErrorNoContinue), GetExceptionCode(&SavedExceptRec) );
	return (SavedExceptRec.ExceptionCode==EXCEPTION_GUARD_PAGE && !result) ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_CONTINUE_SEARCH;		// Go onto Visual C++'s exception handler
}



//
// This functions allows STOP to take multiple parameters
//
int __cdecl InternalFunctionStop( const char *Message, ... )
{
	char ErrorBuffer[256];
	_vsnprintf( ErrorBuffer, sizeof(ErrorBuffer), Message, (char*)(&Message+1) );
	ErrorBuffer[sizeof(ErrorBuffer)-1]=0;
	if( DisableErrors )							// To stop nested exceptions, turn STOP into an exception
		*(BYTE*)0=0;
	return ErrorHandler(gos_ErrorStop+gos_ErrorNoContinue+gos_ErrorNoRegisters+gos_ErrorPop2,ErrorBuffer);
}



//
// This functions allows PAUSE to take multiple parameters
//
int __cdecl InternalFunctionPause( const char *Message, ... )
{
	char ErrorBuffer[256];
	_vsnprintf( ErrorBuffer, sizeof(ErrorBuffer), Message, (char*)(&Message+1) );
	ErrorBuffer[sizeof(ErrorBuffer)-1]=0;
	if( DisableErrors )							// To stop nested exceptions, turn PAUSE into an exception
		*(BYTE*)0=0;
	return ErrorHandler(gos_ErrorStop+gos_ErrorNoRegisters+gos_ErrorPop2,ErrorBuffer);
}



void DestroyExceptions()
{
//
// Destroy ignore list
//
	IgnoreAddress* pThis=pIgnore;
	IgnoreAddress* pNext;
	while( pThis )
	{
		pNext=pThis->pNext;
		gos_Free(pThis);
		pThis=pNext;
	}
	pIgnore=0;
//
// Destroy any FTOL list
//
	if( ListOfFTOL )
	{
		while( ListOfFTOL )
		{
			pFTOL* pThis=ListOfFTOL;
			ListOfFTOL=ListOfFTOL->pNext;
			gos_Free( pThis );
		}
		gShowFTOLUseage=0;
	}
}




