2017-07-13 10:26:03 +02:00
/*
* CConsoleHandler . cpp , part of VCMI engine
*
* Authors : listed in file AUTHORS in main folder
*
* License : GNU General Public License v2 .0 or later
* Full text of license available in license . txt file , in main folder
*
*/
2011-12-14 00:35:28 +03:00
# include "StdInc.h"
2007-08-17 20:42:21 +03:00
# include "CConsoleHandler.h"
2020-12-02 08:28:57 +02:00
# include "CConfigHandler.h"
2011-12-14 00:35:28 +03:00
2011-02-24 15:57:47 +02:00
# include "CThreadHelper.h"
2008-09-18 23:24:53 +03:00
2022-07-26 15:07:42 +02:00
VCMI_LIB_NAMESPACE_BEGIN
2023-04-11 16:11:14 +02:00
std : : mutex CConsoleHandler : : smx ;
2013-04-09 17:31:36 +03:00
2013-04-11 18:58:01 +03:00
DLL_LINKAGE CConsoleHandler * console = nullptr ;
2022-12-06 01:53:19 +02:00
VCMI_LIB_NAMESPACE_END
2014-08-11 21:17:17 +03:00
# ifndef VCMI_WINDOWS
2023-03-13 23:26:44 +02:00
using TColor = std : : string ;
2010-12-24 17:37:48 +02:00
# define CONSOLE_GREEN "\x1b[1;32m"
2013-04-02 20:06:43 +03:00
# define CONSOLE_RED "\x1b[1;31m"
2010-12-24 17:37:48 +02:00
# define CONSOLE_MAGENTA "\x1b[1;35m"
2013-04-02 20:06:43 +03:00
# define CONSOLE_YELLOW "\x1b[1;33m"
# define CONSOLE_WHITE "\x1b[1;37m"
2010-12-24 17:37:48 +02:00
# define CONSOLE_GRAY "\x1b[1;30m"
# define CONSOLE_TEAL "\x1b[1;36m"
2009-06-23 11:14:49 +03:00
# else
2015-11-03 13:40:36 +02:00
# include <windows.h>
2017-07-12 17:51:36 +02:00
# include <dbghelp.h>
2012-09-15 22:16:16 +03:00
# ifndef __MINGW32__
2009-07-31 23:10:22 +03:00
# pragma comment(lib, "dbghelp.lib")
2012-09-15 22:16:16 +03:00
# endif
2009-06-23 11:14:49 +03:00
typedef WORD TColor ;
HANDLE handleIn ;
HANDLE handleOut ;
2014-08-11 21:17:17 +03:00
HANDLE handleErr ;
2009-06-23 11:14:49 +03:00
# define CONSOLE_GREEN FOREGROUND_GREEN | FOREGROUND_INTENSITY
# define CONSOLE_RED FOREGROUND_RED | FOREGROUND_INTENSITY
# define CONSOLE_MAGENTA FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY
# define CONSOLE_YELLOW FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY
# define CONSOLE_WHITE FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY
# define CONSOLE_GRAY FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
2010-11-28 18:39:13 +02:00
# define CONSOLE_TEAL FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY
2014-08-11 21:17:17 +03:00
static TColor defErrColor ;
2008-06-21 16:27:52 +03:00
# endif
2009-06-23 11:14:49 +03:00
2013-11-07 15:48:41 +03:00
static TColor defColor ;
2009-06-23 11:14:49 +03:00
2022-12-06 01:53:19 +02:00
VCMI_LIB_NAMESPACE_BEGIN
2014-08-26 13:19:04 +03:00
# ifdef VCMI_WINDOWS
2009-07-31 23:10:22 +03:00
void printWinError ( )
{
//Get error code
int error = GetLastError ( ) ;
if ( ! error )
{
2017-08-10 18:39:27 +02:00
logGlobal - > error ( " No Win error information set. " ) ;
2009-07-31 23:10:22 +03:00
return ;
}
2017-08-11 19:03:05 +02:00
logGlobal - > error ( " Error %d encountered: " , error ) ;
2009-07-31 23:10:22 +03:00
//Get error description
2013-06-26 14:18:27 +03:00
char * pTemp = nullptr ;
2009-07-31 23:10:22 +03:00
FormatMessageA ( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM ,
2013-06-26 14:18:27 +03:00
nullptr , error , MAKELANGID ( LANG_NEUTRAL , SUBLANG_DEFAULT ) , ( LPSTR ) & pTemp , 1 , nullptr ) ;
2017-08-11 19:03:05 +02:00
logGlobal - > error ( pTemp ) ;
2009-07-31 23:10:22 +03:00
LocalFree ( pTemp ) ;
}
const char * exceptionName ( DWORD exc )
{
# define EXC_CASE(EXC) case EXCEPTION_##EXC : return "EXCEPTION_" #EXC
switch ( exc )
{
EXC_CASE ( ACCESS_VIOLATION ) ;
EXC_CASE ( DATATYPE_MISALIGNMENT ) ;
EXC_CASE ( BREAKPOINT ) ;
EXC_CASE ( SINGLE_STEP ) ;
EXC_CASE ( ARRAY_BOUNDS_EXCEEDED ) ;
EXC_CASE ( FLT_DENORMAL_OPERAND ) ;
EXC_CASE ( FLT_DIVIDE_BY_ZERO ) ;
EXC_CASE ( FLT_INEXACT_RESULT ) ;
EXC_CASE ( FLT_INVALID_OPERATION ) ;
EXC_CASE ( FLT_OVERFLOW ) ;
EXC_CASE ( FLT_STACK_CHECK ) ;
EXC_CASE ( FLT_UNDERFLOW ) ;
EXC_CASE ( INT_DIVIDE_BY_ZERO ) ;
EXC_CASE ( INT_OVERFLOW ) ;
EXC_CASE ( PRIV_INSTRUCTION ) ;
EXC_CASE ( IN_PAGE_ERROR ) ;
EXC_CASE ( ILLEGAL_INSTRUCTION ) ;
EXC_CASE ( NONCONTINUABLE_EXCEPTION ) ;
EXC_CASE ( STACK_OVERFLOW ) ;
EXC_CASE ( INVALID_DISPOSITION ) ;
EXC_CASE ( GUARD_PAGE ) ;
EXC_CASE ( INVALID_HANDLE ) ;
default :
return " UNKNOWN EXCEPTION " ;
}
# undef EXC_CASE
}
LONG WINAPI onUnhandledException ( EXCEPTION_POINTERS * exception )
{
2017-08-10 18:39:27 +02:00
logGlobal - > error ( " Disaster happened. " ) ;
2009-07-31 23:10:22 +03:00
PEXCEPTION_RECORD einfo = exception - > ExceptionRecord ;
2017-08-12 13:36:04 +02:00
logGlobal - > error ( " Reason: 0x%x - %s at %04x:%x " , einfo - > ExceptionCode , exceptionName ( einfo - > ExceptionCode ) , exception - > ContextRecord - > SegCs , ( void * ) einfo - > ExceptionAddress ) ;
2009-07-31 23:10:22 +03:00
if ( einfo - > ExceptionCode = = EXCEPTION_ACCESS_VIOLATION )
{
2017-08-12 13:36:04 +02:00
logGlobal - > error ( " Attempt to %s 0x%8x " , ( einfo - > ExceptionInformation [ 0 ] = = 1 ? " write to " : " read from " ) , ( void * ) einfo - > ExceptionInformation [ 1 ] ) ;
2009-07-31 23:10:22 +03:00
}
const DWORD threadId = : : GetCurrentThreadId ( ) ;
2017-08-11 19:03:05 +02:00
logGlobal - > error ( " Thread ID: %d " , threadId ) ;
2009-07-31 23:10:22 +03:00
//exception info to be placed in the dump
MINIDUMP_EXCEPTION_INFORMATION meinfo = { threadId , exception , TRUE } ;
//create file where dump will be placed
2013-06-26 14:18:27 +03:00
char * mname = nullptr ;
2009-07-31 23:10:22 +03:00
char buffer [ MAX_PATH + 1 ] ;
2013-06-26 14:18:27 +03:00
HMODULE hModule = nullptr ;
2009-07-31 23:10:22 +03:00
GetModuleFileNameA ( hModule , buffer , MAX_PATH ) ;
mname = strrchr ( buffer , ' \\ ' ) ;
if ( mname ! = 0 )
mname + + ;
else
mname = buffer ;
strcat ( mname , " _crashinfo.dmp " ) ;
HANDLE dfile = CreateFileA ( mname , GENERIC_READ | GENERIC_WRITE , FILE_SHARE_WRITE | FILE_SHARE_READ , 0 , CREATE_ALWAYS , 0 , 0 ) ;
2017-08-11 19:03:05 +02:00
logGlobal - > error ( " Crash info will be put in %s " , mname ) ;
2020-12-02 08:28:57 +02:00
// flush loggers
std : : string padding ( 1000 , ' @ ' ) ;
logGlobal - > error ( padding ) ;
logAi - > error ( padding ) ;
logNetwork - > error ( padding ) ;
auto dumpType = MiniDumpWithDataSegs ;
if ( settings [ " general " ] [ " extraDump " ] . Bool ( ) )
{
dumpType = ( MINIDUMP_TYPE ) (
MiniDumpWithFullMemory
| MiniDumpWithFullMemoryInfo
| MiniDumpWithHandleData
| MiniDumpWithUnloadedModules
| MiniDumpWithThreadInfo ) ;
}
MiniDumpWriteDump ( GetCurrentProcess ( ) , GetCurrentProcessId ( ) , dfile , dumpType , & meinfo , 0 , 0 ) ;
2009-07-31 23:10:22 +03:00
MessageBoxA ( 0 , " VCMI has crashed. We are sorry. File with information about encountered problem has been created. " , " VCMI Crashhandler " , MB_OK | MB_ICONERROR ) ;
return EXCEPTION_EXECUTE_HANDLER ;
}
# endif
2008-06-21 16:27:52 +03:00
2009-04-15 17:03:31 +03:00
2013-04-05 13:29:46 +03:00
void CConsoleHandler : : setColor ( EConsoleTextColor : : EConsoleTextColor color )
2008-09-17 13:18:22 +03:00
{
2016-03-12 03:41:27 +02:00
TColor colorCode ;
switch ( color )
2008-09-17 13:18:22 +03:00
{
2016-03-12 03:41:27 +02:00
case EConsoleTextColor : : DEFAULT :
colorCode = defColor ;
2008-09-17 13:18:22 +03:00
break ;
2016-03-12 03:41:27 +02:00
case EConsoleTextColor : : GREEN :
colorCode = CONSOLE_GREEN ;
2008-09-17 13:18:22 +03:00
break ;
2016-03-12 03:41:27 +02:00
case EConsoleTextColor : : RED :
colorCode = CONSOLE_RED ;
2008-09-17 13:18:22 +03:00
break ;
2016-03-12 03:41:27 +02:00
case EConsoleTextColor : : MAGENTA :
colorCode = CONSOLE_MAGENTA ;
2008-09-17 13:18:22 +03:00
break ;
2016-03-12 03:41:27 +02:00
case EConsoleTextColor : : YELLOW :
colorCode = CONSOLE_YELLOW ;
2008-09-17 13:18:22 +03:00
break ;
2016-03-12 03:41:27 +02:00
case EConsoleTextColor : : WHITE :
colorCode = CONSOLE_WHITE ;
2008-09-17 13:18:22 +03:00
break ;
2016-03-12 03:41:27 +02:00
case EConsoleTextColor : : GRAY :
colorCode = CONSOLE_GRAY ;
2008-09-17 13:18:22 +03:00
break ;
2016-03-12 03:41:27 +02:00
case EConsoleTextColor : : TEAL :
colorCode = CONSOLE_TEAL ;
2010-11-28 18:39:13 +02:00
break ;
2008-09-17 13:18:22 +03:00
default :
2016-03-12 03:41:27 +02:00
colorCode = defColor ;
2008-09-17 13:18:22 +03:00
break ;
}
2014-08-11 21:17:17 +03:00
# ifdef VCMI_WINDOWS
2013-04-05 13:29:46 +03:00
SetConsoleTextAttribute ( handleOut , colorCode ) ;
2014-08-11 21:17:17 +03:00
if ( color = = EConsoleTextColor : : DEFAULT )
colorCode = defErrColor ;
SetConsoleTextAttribute ( handleErr , colorCode ) ;
2008-09-18 23:24:53 +03:00
# else
2013-04-05 13:29:46 +03:00
std : : cout < < colorCode ;
2008-09-17 13:18:22 +03:00
# endif
}
2008-06-21 16:27:52 +03:00
2023-03-13 23:26:44 +02:00
int CConsoleHandler : : run ( ) const
2007-08-15 18:13:11 +03:00
{
2012-06-27 23:44:01 +03:00
setThreadName ( " CConsoleHandler::run " ) ;
2011-01-15 19:30:46 +02:00
//disabling sync to make in_avail() work (othervice always returns 0)
2017-07-12 17:51:36 +02:00
{
TLockGuard _ ( smx ) ;
std : : ios : : sync_with_stdio ( false ) ;
}
2011-01-15 19:30:46 +02:00
std : : string buffer ;
while ( std : : cin . good ( ) )
2007-08-15 18:13:11 +03:00
{
2014-08-26 13:19:04 +03:00
# ifndef VCMI_WINDOWS
2011-01-15 19:30:46 +02:00
//check if we have some unreaded symbols
if ( std : : cin . rdbuf ( ) - > in_avail ( ) )
{
if ( getline ( std : : cin , buffer ) . good ( ) )
if ( cb & & * cb )
2023-01-06 22:01:00 +02:00
( * cb ) ( buffer , false ) ;
2011-01-15 19:30:46 +02:00
}
else
boost : : this_thread : : sleep ( boost : : posix_time : : millisec ( 100 ) ) ;
boost : : this_thread : : interruption_point ( ) ;
2011-02-24 15:57:47 +02:00
# else
std : : getline ( std : : cin , buffer ) ;
if ( cb & & * cb )
2023-01-06 22:01:00 +02:00
( * cb ) ( buffer , false ) ;
2011-02-24 15:57:47 +02:00
# endif
2007-08-15 18:13:11 +03:00
}
return - 1 ;
}
2023-03-13 23:26:44 +02:00
CConsoleHandler : : CConsoleHandler ( ) :
cb ( new std : : function < void ( const std : : string & , bool ) > ) ,
thread ( nullptr )
2007-08-15 18:13:11 +03:00
{
2014-08-26 13:19:04 +03:00
# ifdef VCMI_WINDOWS
2008-09-17 13:18:22 +03:00
handleIn = GetStdHandle ( STD_INPUT_HANDLE ) ;
handleOut = GetStdHandle ( STD_OUTPUT_HANDLE ) ;
2014-08-11 21:17:17 +03:00
handleErr = GetStdHandle ( STD_ERROR_HANDLE ) ;
2008-09-17 13:18:22 +03:00
CONSOLE_SCREEN_BUFFER_INFO csbi ;
GetConsoleScreenBufferInfo ( handleOut , & csbi ) ;
defColor = csbi . wAttributes ;
2014-08-11 21:17:17 +03:00
GetConsoleScreenBufferInfo ( handleErr , & csbi ) ;
defErrColor = csbi . wAttributes ;
2009-08-02 17:21:18 +03:00
# ifndef _DEBUG
2009-07-31 23:10:22 +03:00
SetUnhandledExceptionFilter ( onUnhandledException ) ;
2009-08-02 17:21:18 +03:00
# endif
2008-09-18 23:24:53 +03:00
# else
defColor = " \x1b [0m " ;
2008-09-17 13:18:22 +03:00
# endif
2007-08-15 18:13:11 +03:00
}
2008-09-17 13:18:22 +03:00
CConsoleHandler : : ~ CConsoleHandler ( )
{
2017-08-10 18:39:27 +02:00
logGlobal - > info ( " Killing console... " ) ;
2009-10-26 07:39:30 +02:00
end ( ) ;
2008-09-17 13:18:22 +03:00
delete cb ;
2017-08-10 18:39:27 +02:00
logGlobal - > info ( " Killing console... done! " ) ;
2008-09-17 13:18:22 +03:00
}
2009-06-23 11:14:49 +03:00
void CConsoleHandler : : end ( )
2008-09-18 23:24:53 +03:00
{
2012-09-15 22:16:16 +03:00
if ( thread )
2011-02-24 15:57:47 +02:00
{
2014-08-26 13:19:04 +03:00
# ifndef VCMI_WINDOWS
2011-01-15 19:30:46 +02:00
thread - > interrupt ( ) ;
2011-02-24 15:57:47 +02:00
# else
TerminateThread ( thread - > native_handle ( ) , 0 ) ;
# endif
2009-10-26 07:39:30 +02:00
thread - > join ( ) ;
delete thread ;
2013-06-26 14:18:27 +03:00
thread = nullptr ;
2009-10-26 07:39:30 +02:00
}
2008-09-18 23:24:53 +03:00
}
2009-06-23 11:14:49 +03:00
void CConsoleHandler : : start ( )
{
2014-08-04 21:33:59 +03:00
thread = new boost : : thread ( std : : bind ( & CConsoleHandler : : run , console ) ) ;
2009-10-26 07:39:30 +02:00
}
2022-07-26 15:07:42 +02:00
VCMI_LIB_NAMESPACE_END