2022-02-27 07:53:44 -05:00
# include <STDInclude.hpp>
2022-12-26 07:07:24 -05:00
# include "Console.hpp"
2017-01-19 16:23:59 -05:00
2022-11-26 12:38:34 -05:00
# include <version.hpp>
2017-01-19 16:23:59 -05:00
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
2022-12-25 12:23:53 -05:00
__declspec ( noreturn ) void Exception : : LongJmp_Internal_Stub ( jmp_buf env , int status )
2017-02-14 05:38:24 -05:00
{
AssetHandler : : ResetBypassState ( ) ;
2022-12-25 12:23:53 -05:00
Game : : longjmp_internal ( env , status ) ;
2017-02-14 05:38:24 -05:00
}
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 ;
}
2022-02-10 09:41:51 -05:00
2022-12-25 12:23:53 -05:00
auto * lock = GlobalLock ( hMem ) ;
2022-02-10 09:23:53 -05:00
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 )
{
2022-02-13 19:35:59 -05:00
errorStr = " Termination because of a stack overflow. \n Copy exception address to clipboard? " ;
2017-02-09 16:16:49 -05:00
}
else
{
2022-02-13 19:35:59 -05:00
errorStr = Utils : : String : : VA ( " Fatal error (0x%08X) at 0x%08X. \n Copy exception address to clipboard? " , ExceptionInfo - > ExceptionRecord - > ExceptionCode , ExceptionInfo - > ExceptionRecord - > ExceptionAddress ) ;
2017-02-09 16:16:49 -05:00
}
2022-02-10 09:41:51 -05:00
// Message should be copied to the keyboard if no button is pressed
if ( MessageBoxA ( nullptr , errorStr . data ( ) , nullptr , MB_YESNO | MB_ICONERROR ) = = IDYES )
{
2022-12-25 12:23:53 -05:00
CopyMessageToClipboard ( Utils : : String : : VA ( " 0x%08X " , ExceptionInfo - > ExceptionRecord - > ExceptionAddress ) ) ;
2022-02-10 09:41:51 -05:00
}
2017-02-15 07:27:29 -05:00
2022-02-10 09:41:51 -05:00
if ( Flags : : HasFlag ( " bigminidumps " ) )
2017-02-09 16:16:49 -05:00
{
2022-12-25 12:23:53 -05:00
SetMiniDumpType ( true , false ) ;
2017-02-09 16:16:49 -05:00
}
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
2022-12-27 04:40:54 -05:00
char filenameFriendlyTime [ MAX_PATH ] { } ;
2017-03-13 16:50:42 -04:00
__time64_t time ;
tm ltime ;
_time64 ( & time ) ;
_localtime64_s ( & ltime , & time ) ;
strftime ( filenameFriendlyTime , sizeof ( filenameFriendlyTime ) - 1 , " %Y%m%d%H%M%S " , & ltime ) ;
2022-12-25 12:23:53 -05:00
// Combine with queued MinidumpsFolder
char filename [ MAX_PATH ] { } ;
CreateDirectoryA ( " minidumps " , nullptr ) ;
2017-03-13 16:50:42 -04:00
PathCombineA ( filename , " minidumps \\ " , Utils : : String : : VA ( " %s- " VERSION " -%s.dmp " , exeFileName , filenameFriendlyTime ) ) ;
2022-12-25 12:23:53 -05:00
constexpr auto fileShare = FILE_SHARE_READ | FILE_SHARE_WRITE ;
2017-03-13 16:50:42 -04:00
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 } ;
2022-12-25 12:23:53 -05:00
if ( ! MiniDumpWriteDump ( GetCurrentProcess ( ) , GetCurrentProcessId ( ) , hFile , static_cast < MINIDUMP_TYPE > ( MiniDumpType ) , & ex , nullptr , nullptr ) )
2017-01-19 16:23:59 -05:00
{
2022-12-25 12:23:53 -05:00
MessageBoxA ( nullptr , Utils : : String : : Format ( " There was an error creating the minidump ({})! Hit OK to close the program. " , Utils : : GetLastWindowsError ( ) ) , " ERROR " , MB_OK | MB_ICONERROR ) ;
2023-01-25 13:59:15 -05:00
# ifdef _DEBUG
2017-01-19 16:23:59 -05:00
OutputDebugStringA ( " Failed to create new minidump! " ) ;
2023-01-25 13:59:15 -05:00
# endif
2017-01-19 16:23:59 -05:00
Utils : : OutputDebugLastError ( ) ;
2017-02-09 16:16:49 -05:00
TerminateProcess ( GetCurrentProcess ( ) , ExceptionInfo - > ExceptionRecord - > ExceptionCode ) ;
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 ;
}
2017-02-09 16:16:49 -05:00
void Exception : : SetMiniDumpType ( bool codeseg , bool dataseg )
2017-02-06 15:51:46 -05:00
{
2022-12-25 12:23:53 -05:00
MiniDumpType = MiniDumpIgnoreInaccessibleMemory ;
MiniDumpType | = MiniDumpWithHandleData ;
MiniDumpType | = MiniDumpScanMemory ;
MiniDumpType | = MiniDumpWithProcessThreadData ;
MiniDumpType | = MiniDumpWithFullMemoryInfo ;
MiniDumpType | = MiniDumpWithThreadInfo ;
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
{
2022-12-25 12:23:53 -05:00
MiniDumpType | = MiniDumpWithCodeSegs ;
2017-02-06 15:51:46 -05:00
}
2022-12-25 12:23:53 -05:00
2017-02-09 16:16:49 -05:00
if ( dataseg )
2017-02-06 15:51:46 -05:00
{
2022-12-25 12:23:53 -05:00
MiniDumpType | = MiniDumpWithDataSegs ;
2017-02-06 15:51:46 -05:00
}
}
2022-12-27 04:40:54 -05:00
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI Exception : : SetUnhandledExceptionFilter_Stub ( LPTOP_LEVEL_EXCEPTION_FILTER )
{
SetFilterHook . uninstall ( ) ;
LPTOP_LEVEL_EXCEPTION_FILTER result = : : SetUnhandledExceptionFilter ( & ExceptionFilter ) ;
SetFilterHook . install ( ) ;
return result ;
}
2017-01-19 16:23:59 -05:00
Exception : : Exception ( )
{
2022-12-25 12:23:53 -05:00
SetMiniDumpType ( Flags : : HasFlag ( " bigminidumps " ) , Flags : : HasFlag ( " reallybigminidumps " ) ) ;
2017-01-19 16:23:59 -05:00
2022-12-27 04:40:54 -05:00
SetFilterHook . initialize ( : : SetUnhandledExceptionFilter , SetUnhandledExceptionFilter_Stub , HOOK_JUMP ) ;
SetFilterHook . install ( ) ;
: : SetUnhandledExceptionFilter ( & ExceptionFilter ) ;
2017-01-19 16:23:59 -05:00
2022-12-25 12:23:53 -05:00
Utils : : Hook ( 0x4B241F , LongJmp_Internal_Stub , HOOK_CALL ) . install ( ) - > quick ( ) ;
Utils : : Hook ( 0x61DB44 , LongJmp_Internal_Stub , HOOK_CALL ) . install ( ) - > quick ( ) ;
Utils : : Hook ( 0x61F17D , LongJmp_Internal_Stub , HOOK_CALL ) . install ( ) - > quick ( ) ;
Utils : : Hook ( 0x61F248 , LongJmp_Internal_Stub , HOOK_CALL ) . install ( ) - > quick ( ) ;
Utils : : Hook ( 0x61F5E7 , LongJmp_Internal_Stub , HOOK_CALL ) . install ( ) - > quick ( ) ;
2017-01-19 16:23:59 -05:00
2022-12-25 12:23:53 -05:00
# ifdef MAP_TEST
2017-01-19 16:23:59 -05:00
Command : : Add ( " mapTest " , [ ] ( Command : : Params * params )
2022-02-10 10:00:45 -05:00
{
Game : : UI_UpdateArenas ( ) ;
2017-01-19 16:23:59 -05:00
2022-02-10 10:00:45 -05:00
std : : string command ;
2022-03-17 14:50:20 -04:00
for ( auto i = 0 ; i < ( params - > size ( ) > = 2 ? atoi ( params - > get ( 1 ) ) : * Game : : arenaCount ) ; + + i )
2022-02-10 10:00:45 -05:00
{
2022-06-12 17:07:53 -04:00
const auto * mapName = ArenaLength : : NewArenas [ i % * Game : : arenaCount ] . mapName ;
2017-01-19 16:23:59 -05:00
2022-02-10 14:45:00 -05:00
if ( ! ( i % 2 ) ) command . append ( " wait 250;disconnect;wait 750; " ) ; // Test a disconnect
2022-02-10 10:00:45 -05:00
else command . append ( " wait 500; " ) ; // Test direct map switch
2022-06-12 17:07:53 -04:00
command . append ( Utils : : String : : VA ( " map %s; " , mapName ) ) ;
2022-02-10 10:00:45 -05:00
}
2022-02-10 09:41:51 -05:00
2022-02-10 10:00:45 -05:00
Command : : Execute ( command , false ) ;
} ) ;
2022-06-12 17:07:53 -04:00
# endif
2017-01-19 16:23:59 -05:00
}
Exception : : ~ Exception ( )
{
2022-12-25 12:23:53 -05:00
SetFilterHook . uninstall ( ) ;
2017-01-19 16:23:59 -05:00
}
}