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-06-14 06:06:04 -04:00
if ( IsWindow ( Console : : GetWindow ( ) ) ! = FALSE )
2017-04-27 05:14:44 -04:00
{
CloseWindow ( Console : : GetWindow ( ) ) ;
DestroyWindow ( Console : : GetWindow ( ) ) ;
}
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 ;
2017-04-27 05:14:44 -04:00
Utils : : Time : : Interval interval ;
while ( IsWindow ( Window : : GetWindow ( ) ) ! = FALSE & & ! interval . elapsed ( 2 s ) )
2017-02-10 13:45:31 -05:00
{
2022-02-10 07:14:43 -05:00
if ( PeekMessageA ( & msg , nullptr , NULL , NULL , PM_REMOVE ) )
2017-04-27 05:14:44 -04:00
{
TranslateMessage ( & msg ) ;
2022-02-10 07:14:43 -05:00
DispatchMessageA ( & msg ) ;
2017-04-27 05:14:44 -04:00
}
std : : this_thread : : sleep_for ( 10 ms ) ;
2017-02-10 13:45:31 -05:00
}
2017-02-10 03:50:08 -05:00
}
// This only suspends the main game threads, which is enough for us
Game : : Sys_SuspendOtherThreads ( ) ;
}
2022-02-10 07:14:43 -05:00
void Exception : : CopyMessageToClipboard ( const std : : string & error )
{
const auto hWndNewOwner = GetDesktopWindow ( ) ;
const auto result = OpenClipboard ( hWndNewOwner ) ;
if ( result = = FALSE )
return ;
EmptyClipboard ( ) ;
auto * hMem = GlobalAlloc ( GMEM_MOVEABLE , error . size ( ) + 1 ) ;
2022-02-10 09:23:53 -05:00
if ( hMem = = nullptr )
2022-02-10 07:14:43 -05:00
{
2022-02-10 09:23:53 -05:00
CloseClipboard ( ) ;
return ;
}
auto lock = GlobalLock ( hMem ) ;
if ( lock ! = nullptr )
{
std : : memcpy ( lock , error . data ( ) , error . size ( ) + 1 ) ;
GlobalUnlock ( hMem ) ;
SetClipboardData ( 1 , hMem ) ;
2022-02-10 07:14:43 -05:00
}
CloseClipboard ( ) ;
2022-02-10 09:23:53 -05:00
GlobalFree ( hMem ) ;
2022-02-10 07:14:43 -05:00
}
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-05-02 07:37:41 -04:00
std : : string errorStr ;
2017-02-09 16:16:49 -05:00
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 ) ;
}
2019-12-25 20:16:43 -05:00
//Exception::SuspendProcess();
2017-02-10 03:50:08 -05:00
2022-02-10 09:23:53 -05:00
Exception : : CopyMessageToClipboard ( Utils : : String : : VA ( " 0x%08X " , ExceptionInfo - > ExceptionRecord - > ExceptionAddress ) ) ;
2017-05-02 07:37:41 -04:00
MessageBoxA ( nullptr , errorStr . data ( ) , " ERROR " , MB_ICONERROR ) ;
2017-02-15 07:27:29 -05:00
2022-02-10 09:23:53 -05:00
if ( Flags : : HasFlag ( " bigminidumps " ) )
2017-02-09 16:16:49 -05:00
{
Exception : : SetMiniDumpType ( true , false ) ;
}
2017-03-13 16:50:42 -04:00
// Current executable name
char exeFileName [ MAX_PATH ] ;
GetModuleFileNameA ( nullptr , exeFileName , MAX_PATH ) ;
PathStripPathA ( exeFileName ) ;
PathRemoveExtensionA ( exeFileName ) ;
// Generate filename
char filenameFriendlyTime [ MAX_PATH ] ;
__time64_t time ;
tm ltime ;
_time64 ( & time ) ;
_localtime64_s ( & ltime , & time ) ;
strftime ( filenameFriendlyTime , sizeof ( filenameFriendlyTime ) - 1 , " %Y%m%d%H%M%S " , & ltime ) ;
// Combine with queuedMinidumpsFolder
char filename [ MAX_PATH ] = { 0 } ;
2017-03-18 19:23:20 -04:00
Utils : : IO : : CreateDir ( " minidumps " ) ;
2017-03-13 16:50:42 -04:00
PathCombineA ( filename , " minidumps \\ " , Utils : : String : : VA ( " %s- " VERSION " -%s.dmp " , exeFileName , filenameFriendlyTime ) ) ;
2017-07-02 07:41:31 -04:00
# ifndef DISABLE_ANTICHEAT
AntiCheat : : UninstallLibHook ( ) ;
# endif
2017-03-13 16:50:42 -04:00
DWORD fileShare = FILE_SHARE_READ | FILE_SHARE_WRITE ;
HANDLE hFile = CreateFileA ( filename , GENERIC_WRITE | GENERIC_READ , fileShare , nullptr , ( fileShare & FILE_SHARE_WRITE ) > 0 ? OPEN_ALWAYS : OPEN_EXISTING , NULL , nullptr ) ;
MINIDUMP_EXCEPTION_INFORMATION ex = { GetCurrentThreadId ( ) , ExceptionInfo , FALSE } ;
2017-03-18 19:23:20 -04:00
if ( ! MiniDumpWriteDump ( GetCurrentProcess ( ) , GetCurrentProcessId ( ) , hFile , static_cast < MINIDUMP_TYPE > ( Exception : : MiniDumpType ) , & ex , nullptr , nullptr ) )
2017-01-19 16:23:59 -05:00
{
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
}
2019-12-25 20:16:43 -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
}
2019-12-25 20:16:43 -05:00
# ifndef DISABLE_ANTICHEAT
AntiCheat : : InstallLibHook ( ) ;
# endif
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
2017-06-01 04:25:13 -04:00
Scheduler : : OnFrame ( [ ] ( )
{
2018-05-09 08:33:52 -04:00
Game : : Font_s * font = Game : : R_RegisterFont ( " fonts/normalFont " , 0 ) ;
2017-06-01 04:25:13 -04:00
float color [ 4 ] = { 1.0f , 1.0f , 1.0f , 1.0f } ;
2017-01-19 16:23:59 -05:00
2017-06-01 04:25:13 -04:00
// Change the color when attaching a debugger
if ( IsDebuggerPresent ( ) )
{
color [ 0 ] = 0.6588f ;
color [ 1 ] = 1.0000f ;
color [ 2 ] = 0.0000f ;
}
2017-05-31 20:13:39 -04:00
2017-06-01 04:25:13 -04:00
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 ) ;
} , true ) ;
2017-01-19 16:23:59 -05:00
# 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 ) ;
} ) ;
2017-06-14 06:06:04 -04:00
Command : : Add ( " debug_exceptionhandler " , [ ] ( Command : : Params * )
2017-01-19 16:23:59 -05:00
{
Logger : : Print ( " Rerunning SetUnhandledExceptionHandler... \n " ) ;
auto oldHandler = Exception : : Hook ( ) ;
Logger : : Print ( " Old exception handler was 0x%010X. \n " , oldHandler ) ;
} ) ;
}
Exception : : ~ Exception ( )
{
Exception : : SetFilterHook . uninstall ( ) ;
}
}