You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
398 lines
8.4 KiB
398 lines
8.4 KiB
/* |
|
sys_con.c - stdout and log |
|
Copyright (C) 2007 Uncle Mike |
|
|
|
This program 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. |
|
|
|
This program is distributed in the hope that it will be useful, |
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
GNU General Public License for more details. |
|
*/ |
|
|
|
#include "common.h" |
|
#if XASH_WIN32 |
|
#define STDOUT_FILENO 1 |
|
#include <io.h> |
|
#elif XASH_ANDROID |
|
#include <android/log.h> |
|
#endif |
|
#include <string.h> |
|
#include <errno.h> |
|
|
|
#if !XASH_WIN32 && !XASH_MOBILE_PLATFORM |
|
// #define XASH_COLORIZE_CONSOLE |
|
// use with caution, running engine in Qt Creator may cause a freeze in read() call |
|
// I was never encountered this bug anywhere else, so still enable by default |
|
// #define XASH_USE_SELECT 1 |
|
#endif |
|
|
|
#if XASH_USE_SELECT |
|
// non-blocking console input |
|
#include <sys/select.h> |
|
#endif |
|
|
|
typedef struct { |
|
char title[64]; |
|
qboolean log_active; |
|
char log_path[MAX_SYSPATH]; |
|
FILE *logfile; |
|
int logfileno; |
|
} LogData; |
|
|
|
static LogData s_ld; |
|
|
|
char *Sys_Input( void ) |
|
{ |
|
#if XASH_USE_SELECT |
|
{ |
|
fd_set rfds; |
|
static char line[1024]; |
|
static int len; |
|
struct timeval tv; |
|
tv.tv_sec = 0; |
|
tv.tv_usec = 0; |
|
FD_ZERO( &rfds ); |
|
FD_SET( 0, &rfds); // stdin |
|
while( select( 1, &rfds, NULL, NULL, &tv ) > 0 ) |
|
{ |
|
if( read( 0, &line[len], 1 ) != 1 ) |
|
break; |
|
if( line[len] == '\n' || len > 1022 ) |
|
{ |
|
line[ ++len ] = 0; |
|
len = 0; |
|
return line; |
|
} |
|
len++; |
|
tv.tv_sec = 0; |
|
tv.tv_usec = 0; |
|
} |
|
} |
|
#endif |
|
#if XASH_WIN32 |
|
return Wcon_Input(); |
|
#endif |
|
return NULL; |
|
} |
|
|
|
void Sys_DestroyConsole( void ) |
|
{ |
|
// last text message into console or log |
|
Con_Reportf( "Sys_DestroyConsole: Exiting!\n" ); |
|
#if XASH_WIN32 |
|
Wcon_DestroyConsole(); |
|
#endif |
|
} |
|
|
|
/* |
|
=============================================================================== |
|
|
|
SYSTEM LOG |
|
|
|
=============================================================================== |
|
*/ |
|
int Sys_LogFileNo( void ) |
|
{ |
|
return s_ld.logfileno; |
|
} |
|
|
|
static void Sys_FlushStdout( void ) |
|
{ |
|
// never printing anything to stdout on mobiles |
|
#if !XASH_MOBILE_PLATFORM |
|
fflush( stdout ); |
|
#endif |
|
} |
|
|
|
static void Sys_FlushLogfile( void ) |
|
{ |
|
if( s_ld.logfile ) |
|
fflush( s_ld.logfile ); |
|
} |
|
|
|
void Sys_InitLog( void ) |
|
{ |
|
const char *mode; |
|
|
|
if( Sys_CheckParm( "-log" ) && host.allow_console != 0 ) |
|
{ |
|
s_ld.log_active = true; |
|
Q_strncpy( s_ld.log_path, "engine.log", sizeof( s_ld.log_path )); |
|
} |
|
|
|
if( host.change_game && host.type != HOST_DEDICATED ) |
|
mode = "a"; |
|
else mode = "w"; |
|
|
|
// create log if needed |
|
if( s_ld.log_active ) |
|
{ |
|
s_ld.logfile = fopen( s_ld.log_path, mode ); |
|
|
|
if ( !s_ld.logfile ) |
|
{ |
|
Con_Reportf( S_ERROR "Sys_InitLog: can't create log file %s: %s\n", s_ld.log_path, strerror( errno ) ); |
|
return; |
|
} |
|
|
|
s_ld.logfileno = fileno( s_ld.logfile ); |
|
|
|
fprintf( s_ld.logfile, "=================================================================================\n" ); |
|
fprintf( s_ld.logfile, "\t%s (build %i) started at %s\n", s_ld.title, Q_buildnum(), Q_timestamp( TIME_FULL ) ); |
|
fprintf( s_ld.logfile, "=================================================================================\n" ); |
|
} |
|
} |
|
|
|
void Sys_CloseLog( void ) |
|
{ |
|
char event_name[64]; |
|
|
|
// continue logged |
|
switch( host.status ) |
|
{ |
|
case HOST_CRASHED: |
|
Q_strncpy( event_name, "crashed", sizeof( event_name )); |
|
break; |
|
case HOST_ERR_FATAL: |
|
Q_strncpy( event_name, "stopped with error", sizeof( event_name )); |
|
break; |
|
default: |
|
if( !host.change_game ) Q_strncpy( event_name, "stopped", sizeof( event_name )); |
|
else Q_strncpy( event_name, host.finalmsg, sizeof( event_name )); |
|
break; |
|
} |
|
|
|
Sys_FlushStdout(); // flush to stdout to ensure all data was written |
|
|
|
if( s_ld.logfile ) |
|
{ |
|
fprintf( s_ld.logfile, "\n"); |
|
fprintf( s_ld.logfile, "================================================================================="); |
|
if( host.change_game ) fprintf( s_ld.logfile, "\n\t%s (build %i) %s\n", s_ld.title, Q_buildnum(), event_name ); |
|
else fprintf( s_ld.logfile, "\n\t%s (build %i) %s at %s\n", s_ld.title, Q_buildnum(), event_name, Q_timestamp( TIME_FULL )); |
|
fprintf( s_ld.logfile, "=================================================================================\n"); |
|
|
|
fclose( s_ld.logfile ); |
|
s_ld.logfile = NULL; |
|
} |
|
} |
|
|
|
static void Sys_PrintColorized( const char *logtime, const char *msg ) |
|
{ |
|
char colored[4096]; |
|
int len = 0; |
|
|
|
while( *msg && ( len < 4090 ) ) |
|
{ |
|
static char q3ToAnsi[ 8 ] = |
|
{ |
|
'0', // COLOR_BLACK |
|
'1', // COLOR_RED |
|
'2', // COLOR_GREEN |
|
'3', // COLOR_YELLOW |
|
'4', // COLOR_BLUE |
|
'6', // COLOR_CYAN |
|
'5', // COLOR_MAGENTA |
|
0 // COLOR_WHITE |
|
}; |
|
|
|
if( IsColorString( msg ) ) |
|
{ |
|
int color; |
|
|
|
msg++; |
|
color = q3ToAnsi[ *msg++ % 8 ]; |
|
colored[len++] = '\033'; |
|
colored[len++] = '['; |
|
if( color ) |
|
{ |
|
colored[len++] = '3'; |
|
colored[len++] = color; |
|
} |
|
else |
|
colored[len++] = '0'; |
|
colored[len++] = 'm'; |
|
} |
|
else |
|
colored[len++] = *msg++; |
|
} |
|
colored[len] = 0; |
|
|
|
printf( "\033[34m%s\033[0m%s\033[0m", logtime, colored ); |
|
} |
|
|
|
static void Sys_PrintFile( int fd, const char *logtime, const char *msg ) |
|
{ |
|
const char *p = msg; |
|
|
|
write( fd, logtime, Q_strlen( logtime ) ); |
|
|
|
while( p && *p ) |
|
{ |
|
p = Q_strchr( msg, '^' ); |
|
|
|
if( p == NULL ) |
|
{ |
|
write( fd, msg, Q_strlen( msg )); |
|
break; |
|
} |
|
else if( IsColorString( p )) |
|
{ |
|
if( p != msg ) |
|
{ |
|
write( fd, msg, p - msg ); |
|
} |
|
msg = p + 2; |
|
} |
|
else |
|
{ |
|
write( fd, msg, p - msg + 1 ); |
|
msg = p + 1; |
|
} |
|
} |
|
} |
|
|
|
static void Sys_PrintStdout( const char *logtime, const char *msg ) |
|
{ |
|
#if XASH_MOBILE_PLATFORM |
|
static char buf[MAX_PRINT_MSG]; |
|
|
|
// strip color codes |
|
COM_StripColors( msg, buf ); |
|
|
|
// platform-specific output |
|
#if XASH_ANDROID && !XASH_DEDICATED |
|
__android_log_print( ANDROID_LOG_DEBUG, "Xash", "%s", buf ); |
|
#endif // XASH_ANDROID && !XASH_DEDICATED |
|
|
|
#if TARGET_OS_IOS |
|
void IOS_Log( const char * ); |
|
IOS_Log( buf ); |
|
#endif // TARGET_OS_IOS |
|
#else // XASH_MOBILE_PLATFORM |
|
Sys_PrintFile( STDOUT_FILENO, logtime, msg ); |
|
#endif |
|
} |
|
|
|
void Sys_PrintLog( const char *pMsg ) |
|
{ |
|
time_t crt_time; |
|
const struct tm *crt_tm; |
|
char logtime[32] = ""; |
|
static char lastchar; |
|
|
|
time( &crt_time ); |
|
crt_tm = localtime( &crt_time ); |
|
|
|
if( !lastchar || lastchar == '\n') |
|
strftime( logtime, sizeof( logtime ), "[%H:%M:%S] ", crt_tm ); //short time |
|
|
|
// spew to stdout |
|
#ifdef XASH_COLORIZE_CONSOLE |
|
Sys_PrintColorized( logtime, pMsg ); |
|
#elif !XASH_WIN32 // Wcon already does the job |
|
Sys_PrintStdout( logtime, pMsg ); |
|
#endif |
|
Sys_FlushStdout(); |
|
|
|
if( !s_ld.logfile ) |
|
return; |
|
|
|
if( !lastchar || lastchar == '\n') |
|
strftime( logtime, sizeof( logtime ), "[%Y:%m:%d|%H:%M:%S] ", crt_tm ); //full time |
|
|
|
// save last char to detect when line was not ended |
|
lastchar = pMsg[Q_strlen( pMsg ) - 1]; |
|
|
|
Sys_PrintFile( s_ld.logfileno, logtime, pMsg ); |
|
Sys_FlushLogfile(); |
|
} |
|
|
|
/* |
|
============================================================================= |
|
|
|
CONSOLE PRINT |
|
|
|
============================================================================= |
|
*/ |
|
/* |
|
============= |
|
Con_Printf |
|
|
|
============= |
|
*/ |
|
void GAME_EXPORT Con_Printf( const char *szFmt, ... ) |
|
{ |
|
static char buffer[MAX_PRINT_MSG]; |
|
va_list args; |
|
|
|
if( !host.allow_console ) |
|
return; |
|
|
|
va_start( args, szFmt ); |
|
Q_vsnprintf( buffer, sizeof( buffer ), szFmt, args ); |
|
va_end( args ); |
|
|
|
Sys_Print( buffer ); |
|
} |
|
|
|
/* |
|
============= |
|
Con_DPrintf |
|
|
|
============= |
|
*/ |
|
void GAME_EXPORT Con_DPrintf( const char *szFmt, ... ) |
|
{ |
|
static char buffer[MAX_PRINT_MSG]; |
|
va_list args; |
|
|
|
if( host_developer.value < DEV_NORMAL ) |
|
return; |
|
|
|
va_start( args, szFmt ); |
|
Q_vsnprintf( buffer, sizeof( buffer ), szFmt, args ); |
|
va_end( args ); |
|
|
|
if( buffer[0] == '0' && buffer[1] == '\n' && buffer[2] == '\0' ) |
|
return; // hlrally spam |
|
|
|
Sys_Print( buffer ); |
|
} |
|
|
|
/* |
|
============= |
|
Con_Reportf |
|
|
|
============= |
|
*/ |
|
void Con_Reportf( const char *szFmt, ... ) |
|
{ |
|
static char buffer[MAX_PRINT_MSG]; |
|
va_list args; |
|
|
|
if( host_developer.value < DEV_EXTENDED ) |
|
return; |
|
|
|
va_start( args, szFmt ); |
|
Q_vsnprintf( buffer, sizeof( buffer ), szFmt, args ); |
|
va_end( args ); |
|
|
|
Sys_Print( buffer ); |
|
} |
|
|
|
|
|
#if XASH_MESSAGEBOX == MSGBOX_STDERR |
|
void Platform_MessageBox( const char *title, const char *message, qboolean parentMainWindow ) |
|
{ |
|
fprintf( stderr, |
|
"======================================\n" |
|
"%s: %s\n" |
|
"======================================\n", title, message ); |
|
} |
|
#endif |
|
|
|
|