diff --git a/src/openrct2.c b/src/openrct2.c index 5d9bf0973e..6f919e89d3 100644 --- a/src/openrct2.c +++ b/src/openrct2.c @@ -33,13 +33,13 @@ #include "network/http.h" #include "network/network.h" #include "openrct2.h" +#include "platform/crash.h" #include "platform/platform.h" #include "ride/ride.h" #include "title.h" #include "util/sawyercoding.h" #include "util/util.h" #include "world/mapgen.h" -#include "platform/crash.h" #if defined(__unix__) #include @@ -185,11 +185,7 @@ bool openrct2_initialise() return false; } - // Exception handling - breakpad - // Uses user data directory for storing dumps - CExceptionHandler eh; - // never free - eh = newCExceptionHandlerSimple(); + crash_init(); if (!openrct2_setup_rct2_segment()) { log_fatal("Unable to load RCT2 data sector"); diff --git a/src/platform/crash.cpp b/src/platform/crash.cpp index 188abc64b0..7503089ceb 100644 --- a/src/platform/crash.cpp +++ b/src/platform/crash.cpp @@ -1,105 +1,135 @@ -#include "crash.h" #include +#include "crash.h" #ifdef USE_BREAKPAD #include -#ifdef __WINDOWS__ -#include -#include -#include -#define BREAKPAD_PATH "." +#if defined(__WINDOWS__) + #include + #include + #include +#elif defined(__LINUX__) + #include + #define BREAKPAD_PATH "/tmp" #else -#error Breakpad support not implemented yet for this platform -#endif __WINDOWS__ - -#ifdef __LINUX__ -#include -#define BREAKPAD_PATH "/tmp" -#endif __LINUX__ + #error Breakpad support not implemented yet for this platform +#endif extern "C" { -#include "platform.h" -#include "../localisation/language.h" -#include "../scenario.h" + #include "../localisation/language.h" + #include "../scenario.h" + #include "platform.h" } +#include "../core/Console.hpp" -static bool dumpCallback(const wchar_t *dump_path, const wchar_t *minidump_id, void* context, EXCEPTION_POINTERS* exinfo, MDRawAssertionInfo* assertion, bool succeeded) { - if (!succeeded) { - const char *msg = "Failed to create the dump. Nothing left to do. Please file an issue with OpenRCT2 on Github and provide latest save."; - log_fatal(msg); - MessageBoxA(NULL, msg, "OpenRCT2", MB_OK); - return succeeded; - } - char *buffer_path = widechar_to_utf8(dump_path); - char *buffer_minidump = widechar_to_utf8(minidump_id); - char dump_file_path[MAX_PATH]; - char save_file_path[MAX_PATH]; - sprintf(dump_file_path, "%s%s.dmp", buffer_path, buffer_minidump); - sprintf(save_file_path, "%s%s.sv6", buffer_path, buffer_minidump); - log_fatal("Dump path: %s", buffer_path); - log_fatal("minidump id: %s", buffer_minidump); - log_fatal("Version: %s", OPENRCT2_VERSION); - log_fatal("Commit: %s", OPENRCT2_COMMIT_SHA1_SHORT); - SDL_RWops* rw = SDL_RWFromFile(save_file_path, "wb+"); - bool saved = false; - if (rw != NULL) { - scenario_save(rw, 0x80000000); - saved = true; - SDL_RWclose(rw); - } - char message[MAX_PATH * 2]; - sprintf(message, "A crash has occurred and dump was created at %s. Please create an issue with OpenRCT2 on Github and provide the dump and %s. Version: %s, Commit: %s", - dump_file_path, saved ? save_file_path : "latest save", OPENRCT2_VERSION, OPENRCT2_COMMIT_SHA1_SHORT); - // Cannot use platform_show_messagebox here, it tries to set parent window already dead. - MessageBoxA(NULL, message, "OpenRCT2", MB_OK); - HRESULT coinit_result = CoInitialize(NULL); - if (coinit_result == S_OK) { - //ShellExecute(NULL, "explore", message, NULL, NULL, 5); - ITEMIDLIST *pidl = ILCreateFromPath(buffer_path); - ITEMIDLIST *files[2]; - files[0] = ILCreateFromPath(dump_file_path); - int files_count = 1; - if (saved) { - files[1] = ILCreateFromPath(save_file_path); - files_count = 2; - } - if (pidl) { - HRESULT res = SHOpenFolderAndSelectItems(pidl, files_count, (LPCITEMIDLIST *)files, 0); - ILFree(pidl); - ILFree(files[0]); - if (saved) { - ILFree(files[1]); - } - } - CoUninitialize(); - } - free(buffer_path); - free(buffer_minidump); - return succeeded; +#define WSZ(x) L"" x + +static bool OnCrash(const wchar_t * dumpPath, + const wchar_t * miniDumpId, + void * context, + EXCEPTION_POINTERS * exinfo, + MDRawAssertionInfo * assertion, + bool succeeded) +{ + if (!succeeded) + { + constexpr char * DumpFailedMessage = "Failed to create the dump. Nothing left to do. Please file an issue with OpenRCT2 on Github and provide latest save."; + printf("%s\n", DumpFailedMessage); + MessageBoxA(NULL, DumpFailedMessage, OPENRCT2_NAME, MB_OK | MB_ICONERROR); + return succeeded; + } + + wchar_t dumpFilePath[MAX_PATH]; + wchar_t saveFilePath[MAX_PATH]; + wsprintfW(dumpFilePath, L"%s%s.dmp", dumpPath, miniDumpId); + wsprintfW(saveFilePath, L"%s%s.sv6", dumpPath, miniDumpId); + + wprintf(L"Dump Path: %s\n", dumpFilePath); + wprintf(L"Dump Id: %s\n", miniDumpId); + wprintf(L"Version: %s\n", WSZ(OPENRCT2_VERSION)); + wprintf(L"Commit: %s\n", WSZ(OPENRCT2_COMMIT_SHA1_SHORT)); + + utf8 * saveFilePathUTF8 = widechar_to_utf8(saveFilePath); + SDL_RWops * rw = SDL_RWFromFile(saveFilePathUTF8, "wb+"); + free(saveFilePathUTF8); + + bool savedGameDumped = false; + if (rw != NULL) { + scenario_save(rw, 0x80000000); + savedGameDumped = true; + SDL_RWclose(rw); + } + + constexpr wchar_t * MessageFormat = L"A crash has occurred and dump was created at\n%s.\n\nPlease create an issue with OpenRCT2 on Github and provide the dump and save.\n\nVersion: %s\nCommit: %s"; + wchar_t message[MAX_PATH * 2]; + swprintf_s(message, + MessageFormat, + dumpFilePath, + WSZ(OPENRCT2_VERSION), + WSZ(OPENRCT2_COMMIT_SHA1_SHORT)); + + // Cannot use platform_show_messagebox here, it tries to set parent window already dead. + MessageBoxW(NULL, message, WSZ(OPENRCT2_NAME), MB_OK | MB_ICONERROR); + HRESULT coInitializeResult = CoInitialize(NULL); + if (SUCCEEDED(coInitializeResult)) + { + ITEMIDLIST * pidl = ILCreateFromPathW(dumpPath); + ITEMIDLIST * files[2]; + uint32 numFiles = 0; + + files[numFiles++] = ILCreateFromPathW(dumpFilePath); + if (savedGameDumped) + { + files[numFiles++] = ILCreateFromPathW(saveFilePath); + } + if (pidl != nullptr) { + HRESULT result = SHOpenFolderAndSelectItems(pidl, numFiles, (LPCITEMIDLIST *)files, 0); + ILFree(pidl); + for (uint32 i = 0; i < numFiles; i++) + { + ILFree(files[i]); + } + } + CoUninitialize(); + } + + // Return whether the dump was successful + return succeeded; } + +static std::wstring GetDumpDirectory() +{ + char userDirectory[MAX_PATH]; + platform_get_user_directory(userDirectory, NULL); + + wchar_t * userDirectoryW = utf8_to_widechar(userDirectory); + auto result = std::wstring(userDirectoryW); + free(userDirectoryW); + + return result; +} + #endif // USE_BREAKPAD -extern "C" CExceptionHandler newCExceptionHandlerSimple(void) +// Using non-null pipe name here lets breakpad try setting OOP crash handling +constexpr wchar_t * PipeName = L"openrct2-bpad"; + +extern "C" CExceptionHandler crash_init() { #ifdef USE_BREAKPAD - char path[MAX_PATH]; - platform_get_user_directory(path, NULL); - wchar_t *wpath_buffer = utf8_to_widechar(path); - std::wstring wpath(wpath_buffer); - free(wpath_buffer); - // Path must exist and be RW! - return reinterpret_cast(new google_breakpad::ExceptionHandler( - wpath, - 0, - dumpCallback, - 0, - google_breakpad::ExceptionHandler::HANDLER_ALL, - MiniDumpWithDataSegs, - L"openrct2-bpad", // using non-null pipe name here lets breakpad try setting OOP crash handling - 0 )); + // Path must exist and be RW! + auto exHandler = new google_breakpad::ExceptionHandler( + GetDumpDirectory(), + 0, + OnCrash, + 0, + google_breakpad::ExceptionHandler::HANDLER_ALL, + MiniDumpWithDataSegs, + PipeName, + 0); + return reinterpret_cast(exHandler); #else // USE_BREAKPAD - return nullptr; + return nullptr; #endif // USE_BREAKPAD } diff --git a/src/platform/crash.h b/src/platform/crash.h index 0b9938a285..35c0d1f74b 100644 --- a/src/platform/crash.h +++ b/src/platform/crash.h @@ -1,13 +1,13 @@ #ifndef _OPENRCT2_CRASH_ #define _OPENRCT2_CRASH_ -typedef void* CExceptionHandler; +typedef void * CExceptionHandler; #ifdef __cplusplus extern "C" { #endif -CExceptionHandler newCExceptionHandlerSimple(void); + CExceptionHandler crash_init(); #ifdef __cplusplus } #endif