From 1e6cc26f0fc566ec864412f35950db34640318cc Mon Sep 17 00:00:00 2001 From: hgn Date: Fri, 4 Jul 2025 21:18:43 +0100 Subject: [PATCH] Crash report stuff --- src/vgcrashreport.c | 281 ++++++++++++++++++++++++++++++++++++++++++++ vg_engine.c | 52 ++++++-- vg_log.c | 24 ++-- vg_log.h | 1 + vg_string.c | 6 +- 5 files changed, 336 insertions(+), 28 deletions(-) create mode 100644 src/vgcrashreport.c diff --git a/src/vgcrashreport.c b/src/vgcrashreport.c new file mode 100644 index 0000000..9fbd507 --- /dev/null +++ b/src/vgcrashreport.c @@ -0,0 +1,281 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "vg_opt.h" +#include "vg_platform.h" +#include "vg_string.h" +#include "vg_io.h" + +#include "vg_tool.c" + +char *report_text = NULL; +u32 report_length = 0; + +HWND window = NULL, + send_button = NULL, + select_button = NULL, + close_button = NULL, + textbox = NULL; + +int sent_report = 0; + +WNDPROC defWndProc = NULL; + +LRESULT OnWindowClose( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) +{ + PostQuitMessage(0); + return CallWindowProc(defWndProc, hwnd, message, wParam, lParam); +} + +#define HTTPBOUND "99667vg.mtzero.boundary88337" + +void send_report(void) +{ + HINTERNET hSession = InternetOpen( + "Mozilla/5.0", // User-Agent + INTERNET_OPEN_TYPE_PRECONFIG, + NULL, + NULL, + 0); + + if( hSession == NULL ) + { + MessageBox( window, "Call failed.", + "InternetOpen (wininet)", MB_OK|MB_ICONERROR); + goto e4; + } + + HINTERNET hConnect = InternetConnect( + hSession, + "skaterift.com", // HOST + INTERNET_DEFAULT_HTTPS_PORT, + "", + "", + INTERNET_SERVICE_HTTP, + 0, + 0); + + if( hConnect == NULL ) + { + MessageBox( window, "Call failed.", + "InternetConnect (wininet)", MB_OK|MB_ICONERROR); + goto e3; + } + + HINTERNET hHttpFile = HttpOpenRequest( + hConnect, + "POST", // METHOD + "/vgreport/index.php", // URI + NULL, + NULL, + NULL, + INTERNET_FLAG_SECURE|INTERNET_FLAG_RELOAD, + 0); + if( hHttpFile == NULL ) + { + MessageBox( window, "Call failed.", + "HttpOpenRequest (wininet)", MB_OK|MB_ICONERROR); + goto e2; + } + + HttpAddRequestHeaders( hHttpFile, "Content-Type: multipart/form-data; boundary=" HTTPBOUND "\r\nConnection: close\r\n", + -1, HTTP_ADDREQ_FLAG_ADD ); + vg_str content; + vg_strnull( &content, NULL, 0 ); + vg_strcat( &content, "--" HTTPBOUND "\r\nContent-Disposition: form-data; name=\"uploaded_file\"\r\n" ); + vg_strcat( &content, "Content-Type: text/plain\r\n\r\n" ); + vg_strcat( &content, report_text ); + vg_strcat( &content, "\r\n\r\n--" HTTPBOUND "--\r\n" ); + + while( !HttpSendRequest(hHttpFile, NULL, 0, content.buffer, content.i) ) + { + DWORD option = InternetErrorDlg( + window, + hHttpFile, + ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED, + FLAGS_ERROR_UI_FILTER_FOR_ERRORS | + FLAGS_ERROR_UI_FLAGS_GENERATE_DATA | + FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS, + NULL); + + if( option == ERROR_CANCELLED ) + goto e1; + } + + { + DWORD dwFileSize = 4096; + char* buffer[dwFileSize + 1]; + + while(1) + { + DWORD dwBytesRead; + BOOL bRead; + + bRead = InternetReadFile( + hHttpFile, + buffer, + dwFileSize + 1, + &dwBytesRead); + + if( dwBytesRead == 0 ) + break; + + if( !bRead ) + { + MessageBox( window, "Report may or may not have been sent", + "Error reading response", MB_OK|MB_ICONERROR); + goto e1; + } + else + buffer[dwBytesRead] = 0; + } + + sent_report = 1; + MessageBox( window, "Thank you, a developer will take a look and try to fix this bug.", "Report Sent!", MB_OK ); + } + +e1:InternetCloseHandle(hHttpFile); +e2:InternetCloseHandle(hConnect); +e3:InternetCloseHandle(hSession); +e4:return; +} + +LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) +{ + if (message == WM_CLOSE && hwnd == window) return OnWindowClose(hwnd, message, wParam, lParam); + if( message == WM_COMMAND && HIWORD(wParam) == BN_CLICKED && (HWND)lParam == close_button ) + { + exit(0); + } + if( message == WM_COMMAND && HIWORD(wParam) == BN_CLICKED && (HWND)lParam == send_button ) + { + if( sent_report ) + { + if( MessageBox( window, + "The more times you send it the faster the bug gets fixed!", + "Mail this report?", MB_YESNO|MB_APPLMODAL ) == IDYES ) + { + MessageBox( window, "Thank you, a developer will take a look and try to fix this bug.", "Report Sent!", MB_OK ); + } + } + else + { + if( MessageBox( window, + "Clicking yes confirms sending this error log straight to the developers (which we would much appreciate)", + "Mail this report?", MB_YESNO|MB_APPLMODAL ) == IDYES ) + { + send_report(); + } + } + } + if( message == WM_COMMAND && HIWORD(wParam) == BN_CLICKED && (HWND)lParam == select_button ) + { + SetFocus( textbox ); + Edit_SetSel( textbox, 0, 100000 ); + } + return CallWindowProc(defWndProc, hwnd, message, wParam, lParam); +} + + +int main( int argc, const char *argv[] ) +{ + if( argc < 3 ) + { + MessageBox( NULL, "Required parameters: PID and Crash path.", + "Usage error", MB_OK|MB_ICONERROR ); + return -1; + + } + vg_log_init(); + + DWORD pid = (DWORD)strtoul( argv[1], NULL, 16 ); + HANDLE hGameProcess = OpenProcess( PROCESS_QUERY_INFORMATION, 0, pid ); + if( hGameProcess == NULL ) + goto we_crashed; + + while(1) + { + sleep(2); + DWORD exit_code; + if( GetExitCodeProcess( hGameProcess, &exit_code ) ) + { + if( exit_code == STATUS_PENDING ) + continue; + else if( exit_code == 0 ) + return 0; + else + goto we_crashed; + } + } + +we_crashed: + report_text = vg_file_read( NULL, argv[2], &report_length, 1 ); + if( !report_text ) + { + MessageBox( NULL, "Can't even open the crash text file. Something has gone seriously wrong! Contact a developer.", + "Total epic failure!", MB_OK|MB_ICONERROR ); + return -1; + } + + int x = 10; + int y = 10; + int w = 800; + int h = 600; + int sh = 32; + int p = 8; + + RECT rect; + rect.left = x; + rect.top = y; + rect.right = x + w; + rect.bottom = y + h; + + UINT style = WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX|WS_MAXIMIZEBOX; + AdjustWindowRectEx( &rect, style, 0, 0 ); + + window = CreateWindowEx( 0, WC_DIALOG, "VG Error report", style, + rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, + NULL, NULL, NULL, NULL ); + + CreateWindowEx( 0, WC_STATIC, "Very sorry, VG Game engine has crashed. This is probably the fault of a developer!\n" + "\n" + "Here is some information about the crash. Optionally you can submit " + "this log anonymously, straight to Mt.Zero Software." + " This will help get the bug fixed for future players!", + WS_CHILD | WS_VISIBLE, p, p, w-p*2, 80, window, NULL, NULL, NULL ); + + textbox = CreateWindowEx( WS_EX_CLIENTEDGE, WC_EDIT, "textBox", + WS_CHILD | WS_VSCROLL | WS_VISIBLE | ES_MULTILINE | ES_WANTRETURN | ES_READONLY, + p, p+80+p, 800-p*2, 600-(p*2+sh+80+p), window, NULL, NULL, NULL ); + + send_button = CreateWindowEx( 0, WC_BUTTON, "Send to Mt.Zero developers", + WS_CHILD | WS_VISIBLE, + 800-(200+p), 600-(p*2+sh)+p, 200, sh, window, NULL, NULL, NULL ); + close_button = CreateWindowEx( 0, WC_BUTTON, "Close", + WS_CHILD | WS_VISIBLE, + 800-(200+p)-(100+p), 600-(p*2+sh)+p, 100, sh, window, NULL, NULL, NULL ); + select_button = CreateWindowEx( 0, WC_BUTTON, "Select all", + WS_CHILD | WS_VISIBLE, + p, 600-(p*2+sh)+p, 100, sh, window, NULL, NULL, NULL ); + + Edit_SetText( textbox, report_text ); + Edit_SetSel( textbox, 0, 0 ); + + HFONT font = CreateFont( 16, 0, 0, 0, FW_NORMAL, 0, 0, 0, 0, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, + DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, "Consolas"); + SendMessage( textbox, WM_SETFONT, (WPARAM)font, 1 ); + + defWndProc = (WNDPROC)SetWindowLongPtr(window, GWLP_WNDPROC, (LONG_PTR)WndProc); + + ShowWindow(window, SW_SHOW); + + MSG message = { 0 }; + while( GetMessage(&message, NULL, 0, 0) ) + DispatchMessage(&message); + return (int)message.wParam; +} diff --git a/vg_engine.c b/vg_engine.c index fee2954..64b5c96 100644 --- a/vg_engine.c +++ b/vg_engine.c @@ -630,26 +630,19 @@ static void vg_on_client_ready( vg_signal_id id, bool state ) vg_magi_restore(); } +#ifdef _WIN32 +#include +#include +#endif static int cmd_vg_settings_toggle( int argc, const char *argv[] ); void vg_init( int argc, const char *argv[], const char *window_name ) { + vg.window_name = window_name; vg.thread_purpose = SDL_TLSCreate(); VG_ASSERT( vg.thread_purpose ); SDL_TLSSet( vg.thread_purpose, &_thread_purpose_main, NULL ); - vg_log_init(); - vg.sig_engine = _vg_tower_create_signal( "Engine" ); - vg.sig_client = _vg_tower_create_signal( "Client" ); - _vg_tower_register_trigger( _vg_tower_mask( vg.sig_client ), vg_on_client_ready ); - - if( !vg_init_async_queue( &vg.main_tasks ) ) - return; - if( !vg_init_async_queue( &vg.loader_tasks ) ) - return; - - vg_rand_seed( &vg.rand, 461 ); - /* launch options */ _vg_opt_init( argc, argv ); const char *arg; @@ -675,7 +668,40 @@ void vg_init( int argc, const char *argv[], const char *window_name ) vg_error( "Could not open '%s' for logging.\n", arg ); } - vg.window_name = window_name; +#ifdef _WIN32 + DWORD pid = GetCurrentProcessId(); + + char report_args_buf[ 512 ]; + vg_str report_args; + vg_strnull( &report_args, report_args_buf, sizeof(report_args_buf) ); + vg_strcat( &report_args, "vgcrashreport.exe " ); + vg_strcatu64( &report_args, pid, 16 ); + vg_strcat( &report_args, " " ); + vg_strcat( &report_args, vg_log.crash_path ); + + STARTUPINFO si={0}; + PROCESS_INFORMATION pi={0}; + si.cb = sizeof(si); + + if( CreateProcess( NULL, report_args_buf, NULL, NULL, 0, BELOW_NORMAL_PRIORITY_CLASS, + NULL, NULL, &si, &pi ) == 0 ) + { + vg_error( "Could not start crash reporter. Reason: %d\n", GetLastError() ); + } + else + vg_success( "Crash watcher started!\n" ); + +#endif + vg.sig_engine = _vg_tower_create_signal( "Engine" ); + vg.sig_client = _vg_tower_create_signal( "Client" ); + _vg_tower_register_trigger( _vg_tower_mask( vg.sig_client ), vg_on_client_ready ); + + if( !vg_init_async_queue( &vg.main_tasks ) ) + return; + if( !vg_init_async_queue( &vg.loader_tasks ) ) + return; + + vg_rand_seed( &vg.rand, 461 ); } static int cmd_die( int argc, const char *argv[] ) diff --git a/vg_log.c b/vg_log.c index e90d73d..1eb4941 100644 --- a/vg_log.c +++ b/vg_log.c @@ -1,6 +1,7 @@ #include #include #include +#include #include "vg_platform.h" #include "vg_log.h" #include "vg_string.h" @@ -28,6 +29,16 @@ static void _vg_log_append_line( const char *str ) void vg_log_init(void) { vg_log.initialized = 1; + + vg_str log_path; + vg_strnull( &log_path, vg_log.crash_path, sizeof(vg_log.crash_path) ); + vg_strcat( &log_path, "crash-" ); + vg_strcatu64( &log_path, time(NULL), 10 ); + vg_strcat( &log_path, "-trace.txt" ); + + if( !vg_strgood( &log_path ) ) + exit(-2); + #ifdef VG_MULTITHREAD #ifdef VG_ENGINE vg_log.mutex = SDL_CreateMutex(); @@ -174,20 +185,11 @@ void vg_fatal_error( const char *fmt, ... ) void vg_fatal_exit( const char *comment ) { - char buf[ 1024 ]; - vg_str log_path; - vg_strnull( &log_path, buf, sizeof(buf) ); - vg_strcat( &log_path, "crash-" ); - vg_strcatu64( &log_path, time(NULL), 10 ); - vg_strcat( &log_path, "-trace.txt" ); - - if( !vg_strgood( &log_path ) ) - exit(-2); - - int fd = open( buf, O_CREAT | O_WRONLY, 0666 ); + int fd = open( vg_log.crash_path, O_CREAT | O_WRONLY, 0666 ); if( fd == -1 ) exit(-3); + char buf[ 1024 ]; vg_str line; vg_strnull( &line, buf, sizeof(buf) ); diff --git a/vg_log.h b/vg_log.h index f0582fe..5f10b1a 100644 --- a/vg_log.h +++ b/vg_log.h @@ -52,6 +52,7 @@ struct vg_log { + char crash_path[256]; char log[64][96]; u32 log_line_count, log_line_current; diff --git a/vg_string.c b/vg_string.c index 627d4fc..12546bd 100644 --- a/vg_string.c +++ b/vg_string.c @@ -129,6 +129,7 @@ void vg_strcatch( vg_str *str, char c ) void vg_strcati32( vg_str *str, i32 value ){ vg_strcatu64( str, (u64)value, 10 ); } void vg_strcatu64( vg_str *str, u64 value, u64 base ) { + const char *digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; if( value ) { char temp[32]; @@ -136,10 +137,7 @@ void vg_strcatu64( vg_str *str, u64 value, u64 base ) while( value && (i<31) ) { u32 digit = (u32)(value % base); - if( digit <= 9 ) - temp[ i ++ ] = '0' + digit; - else - temp[ i ++ ] = 'A' + (digit-10); + temp[ i ++ ] = digits[digit]; value /= base; } -- 2.25.1