Crash report stuff
authorhgn <hgodden00@gmail.com>
Fri, 4 Jul 2025 20:18:43 +0000 (21:18 +0100)
committerhgn <hgodden00@gmail.com>
Fri, 4 Jul 2025 20:18:43 +0000 (21:18 +0100)
src/vgcrashreport.c [new file with mode: 0644]
vg_engine.c
vg_log.c
vg_log.h
vg_string.c

diff --git a/src/vgcrashreport.c b/src/vgcrashreport.c
new file mode 100644 (file)
index 0000000..9fbd507
--- /dev/null
@@ -0,0 +1,281 @@
+#include <windows.h>
+#include <windowsx.h>
+#include <wingdi.h>
+#include <commctrl.h>
+#include <wininet.h>
+#include <time.h>
+#include <processthreadsapi.h>
+
+#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;
+}
index fee2954cfd932cfa0fba7e4b35cf2a1e6377e5f5..64b5c96992b9b15bb530877076e0ad5e2fbc37ed 100644 (file)
@@ -630,26 +630,19 @@ static void vg_on_client_ready( vg_signal_id id, bool state )
       vg_magi_restore();
 }
 
+#ifdef _WIN32
+#include <windows.h>
+#include <processthreadsapi.h>
+#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[] )
index e90d73da7c88af265b03f70a016ab80bf464f237..1eb4941f505b78eedd8e0756d107ff9364ca212a 100644 (file)
--- a/vg_log.c
+++ b/vg_log.c
@@ -1,6 +1,7 @@
 #include <stdarg.h>
 #include <string.h>
 #include <malloc.h>
+#include <time.h>
 #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) );
 
index f0582fe3829a69aec4a43f319ab89f06ce1437c2..5f10b1a98c8264daade68054c88ebfbb0ca4d671 100644 (file)
--- 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;
 
index 627d4fc9884688cf94673866249d42d57842d632..12546bd3cc6c2bc29e2f89e76f47f9941544fb79 100644 (file)
@@ -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;
       }