123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544 |
- /*
- ===========================================================================
- Doom 3 BFG Edition GPL Source Code
- Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
- This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
- Doom 3 BFG Edition Source Code 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.
- Doom 3 BFG Edition Source Code 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.
- You should have received a copy of the GNU General Public License
- along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
- In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
- If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
- ===========================================================================
- */
- #include "../idlib/precompiled.h"
- #pragma hdrstop
- #include "Common_local.h"
- idCVar com_logFile( "logFile", "0", CVAR_SYSTEM | CVAR_NOCHEAT, "1 = buffer log, 2 = flush after each print", 0, 2, idCmdSystem::ArgCompletion_Integer<0,2> );
- idCVar com_logFileName( "logFileName", "qconsole.log", CVAR_SYSTEM | CVAR_NOCHEAT, "name of log file, if empty, qconsole.log will be used" );
- idCVar com_timestampPrints( "com_timestampPrints", "0", CVAR_SYSTEM, "print time with each console print, 1 = msec, 2 = sec", 0, 2, idCmdSystem::ArgCompletion_Integer<0,2> );
- #ifndef ID_RETAIL
- idCVar com_printFilter( "com_printFilter", "", CVAR_SYSTEM, "only print lines that contain this, add multiple filters with a ; delimeter");
- #endif
- /*
- ==================
- idCommonLocal::BeginRedirect
- ==================
- */
- void idCommonLocal::BeginRedirect( char *buffer, int buffersize, void (*flush)( const char *) ) {
- if ( !buffer || !buffersize || !flush ) {
- return;
- }
- rd_buffer = buffer;
- rd_buffersize = buffersize;
- rd_flush = flush;
- *rd_buffer = 0;
- }
- /*
- ==================
- idCommonLocal::EndRedirect
- ==================
- */
- void idCommonLocal::EndRedirect() {
- if ( rd_flush && rd_buffer[ 0 ] ) {
- rd_flush( rd_buffer );
- }
- rd_buffer = NULL;
- rd_buffersize = 0;
- rd_flush = NULL;
- }
- /*
- ==================
- idCommonLocal::CloseLogFile
- ==================
- */
- void idCommonLocal::CloseLogFile() {
- if ( logFile ) {
- com_logFile.SetBool( false ); // make sure no further VPrintf attempts to open the log file again
- fileSystem->CloseFile( logFile );
- logFile = NULL;
- }
- }
- /*
- ==================
- idCommonLocal::SetRefreshOnPrint
- ==================
- */
- void idCommonLocal::SetRefreshOnPrint( bool set ) {
- com_refreshOnPrint = set;
- }
- /*
- ==================
- idCommonLocal::VPrintf
- A raw string should NEVER be passed as fmt, because of "%f" type crashes.
- ==================
- */
- void idCommonLocal::VPrintf( const char *fmt, va_list args ) {
- static bool logFileFailed = false;
- // if the cvar system is not initialized
- if ( !cvarSystem->IsInitialized() ) {
- return;
- }
- // optionally put a timestamp at the beginning of each print,
- // so we can see how long different init sections are taking
- int timeLength = 0;
- char msg[MAX_PRINT_MSG_SIZE];
- msg[ 0 ] = '\0';
- if ( com_timestampPrints.GetInteger() ) {
- int t = Sys_Milliseconds();
- if ( com_timestampPrints.GetInteger() == 1 ) {
- sprintf( msg, "[%5.2f]", t * 0.001f );
- } else {
- sprintf( msg, "[%i]", t );
- }
- }
- timeLength = strlen( msg );
- // don't overflow
- if ( idStr::vsnPrintf( msg+timeLength, MAX_PRINT_MSG_SIZE-timeLength-1, fmt, args ) < 0 ) {
- msg[sizeof(msg)-2] = '\n'; msg[sizeof(msg)-1] = '\0'; // avoid output garbling
- Sys_Printf( "idCommon::VPrintf: truncated to %d characters\n", strlen(msg)-1 );
- }
- if ( rd_buffer ) {
- if ( (int)( strlen( msg ) + strlen( rd_buffer ) ) > ( rd_buffersize - 1 ) ) {
- rd_flush( rd_buffer );
- *rd_buffer = 0;
- }
- strcat( rd_buffer, msg );
- return;
- }
- #ifndef ID_RETAIL
- if ( com_printFilter.GetString() != NULL && com_printFilter.GetString()[ 0 ] != '\0' ) {
- idStrStatic< 4096 > filterBuf = com_printFilter.GetString();
- idStrStatic< 4096 > msgBuf = msg;
- filterBuf.ToLower();
- msgBuf.ToLower();
- char *sp = strtok( &filterBuf[ 0 ], ";" );
- bool p = false;
- for( ; sp != NULL ; ) {
- if ( strstr( msgBuf, sp ) != NULL ) {
- p = true;
- break;
- }
- sp = strtok( NULL, ";" );
- }
- if ( !p ) {
- return;
- }
- }
- #endif
- if ( !idLib::IsMainThread() ) {
- OutputDebugString( msg );
- return;
- }
- // echo to console buffer
- console->Print( msg );
- // remove any color codes
- idStr::RemoveColors( msg );
- // echo to dedicated console and early console
- Sys_Printf( "%s", msg );
- // print to script debugger server
- // DebuggerServerPrint( msg );
- #if 0 // !@#
- #if defined(_DEBUG) && defined(WIN32)
- if ( strlen( msg ) < 512 ) {
- TRACE( msg );
- }
- #endif
- #endif
- // logFile
- if ( com_logFile.GetInteger() && !logFileFailed && fileSystem->IsInitialized() ) {
- static bool recursing;
- if ( !logFile && !recursing ) {
- const char *fileName = com_logFileName.GetString()[0] ? com_logFileName.GetString() : "qconsole.log";
- // fileSystem->OpenFileWrite can cause recursive prints into here
- recursing = true;
- logFile = fileSystem->OpenFileWrite( fileName );
- if ( !logFile ) {
- logFileFailed = true;
- FatalError( "failed to open log file '%s'\n", fileName );
- }
- recursing = false;
- if ( com_logFile.GetInteger() > 1 ) {
- // force it to not buffer so we get valid
- // data even if we are crashing
- logFile->ForceFlush();
- }
- time_t aclock;
- time( &aclock );
- struct tm * newtime = localtime( &aclock );
- Printf( "log file '%s' opened on %s\n", fileName, asctime( newtime ) );
- }
- if ( logFile ) {
- logFile->Write( msg, strlen( msg ) );
- logFile->Flush(); // ForceFlush doesn't help a whole lot
- }
- }
- // don't trigger any updates if we are in the process of doing a fatal error
- if ( com_errorEntered != ERP_FATAL ) {
- // update the console if we are in a long-running command, like dmap
- if ( com_refreshOnPrint ) {
- const bool captureToImage = false;
- UpdateScreen( captureToImage );
- }
- }
- }
- /*
- ==================
- idCommonLocal::Printf
- Both client and server can use this, and it will output to the appropriate place.
- A raw string should NEVER be passed as fmt, because of "%f" type crashers.
- ==================
- */
- void idCommonLocal::Printf( const char *fmt, ... ) {
- va_list argptr;
- va_start( argptr, fmt );
- VPrintf( fmt, argptr );
- va_end( argptr );
- }
- /*
- ==================
- idCommonLocal::DPrintf
- prints message that only shows up if the "developer" cvar is set
- ==================
- */
- void idCommonLocal::DPrintf( const char *fmt, ... ) {
- va_list argptr;
- char msg[MAX_PRINT_MSG_SIZE];
-
- if ( !cvarSystem->IsInitialized() || !com_developer.GetBool() ) {
- return; // don't confuse non-developers with techie stuff...
- }
- va_start( argptr, fmt );
- idStr::vsnPrintf( msg, sizeof(msg), fmt, argptr );
- va_end( argptr );
- msg[sizeof(msg)-1] = '\0';
-
- // never refresh the screen, which could cause reentrency problems
- bool temp = com_refreshOnPrint;
- com_refreshOnPrint = false;
- Printf( S_COLOR_RED"%s", msg );
- com_refreshOnPrint = temp;
- }
- /*
- ==================
- idCommonLocal::DWarning
- prints warning message in yellow that only shows up if the "developer" cvar is set
- ==================
- */
- void idCommonLocal::DWarning( const char *fmt, ... ) {
- va_list argptr;
- char msg[MAX_PRINT_MSG_SIZE];
-
- if ( !com_developer.GetBool() ) {
- return; // don't confuse non-developers with techie stuff...
- }
- va_start( argptr, fmt );
- idStr::vsnPrintf( msg, sizeof(msg), fmt, argptr );
- va_end( argptr );
- msg[sizeof(msg)-1] = '\0';
- Printf( S_COLOR_YELLOW"WARNING: %s\n", msg );
- }
- /*
- ==================
- idCommonLocal::Warning
- prints WARNING %s and adds the warning message to a queue to be printed later on
- ==================
- */
- void idCommonLocal::Warning( const char *fmt, ... ) {
- va_list argptr;
- char msg[MAX_PRINT_MSG_SIZE];
-
- if ( !idLib::IsMainThread() ) {
- return; // not thread safe!
- }
- va_start( argptr, fmt );
- idStr::vsnPrintf( msg, sizeof(msg), fmt, argptr );
- va_end( argptr );
- msg[sizeof(msg)-1] = 0;
- Printf( S_COLOR_YELLOW "WARNING: " S_COLOR_RED "%s\n", msg );
- if ( warningList.Num() < MAX_WARNING_LIST ) {
- warningList.AddUnique( msg );
- }
- }
- /*
- ==================
- idCommonLocal::PrintWarnings
- ==================
- */
- void idCommonLocal::PrintWarnings() {
- int i;
- if ( !warningList.Num() ) {
- return;
- }
- Printf( "------------- Warnings ---------------\n" );
- Printf( "during %s...\n", warningCaption.c_str() );
- for ( i = 0; i < warningList.Num(); i++ ) {
- Printf( S_COLOR_YELLOW "WARNING: " S_COLOR_RED "%s\n", warningList[i].c_str() );
- }
- if ( warningList.Num() ) {
- if ( warningList.Num() >= MAX_WARNING_LIST ) {
- Printf( "more than %d warnings\n", MAX_WARNING_LIST );
- } else {
- Printf( "%d warnings\n", warningList.Num() );
- }
- }
- }
- /*
- ==================
- idCommonLocal::ClearWarnings
- ==================
- */
- void idCommonLocal::ClearWarnings( const char *reason ) {
- warningCaption = reason;
- warningList.Clear();
- }
- /*
- ==================
- idCommonLocal::DumpWarnings
- ==================
- */
- void idCommonLocal::DumpWarnings() {
- int i;
- idFile *warningFile;
- if ( !warningList.Num() ) {
- return;
- }
- warningFile = fileSystem->OpenFileWrite( "warnings.txt", "fs_savepath" );
- if ( warningFile ) {
- warningFile->Printf( "------------- Warnings ---------------\n\n" );
- warningFile->Printf( "during %s...\n", warningCaption.c_str() );
- for ( i = 0; i < warningList.Num(); i++ ) {
- warningList[i].RemoveColors();
- warningFile->Printf( "WARNING: %s\n", warningList[i].c_str() );
- }
- if ( warningList.Num() >= MAX_WARNING_LIST ) {
- warningFile->Printf( "\nmore than %d warnings!\n", MAX_WARNING_LIST );
- } else {
- warningFile->Printf( "\n%d warnings.\n", warningList.Num() );
- }
- warningFile->Printf( "\n\n-------------- Errors ---------------\n\n" );
- for ( i = 0; i < errorList.Num(); i++ ) {
- errorList[i].RemoveColors();
- warningFile->Printf( "ERROR: %s", errorList[i].c_str() );
- }
- warningFile->ForceFlush();
- fileSystem->CloseFile( warningFile );
- #ifndef ID_DEBUG
- idStr osPath;
- osPath = fileSystem->RelativePathToOSPath( "warnings.txt", "fs_savepath" );
- WinExec( va( "Notepad.exe %s", osPath.c_str() ), SW_SHOW );
- #endif
- }
- }
- /*
- ==================
- idCommonLocal::Error
- ==================
- */
- void idCommonLocal::Error( const char *fmt, ... ) {
- va_list argptr;
- static int lastErrorTime;
- static int errorCount;
- int currentTime;
- errorParm_t code = ERP_DROP;
- // always turn this off after an error
- com_refreshOnPrint = false;
- if ( com_productionMode.GetInteger() == 3 ) {
- Sys_Quit();
- }
- // when we are running automated scripts, make sure we
- // know if anything failed
- if ( cvarSystem->GetCVarInteger( "fs_copyfiles" ) ) {
- code = ERP_FATAL;
- }
- // if we don't have GL running, make it a fatal error
- if ( !renderSystem->IsOpenGLRunning() ) {
- code = ERP_FATAL;
- }
- // if we got a recursive error, make it fatal
- if ( com_errorEntered ) {
- // if we are recursively erroring while exiting
- // from a fatal error, just kill the entire
- // process immediately, which will prevent a
- // full screen rendering window covering the
- // error dialog
- if ( com_errorEntered == ERP_FATAL ) {
- Sys_Quit();
- }
- code = ERP_FATAL;
- }
- // if we are getting a solid stream of ERP_DROP, do an ERP_FATAL
- currentTime = Sys_Milliseconds();
- if ( currentTime - lastErrorTime < 100 ) {
- if ( ++errorCount > 3 ) {
- code = ERP_FATAL;
- }
- } else {
- errorCount = 0;
- }
- lastErrorTime = currentTime;
- com_errorEntered = code;
- va_start (argptr,fmt);
- idStr::vsnPrintf( errorMessage, sizeof(errorMessage), fmt, argptr );
- va_end (argptr);
- errorMessage[sizeof(errorMessage)-1] = '\0';
- // copy the error message to the clip board
- Sys_SetClipboardData( errorMessage );
- // add the message to the error list
- errorList.AddUnique( errorMessage );
- Stop();
- if ( code == ERP_DISCONNECT ) {
- com_errorEntered = ERP_NONE;
- throw idException( errorMessage );
- } else if ( code == ERP_DROP ) {
- Printf( "********************\nERROR: %s\n********************\n", errorMessage );
- com_errorEntered = ERP_NONE;
- throw idException( errorMessage );
- } else {
- Printf( "********************\nERROR: %s\n********************\n", errorMessage );
- }
- if ( cvarSystem->GetCVarBool( "r_fullscreen" ) ) {
- cmdSystem->BufferCommandText( CMD_EXEC_NOW, "vid_restart partial windowed\n" );
- }
- Sys_Error( "%s", errorMessage );
- }
- /*
- ==================
- idCommonLocal::FatalError
- Dump out of the game to a system dialog
- ==================
- */
- void idCommonLocal::FatalError( const char *fmt, ... ) {
- va_list argptr;
- if ( com_productionMode.GetInteger() == 3 ) {
- Sys_Quit();
- }
- // if we got a recursive error, make it fatal
- if ( com_errorEntered ) {
- // if we are recursively erroring while exiting
- // from a fatal error, just kill the entire
- // process immediately, which will prevent a
- // full screen rendering window covering the
- // error dialog
- Sys_Printf( "FATAL: recursed fatal error:\n%s\n", errorMessage );
- va_start( argptr, fmt );
- idStr::vsnPrintf( errorMessage, sizeof(errorMessage), fmt, argptr );
- va_end( argptr );
- errorMessage[sizeof(errorMessage)-1] = '\0';
- Sys_Printf( "%s\n", errorMessage );
- // write the console to a log file?
- Sys_Quit();
- }
- com_errorEntered = ERP_FATAL;
- va_start( argptr, fmt );
- idStr::vsnPrintf( errorMessage, sizeof(errorMessage), fmt, argptr );
- va_end( argptr );
- errorMessage[sizeof(errorMessage)-1] = '\0';
- if ( cvarSystem->GetCVarBool( "r_fullscreen" ) ) {
- cmdSystem->BufferCommandText( CMD_EXEC_NOW, "vid_restart partial windowed\n" );
- }
- Sys_SetFatalError( errorMessage );
- Sys_Error( "%s", errorMessage );
- }
|