2017-01-19 16:23:59 -05:00
# include "STDInclude.hpp"
namespace Components
{
Utils : : Hook Exception : : SetFilterHook ;
2017-02-06 15:51:46 -05:00
int Exception : : MiniDumpType ;
2017-01-19 16:23:59 -05:00
__declspec ( noreturn ) void Exception : : ErrorLongJmp ( jmp_buf _Buf , int _Value )
{
if ( ! * reinterpret_cast < DWORD * > ( 0x1AD7EB4 ) )
{
TerminateProcess ( GetCurrentProcess ( ) , 1337 ) ;
}
longjmp ( _Buf , _Value ) ;
}
2017-02-14 05:38:24 -05:00
__declspec ( noreturn ) void Exception : : LongJmp ( jmp_buf _Buf , int _Value )
{
AssetHandler : : ResetBypassState ( ) ;
longjmp ( _Buf , _Value ) ;
}
2017-02-10 03:50:08 -05:00
void Exception : : SuspendProcess ( )
{
FreeConsole ( ) ;
2017-02-10 13:45:31 -05:00
if ( IsWindow ( Window : : GetWindow ( ) ) ! = FALSE )
2017-02-10 03:50:08 -05:00
{
2017-02-10 13:45:31 -05:00
CloseWindow ( Window : : GetWindow ( ) ) ;
DestroyWindow ( Window : : GetWindow ( ) ) ;
std : : this_thread : : sleep_for ( 2 s ) ;
// This makes sure we either destroy the windows or wait till they are destroyed
MSG msg ;
while ( IsWindow ( Window : : GetWindow ( ) ) ! = FALSE & & GetMessage ( & msg , nullptr , NULL , NULL ) )
{
TranslateMessage ( & msg ) ;
DispatchMessage ( & msg ) ;
}
2017-02-10 03:50:08 -05:00
}
// This only suspends the main game threads, which is enough for us
Game : : Sys_SuspendOtherThreads ( ) ;
}
2017-01-19 16:23:59 -05:00
LONG WINAPI Exception : : ExceptionFilter ( LPEXCEPTION_POINTERS ExceptionInfo )
{
// Pass on harmless errors
if ( ExceptionInfo - > ExceptionRecord - > ExceptionCode = = STATUS_INTEGER_OVERFLOW | |
ExceptionInfo - > ExceptionRecord - > ExceptionCode = = STATUS_FLOAT_OVERFLOW )
{
return EXCEPTION_CONTINUE_EXECUTION ;
}
2017-02-09 16:16:49 -05:00
const char * errorStr ;
if ( ExceptionInfo - > ExceptionRecord - > ExceptionCode = = EXCEPTION_STACK_OVERFLOW )
{
errorStr = " Termination because of a stack overflow. " ;
}
else
{
errorStr = Utils : : String : : VA ( " Fatal error (0x%08X) at 0x%08X. " , ExceptionInfo - > ExceptionRecord - > ExceptionCode , ExceptionInfo - > ExceptionRecord - > ExceptionAddress ) ;
}
2017-02-10 03:50:08 -05:00
Exception : : SuspendProcess ( ) ;
2017-02-09 16:16:49 -05:00
bool doFullDump = Flags : : HasFlag ( " bigdumps " ) | | Flags : : HasFlag ( " reallybigdumps " ) ;
2017-02-14 14:00:29 -05:00
/*if (!doFullDump)
2017-02-09 16:16:49 -05:00
{
2017-02-10 03:50:08 -05:00
if ( MessageBoxA ( nullptr ,
2017-02-10 15:46:32 -05:00
Utils : : String : : VA ( " %s \n \n " // errorStr
" Would you like to create a full crash dump for the developers (this can be 100mb or more)? \n No will create small dumps that are automatically uploaded. " , errorStr ) ,
2017-02-09 16:16:49 -05:00
" IW4x Error! " , MB_YESNO | MB_ICONERROR ) = = IDYES )
{
doFullDump = true ;
}
2017-02-14 14:00:29 -05:00
} */
2017-02-09 16:16:49 -05:00
2017-02-15 07:27:29 -05:00
MessageBoxA ( nullptr , errorStr , " ERROR " , MB_ICONERROR ) ;
2017-02-09 16:16:49 -05:00
if ( doFullDump )
{
Exception : : SetMiniDumpType ( true , false ) ;
}
2017-02-06 15:51:46 -05:00
auto minidump = MinidumpUpload : : CreateQueuedMinidump ( ExceptionInfo , Exception : : MiniDumpType ) ;
2017-01-19 16:23:59 -05:00
if ( ! minidump )
{
2017-02-20 15:30:59 -05:00
MessageBoxA ( nullptr , Utils : : String : : VA ( " There was an error creating the minidump (%s)! Hit OK to close the program. " , Utils : : GetLastWindowsError ( ) . data ( ) ) , " Minidump Error " , MB_OK | MB_ICONERROR ) ;
2017-01-19 16:23:59 -05:00
OutputDebugStringA ( " Failed to create new minidump! " ) ;
Utils : : OutputDebugLastError ( ) ;
2017-02-09 16:16:49 -05:00
TerminateProcess ( GetCurrentProcess ( ) , ExceptionInfo - > ExceptionRecord - > ExceptionCode ) ;
2017-01-19 16:23:59 -05:00
}
else
{
delete minidump ;
}
2017-02-09 16:16:49 -05:00
if ( ExceptionInfo - > ExceptionRecord - > ExceptionFlags = = EXCEPTION_NONCONTINUABLE )
2017-01-19 16:23:59 -05:00
{
2017-02-09 16:16:49 -05:00
TerminateProcess ( GetCurrentProcess ( ) , ExceptionInfo - > ExceptionRecord - > ExceptionCode ) ;
2017-01-19 16:23:59 -05:00
}
return EXCEPTION_CONTINUE_SEARCH ;
}
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI Exception : : SetUnhandledExceptionFilterStub ( LPTOP_LEVEL_EXCEPTION_FILTER )
{
Exception : : SetFilterHook . uninstall ( ) ;
LPTOP_LEVEL_EXCEPTION_FILTER retval = SetUnhandledExceptionFilter ( & Exception : : ExceptionFilter ) ;
Exception : : SetFilterHook . install ( ) ;
return retval ;
}
LPTOP_LEVEL_EXCEPTION_FILTER Exception : : Hook ( )
{
return SetUnhandledExceptionFilter ( & Exception : : ExceptionFilter ) ;
}
2017-02-09 16:16:49 -05:00
void Exception : : SetMiniDumpType ( bool codeseg , bool dataseg )
2017-02-06 15:51:46 -05:00
{
Exception : : MiniDumpType = MiniDumpIgnoreInaccessibleMemory ;
Exception : : MiniDumpType | = MiniDumpWithHandleData ;
2017-02-09 16:16:49 -05:00
Exception : : MiniDumpType | = MiniDumpScanMemory ;
2017-02-06 15:51:46 -05:00
Exception : : MiniDumpType | = MiniDumpWithProcessThreadData ;
2017-02-09 16:16:49 -05:00
Exception : : MiniDumpType | = MiniDumpWithFullMemoryInfo ;
Exception : : MiniDumpType | = MiniDumpWithThreadInfo ;
//Exception::MiniDumpType |= MiniDumpWithModuleHeaders;
2017-02-06 15:51:46 -05:00
2017-02-09 16:16:49 -05:00
if ( codeseg )
2017-02-06 15:51:46 -05:00
{
Exception : : MiniDumpType | = MiniDumpWithCodeSegs ;
}
2017-02-09 16:16:49 -05:00
if ( dataseg )
2017-02-06 15:51:46 -05:00
{
Exception : : MiniDumpType | = MiniDumpWithDataSegs ;
}
}
2017-01-19 16:23:59 -05:00
Exception : : Exception ( )
{
2017-02-09 16:16:49 -05:00
Exception : : SetMiniDumpType ( Flags : : HasFlag ( " bigminidumps " ) , Flags : : HasFlag ( " reallybigminidumps " ) ) ;
2017-02-06 15:51:46 -05:00
2017-01-19 16:23:59 -05:00
# ifdef DEBUG
// Display DEBUG branding, so we know we're on a debug build
Renderer : : OnFrame ( [ ] ( )
{
Game : : Font * font = Game : : R_RegisterFont ( " fonts/normalFont " ) ;
float color [ 4 ] = { 1.0f , 1.0f , 1.0f , 1.0f } ;
// Change the color when attaching a debugger
if ( IsDebuggerPresent ( ) )
{
color [ 0 ] = 0.6588f ;
color [ 1 ] = 1.0000f ;
color [ 2 ] = 0.0000f ;
}
Game : : R_AddCmdDrawText ( " DEBUG-BUILD " , 0x7FFFFFFF , font , 15.0f , 10.0f + Game : : R_TextHeight ( font ) , 1.0f , 1.0f , 0.0f , color , Game : : ITEM_TEXTSTYLE_SHADOWED ) ;
} ) ;
# endif
# if !defined(DEBUG) || defined(FORCE_EXCEPTION_HANDLER)
Exception : : SetFilterHook . initialize ( SetUnhandledExceptionFilter , Exception : : SetUnhandledExceptionFilterStub , HOOK_JUMP ) ;
Exception : : SetFilterHook . install ( ) ;
SetUnhandledExceptionFilter ( & Exception : : ExceptionFilter ) ;
# endif
//Utils::Hook(0x4B241F, Exception::ErrorLongJmp, HOOK_CALL).install()->quick();
2017-02-14 05:38:24 -05:00
Utils : : Hook ( 0x6B8898 , Exception : : LongJmp , HOOK_JUMP ) . install ( ) - > quick ( ) ;
2017-01-19 16:23:59 -05:00
Command : : Add ( " mapTest " , [ ] ( Command : : Params * params )
{
Game : : UI_UpdateArenas ( ) ;
std : : string command ;
for ( int i = 0 ; i < ( params - > length ( ) > = 2 ? atoi ( params - > get ( 1 ) ) : * Game : : arenaCount ) ; + + i )
{
char * mapname = ArenaLength : : NewArenas [ i % * Game : : arenaCount ] . mapName ;
if ( ! ( i % 2 ) ) command . append ( Utils : : String : : VA ( " wait 250;disconnect;wait 750; " , mapname ) ) ; // Test a disconnect
else command . append ( Utils : : String : : VA ( " wait 500; " , mapname ) ) ; // Test direct map switch
command . append ( Utils : : String : : VA ( " map %s; " , mapname ) ) ;
}
Command : : Execute ( command , false ) ;
} ) ;
Command : : Add ( " debug_exceptionhandler " , [ ] ( Command : : Params * )
{
Logger : : Print ( " Rerunning SetUnhandledExceptionHandler... \n " ) ;
auto oldHandler = Exception : : Hook ( ) ;
Logger : : Print ( " Old exception handler was 0x%010X. \n " , oldHandler ) ;
} ) ;
# pragma warning(push)
# pragma warning(disable:4740) // flow in or out of inline asm code suppresses global optimization
Command : : Add ( " debug_minidump " , [ ] ( Command : : Params * )
{
// The following code was taken from VC++ 8.0 CRT (invarg.c: line 104)
CONTEXT ContextRecord ;
EXCEPTION_RECORD ExceptionRecord ;
ZeroMemory ( & ContextRecord , sizeof ( CONTEXT ) ) ;
__asm
{
mov [ ContextRecord . Eax ] , eax
mov [ ContextRecord . Ecx ] , ecx
mov [ ContextRecord . Edx ] , edx
mov [ ContextRecord . Ebx ] , ebx
mov [ ContextRecord . Esi ] , esi
mov [ ContextRecord . Edi ] , edi
mov word ptr [ ContextRecord . SegSs ] , ss
mov word ptr [ ContextRecord . SegCs ] , cs
mov word ptr [ ContextRecord . SegDs ] , ds
mov word ptr [ ContextRecord . SegEs ] , es
mov word ptr [ ContextRecord . SegFs ] , fs
mov word ptr [ ContextRecord . SegGs ] , gs
pushfd
pop [ ContextRecord . EFlags ]
}
ContextRecord . ContextFlags = CONTEXT_CONTROL ;
ContextRecord . Eip = reinterpret_cast < DWORD > ( _ReturnAddress ( ) ) ;
ContextRecord . Esp = reinterpret_cast < DWORD > ( _AddressOfReturnAddress ( ) ) ;
ContextRecord . Ebp = * reinterpret_cast < DWORD * > ( _AddressOfReturnAddress ( ) ) - 1 ;
ZeroMemory ( & ExceptionRecord , sizeof ( EXCEPTION_RECORD ) ) ;
ExceptionRecord . ExceptionCode = EXCEPTION_BREAKPOINT ;
ExceptionRecord . ExceptionAddress = _ReturnAddress ( ) ;
EXCEPTION_POINTERS eptr ;
eptr . ExceptionRecord = & ExceptionRecord ;
eptr . ContextRecord = & ContextRecord ;
Exception : : ExceptionFilter ( & eptr ) ;
} ) ;
# pragma warning(pop)
}
Exception : : ~ Exception ( )
{
Exception : : SetFilterHook . uninstall ( ) ;
}
}