2017-06-01 21:55:10 +02:00
# pragma region Copyright (c) 2014-2017 OpenRCT2 Developers
2016-05-04 19:24:41 +02:00
/*****************************************************************************
* 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
2018-01-18 13:55:34 +01:00
# include "Crash.h"
2016-04-07 00:10:12 +02:00
# ifdef USE_BREAKPAD
2017-02-08 23:17:01 +01:00
# include <memory>
2016-04-07 00:10:12 +02:00
# include <stdio.h>
2017-03-26 23:30:23 +02:00
# if defined(_WIN32)
2016-04-10 00:30:10 +02:00
# include <breakpad/client/windows/handler/exception_handler.h>
# include <string>
# include <ShlObj.h>
2016-04-07 00:10:12 +02:00
# else
2016-04-10 00:30:10 +02:00
# error Breakpad support not implemented yet for this platform
# endif
2016-04-07 00:10:12 +02:00
2016-04-10 00:30:10 +02:00
# include "../core/Console.hpp"
2018-02-01 18:49:14 +01:00
# include "../localisation/Language.h"
2017-02-08 23:17:01 +01:00
# include "../rct2/S6Exporter.h"
2018-02-01 18:49:14 +01:00
# include "../scenario/Scenario.h"
2017-02-03 14:11:58 +01:00
# include "../Version.h"
2018-02-01 18:49:14 +01:00
# include "platform.h"
2016-04-10 00:30:10 +02:00
# define WSZ(x) L"" x
2016-08-28 18:17:20 +02:00
# ifdef OPENRCT2_COMMIT_SHA1_SHORT
const wchar_t * _wszCommitSha1Short = WSZ ( OPENRCT2_COMMIT_SHA1_SHORT ) ;
# else
const wchar_t * _wszCommitSha1Short = WSZ ( " " ) ;
# endif
2016-10-18 13:25:57 +02:00
// OPENRCT2_ARCHITECTURE is required to be defined in version.h
const wchar_t * _wszArchitecture = WSZ ( OPENRCT2_ARCHITECTURE ) ;
2016-04-10 00:30:10 +02:00
static bool OnCrash ( const wchar_t * dumpPath ,
const wchar_t * miniDumpId ,
void * context ,
EXCEPTION_POINTERS * exinfo ,
MDRawAssertionInfo * assertion ,
bool succeeded )
{
if ( ! succeeded )
{
2017-04-30 06:39:24 +02:00
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 occurred. " ;
2016-04-10 00:30:10 +02:00
printf ( " %s \n " , DumpFailedMessage ) ;
2016-05-11 13:25:21 +02:00
if ( ! gOpenRCT2SilentBreakpad )
{
2017-08-15 10:07:44 +02:00
MessageBoxA ( nullptr , DumpFailedMessage , OPENRCT2_NAME , MB_OK | MB_ICONERROR ) ;
2016-05-11 13:25:21 +02:00
}
2016-04-10 00:30:10 +02:00
return succeeded ;
}
2016-05-02 23:20:36 +02:00
// Get filenames
2016-04-10 00:30:10 +02:00
wchar_t dumpFilePath [ MAX_PATH ] ;
wchar_t saveFilePath [ MAX_PATH ] ;
2016-09-26 04:24:29 +02:00
swprintf_s ( dumpFilePath , sizeof ( dumpFilePath ) , L " %s%s.dmp " , dumpPath , miniDumpId ) ;
swprintf_s ( saveFilePath , sizeof ( saveFilePath ) , L " %s%s.sv6 " , dumpPath , miniDumpId ) ;
2016-04-10 00:30:10 +02:00
2016-05-02 23:20:36 +02:00
// Try to rename the files
wchar_t dumpFilePathNew [ MAX_PATH ] ;
2016-10-18 13:25:57 +02:00
swprintf_s ( dumpFilePathNew , sizeof ( dumpFilePathNew ) , L " %s%s(%s_%s).dmp " , dumpPath , miniDumpId , _wszCommitSha1Short , _wszArchitecture ) ;
2016-05-02 23:20:36 +02:00
if ( _wrename ( dumpFilePath , dumpFilePathNew ) = = 0 )
{
std : : wcscpy ( dumpFilePath , dumpFilePathNew ) ;
}
// Log information to output
2016-05-02 23:50:43 +02:00
wprintf ( L " Dump Path: %s \n " , dumpPath ) ;
wprintf ( L " Dump File Path: %s \n " , dumpFilePath ) ;
2016-04-10 00:30:10 +02:00
wprintf ( L " Dump Id: %s \n " , miniDumpId ) ;
wprintf ( L " Version: %s \n " , WSZ ( OPENRCT2_VERSION ) ) ;
2016-08-28 18:17:20 +02:00
wprintf ( L " Commit: %s \n " , _wszCommitSha1Short ) ;
2016-04-10 00:30:10 +02:00
bool savedGameDumped = false ;
2017-02-08 23:17:01 +01:00
utf8 * saveFilePathUTF8 = widechar_to_utf8 ( saveFilePath ) ;
try
{
auto exporter = std : : make_unique < S6Exporter > ( ) ;
exporter - > Export ( ) ;
exporter - > SaveGame ( saveFilePathUTF8 ) ;
2016-04-10 00:30:10 +02:00
savedGameDumped = true ;
}
2018-01-02 20:23:22 +01:00
catch ( const std : : exception & )
2017-02-08 23:17:01 +01:00
{
}
free ( saveFilePathUTF8 ) ;
2016-04-10 00:30:10 +02:00
2016-05-11 13:25:21 +02:00
if ( gOpenRCT2SilentBreakpad )
{
return succeeded ;
}
2017-02-08 23:17:01 +01:00
2016-05-02 23:20:36 +02:00
constexpr const wchar_t * MessageFormat = L " A crash has occurred and a dump was created at \n %s. \n \n Please file an issue with OpenRCT2 on GitHub, and provide the dump and saved game there. \n \n Version: %s \n Commit: %s " ;
2016-04-10 00:30:10 +02:00
wchar_t message [ MAX_PATH * 2 ] ;
swprintf_s ( message ,
MessageFormat ,
dumpFilePath ,
WSZ ( OPENRCT2_VERSION ) ,
2016-08-28 18:17:20 +02:00
_wszCommitSha1Short ) ;
2016-04-10 00:30:10 +02:00
// Cannot use platform_show_messagebox here, it tries to set parent window already dead.
2017-08-15 10:07:44 +02:00
MessageBoxW ( nullptr , message , WSZ ( OPENRCT2_NAME ) , MB_OK | MB_ICONERROR ) ;
HRESULT coInitializeResult = CoInitialize ( nullptr ) ;
2016-04-10 00:30:10 +02:00
if ( SUCCEEDED ( coInitializeResult ) )
{
2016-08-28 18:19:13 +02:00
LPITEMIDLIST pidl = ILCreateFromPathW ( dumpPath ) ;
LPITEMIDLIST files [ 2 ] ;
2016-04-10 00:30:10 +02:00
uint32 numFiles = 0 ;
files [ numFiles + + ] = ILCreateFromPathW ( dumpFilePath ) ;
if ( savedGameDumped )
{
files [ numFiles + + ] = ILCreateFromPathW ( saveFilePath ) ;
}
if ( pidl ! = nullptr ) {
2017-01-12 19:28:51 +01:00
SHOpenFolderAndSelectItems ( pidl , numFiles , ( LPCITEMIDLIST * ) files , 0 ) ;
2016-04-10 00:30:10 +02:00
ILFree ( pidl ) ;
for ( uint32 i = 0 ; i < numFiles ; i + + )
{
ILFree ( files [ i ] ) ;
}
}
CoUninitialize ( ) ;
}
// Return whether the dump was successful
return succeeded ;
2016-04-09 19:21:40 +02:00
}
2016-04-10 00:30:10 +02:00
static std : : wstring GetDumpDirectory ( )
{
char userDirectory [ MAX_PATH ] ;
2017-08-15 10:07:44 +02:00
platform_get_user_directory ( userDirectory , nullptr , sizeof ( userDirectory ) ) ;
2016-04-10 00:30:10 +02:00
wchar_t * userDirectoryW = utf8_to_widechar ( userDirectory ) ;
auto result = std : : wstring ( userDirectoryW ) ;
free ( userDirectoryW ) ;
2016-04-07 00:10:12 +02:00
2016-04-10 00:30:10 +02:00
return result ;
2016-04-07 00:10:12 +02:00
}
2016-04-10 00:30:10 +02:00
// Using non-null pipe name here lets breakpad try setting OOP crash handling
2016-04-10 20:51:44 +02:00
constexpr const wchar_t * PipeName = L " openrct2-bpad " ;
2016-04-10 00:30:10 +02:00
2016-10-06 23:32:10 +02:00
# endif // USE_BREAKPAD
2018-02-01 18:49:14 +01:00
CExceptionHandler crash_init ( )
2016-04-07 00:10:12 +02:00
{
# ifdef USE_BREAKPAD
2016-04-10 00:30:10 +02:00
// 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 ) ;
2016-04-07 00:10:12 +02:00
# else // USE_BREAKPAD
2016-04-10 00:30:10 +02:00
return nullptr ;
2016-04-07 00:10:12 +02:00
# endif // USE_BREAKPAD
}