mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2026-01-17 03:53:07 +01:00
170 lines
5.5 KiB
C++
170 lines
5.5 KiB
C++
#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers
|
|
/*****************************************************************************
|
|
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
|
|
*
|
|
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
|
|
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
|
|
*
|
|
* OpenRCT2 is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* A full copy of the GNU General Public License can be found in licence.txt
|
|
*****************************************************************************/
|
|
#pragma endregion
|
|
|
|
#include <SDL_platform.h>
|
|
#include "crash.h"
|
|
|
|
#ifdef USE_BREAKPAD
|
|
#include <stdio.h>
|
|
|
|
#if defined(__WINDOWS__)
|
|
#include <breakpad/client/windows/handler/exception_handler.h>
|
|
#include <string>
|
|
#include <ShlObj.h>
|
|
#elif defined(__LINUX__)
|
|
#include <breakpad/client/linux/handler/exception_handler.h>
|
|
#define BREAKPAD_PATH "/tmp"
|
|
#else
|
|
#error Breakpad support not implemented yet for this platform
|
|
#endif
|
|
|
|
extern "C" {
|
|
#include "../localisation/language.h"
|
|
#include "../scenario.h"
|
|
#include "platform.h"
|
|
}
|
|
|
|
#include "../core/Console.hpp"
|
|
|
|
#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 const char * DumpFailedMessage = "Failed to create the dump. Please file an issue with OpenRCT2 on GitHub and provide latest save, and provide information about what you did before the crash occured.";
|
|
printf("%s\n", DumpFailedMessage);
|
|
if (!gOpenRCT2SilentBreakpad)
|
|
{
|
|
MessageBoxA(NULL, DumpFailedMessage, OPENRCT2_NAME, MB_OK | MB_ICONERROR);
|
|
}
|
|
return succeeded;
|
|
}
|
|
|
|
// Get filenames
|
|
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);
|
|
|
|
// Try to rename the files
|
|
wchar_t dumpFilePathNew[MAX_PATH];
|
|
wsprintfW(dumpFilePathNew, L"%s%s(%s).dmp", dumpPath, miniDumpId, WSZ(OPENRCT2_COMMIT_SHA1_SHORT));
|
|
if (_wrename(dumpFilePath, dumpFilePathNew) == 0)
|
|
{
|
|
std::wcscpy(dumpFilePath, dumpFilePathNew);
|
|
}
|
|
|
|
// Log information to output
|
|
wprintf(L"Dump Path: %s\n", dumpPath);
|
|
wprintf(L"Dump File 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);
|
|
}
|
|
|
|
if (gOpenRCT2SilentBreakpad)
|
|
{
|
|
return succeeded;
|
|
}
|
|
constexpr const wchar_t * MessageFormat = L"A crash has occurred and a dump was created at\n%s.\n\nPlease file an issue with OpenRCT2 on GitHub, and provide the dump and saved game there.\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
|
|
|
|
// Using non-null pipe name here lets breakpad try setting OOP crash handling
|
|
constexpr const wchar_t * PipeName = L"openrct2-bpad";
|
|
|
|
extern "C" CExceptionHandler crash_init()
|
|
{
|
|
#ifdef USE_BREAKPAD
|
|
// 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<CExceptionHandler>(exHandler);
|
|
#else // USE_BREAKPAD
|
|
return nullptr;
|
|
#endif // USE_BREAKPAD
|
|
}
|