Common_printf.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544
  1. /*
  2. ===========================================================================
  3. Doom 3 BFG Edition GPL Source Code
  4. Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
  6. Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. 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.
  17. 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.
  18. ===========================================================================
  19. */
  20. #include "../idlib/precompiled.h"
  21. #pragma hdrstop
  22. #include "Common_local.h"
  23. idCVar com_logFile( "logFile", "0", CVAR_SYSTEM | CVAR_NOCHEAT, "1 = buffer log, 2 = flush after each print", 0, 2, idCmdSystem::ArgCompletion_Integer<0,2> );
  24. idCVar com_logFileName( "logFileName", "qconsole.log", CVAR_SYSTEM | CVAR_NOCHEAT, "name of log file, if empty, qconsole.log will be used" );
  25. 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> );
  26. #ifndef ID_RETAIL
  27. idCVar com_printFilter( "com_printFilter", "", CVAR_SYSTEM, "only print lines that contain this, add multiple filters with a ; delimeter");
  28. #endif
  29. /*
  30. ==================
  31. idCommonLocal::BeginRedirect
  32. ==================
  33. */
  34. void idCommonLocal::BeginRedirect( char *buffer, int buffersize, void (*flush)( const char *) ) {
  35. if ( !buffer || !buffersize || !flush ) {
  36. return;
  37. }
  38. rd_buffer = buffer;
  39. rd_buffersize = buffersize;
  40. rd_flush = flush;
  41. *rd_buffer = 0;
  42. }
  43. /*
  44. ==================
  45. idCommonLocal::EndRedirect
  46. ==================
  47. */
  48. void idCommonLocal::EndRedirect() {
  49. if ( rd_flush && rd_buffer[ 0 ] ) {
  50. rd_flush( rd_buffer );
  51. }
  52. rd_buffer = NULL;
  53. rd_buffersize = 0;
  54. rd_flush = NULL;
  55. }
  56. /*
  57. ==================
  58. idCommonLocal::CloseLogFile
  59. ==================
  60. */
  61. void idCommonLocal::CloseLogFile() {
  62. if ( logFile ) {
  63. com_logFile.SetBool( false ); // make sure no further VPrintf attempts to open the log file again
  64. fileSystem->CloseFile( logFile );
  65. logFile = NULL;
  66. }
  67. }
  68. /*
  69. ==================
  70. idCommonLocal::SetRefreshOnPrint
  71. ==================
  72. */
  73. void idCommonLocal::SetRefreshOnPrint( bool set ) {
  74. com_refreshOnPrint = set;
  75. }
  76. /*
  77. ==================
  78. idCommonLocal::VPrintf
  79. A raw string should NEVER be passed as fmt, because of "%f" type crashes.
  80. ==================
  81. */
  82. void idCommonLocal::VPrintf( const char *fmt, va_list args ) {
  83. static bool logFileFailed = false;
  84. // if the cvar system is not initialized
  85. if ( !cvarSystem->IsInitialized() ) {
  86. return;
  87. }
  88. // optionally put a timestamp at the beginning of each print,
  89. // so we can see how long different init sections are taking
  90. int timeLength = 0;
  91. char msg[MAX_PRINT_MSG_SIZE];
  92. msg[ 0 ] = '\0';
  93. if ( com_timestampPrints.GetInteger() ) {
  94. int t = Sys_Milliseconds();
  95. if ( com_timestampPrints.GetInteger() == 1 ) {
  96. sprintf( msg, "[%5.2f]", t * 0.001f );
  97. } else {
  98. sprintf( msg, "[%i]", t );
  99. }
  100. }
  101. timeLength = strlen( msg );
  102. // don't overflow
  103. if ( idStr::vsnPrintf( msg+timeLength, MAX_PRINT_MSG_SIZE-timeLength-1, fmt, args ) < 0 ) {
  104. msg[sizeof(msg)-2] = '\n'; msg[sizeof(msg)-1] = '\0'; // avoid output garbling
  105. Sys_Printf( "idCommon::VPrintf: truncated to %d characters\n", strlen(msg)-1 );
  106. }
  107. if ( rd_buffer ) {
  108. if ( (int)( strlen( msg ) + strlen( rd_buffer ) ) > ( rd_buffersize - 1 ) ) {
  109. rd_flush( rd_buffer );
  110. *rd_buffer = 0;
  111. }
  112. strcat( rd_buffer, msg );
  113. return;
  114. }
  115. #ifndef ID_RETAIL
  116. if ( com_printFilter.GetString() != NULL && com_printFilter.GetString()[ 0 ] != '\0' ) {
  117. idStrStatic< 4096 > filterBuf = com_printFilter.GetString();
  118. idStrStatic< 4096 > msgBuf = msg;
  119. filterBuf.ToLower();
  120. msgBuf.ToLower();
  121. char *sp = strtok( &filterBuf[ 0 ], ";" );
  122. bool p = false;
  123. for( ; sp != NULL ; ) {
  124. if ( strstr( msgBuf, sp ) != NULL ) {
  125. p = true;
  126. break;
  127. }
  128. sp = strtok( NULL, ";" );
  129. }
  130. if ( !p ) {
  131. return;
  132. }
  133. }
  134. #endif
  135. if ( !idLib::IsMainThread() ) {
  136. OutputDebugString( msg );
  137. return;
  138. }
  139. // echo to console buffer
  140. console->Print( msg );
  141. // remove any color codes
  142. idStr::RemoveColors( msg );
  143. // echo to dedicated console and early console
  144. Sys_Printf( "%s", msg );
  145. // print to script debugger server
  146. // DebuggerServerPrint( msg );
  147. #if 0 // !@#
  148. #if defined(_DEBUG) && defined(WIN32)
  149. if ( strlen( msg ) < 512 ) {
  150. TRACE( msg );
  151. }
  152. #endif
  153. #endif
  154. // logFile
  155. if ( com_logFile.GetInteger() && !logFileFailed && fileSystem->IsInitialized() ) {
  156. static bool recursing;
  157. if ( !logFile && !recursing ) {
  158. const char *fileName = com_logFileName.GetString()[0] ? com_logFileName.GetString() : "qconsole.log";
  159. // fileSystem->OpenFileWrite can cause recursive prints into here
  160. recursing = true;
  161. logFile = fileSystem->OpenFileWrite( fileName );
  162. if ( !logFile ) {
  163. logFileFailed = true;
  164. FatalError( "failed to open log file '%s'\n", fileName );
  165. }
  166. recursing = false;
  167. if ( com_logFile.GetInteger() > 1 ) {
  168. // force it to not buffer so we get valid
  169. // data even if we are crashing
  170. logFile->ForceFlush();
  171. }
  172. time_t aclock;
  173. time( &aclock );
  174. struct tm * newtime = localtime( &aclock );
  175. Printf( "log file '%s' opened on %s\n", fileName, asctime( newtime ) );
  176. }
  177. if ( logFile ) {
  178. logFile->Write( msg, strlen( msg ) );
  179. logFile->Flush(); // ForceFlush doesn't help a whole lot
  180. }
  181. }
  182. // don't trigger any updates if we are in the process of doing a fatal error
  183. if ( com_errorEntered != ERP_FATAL ) {
  184. // update the console if we are in a long-running command, like dmap
  185. if ( com_refreshOnPrint ) {
  186. const bool captureToImage = false;
  187. UpdateScreen( captureToImage );
  188. }
  189. }
  190. }
  191. /*
  192. ==================
  193. idCommonLocal::Printf
  194. Both client and server can use this, and it will output to the appropriate place.
  195. A raw string should NEVER be passed as fmt, because of "%f" type crashers.
  196. ==================
  197. */
  198. void idCommonLocal::Printf( const char *fmt, ... ) {
  199. va_list argptr;
  200. va_start( argptr, fmt );
  201. VPrintf( fmt, argptr );
  202. va_end( argptr );
  203. }
  204. /*
  205. ==================
  206. idCommonLocal::DPrintf
  207. prints message that only shows up if the "developer" cvar is set
  208. ==================
  209. */
  210. void idCommonLocal::DPrintf( const char *fmt, ... ) {
  211. va_list argptr;
  212. char msg[MAX_PRINT_MSG_SIZE];
  213. if ( !cvarSystem->IsInitialized() || !com_developer.GetBool() ) {
  214. return; // don't confuse non-developers with techie stuff...
  215. }
  216. va_start( argptr, fmt );
  217. idStr::vsnPrintf( msg, sizeof(msg), fmt, argptr );
  218. va_end( argptr );
  219. msg[sizeof(msg)-1] = '\0';
  220. // never refresh the screen, which could cause reentrency problems
  221. bool temp = com_refreshOnPrint;
  222. com_refreshOnPrint = false;
  223. Printf( S_COLOR_RED"%s", msg );
  224. com_refreshOnPrint = temp;
  225. }
  226. /*
  227. ==================
  228. idCommonLocal::DWarning
  229. prints warning message in yellow that only shows up if the "developer" cvar is set
  230. ==================
  231. */
  232. void idCommonLocal::DWarning( const char *fmt, ... ) {
  233. va_list argptr;
  234. char msg[MAX_PRINT_MSG_SIZE];
  235. if ( !com_developer.GetBool() ) {
  236. return; // don't confuse non-developers with techie stuff...
  237. }
  238. va_start( argptr, fmt );
  239. idStr::vsnPrintf( msg, sizeof(msg), fmt, argptr );
  240. va_end( argptr );
  241. msg[sizeof(msg)-1] = '\0';
  242. Printf( S_COLOR_YELLOW"WARNING: %s\n", msg );
  243. }
  244. /*
  245. ==================
  246. idCommonLocal::Warning
  247. prints WARNING %s and adds the warning message to a queue to be printed later on
  248. ==================
  249. */
  250. void idCommonLocal::Warning( const char *fmt, ... ) {
  251. va_list argptr;
  252. char msg[MAX_PRINT_MSG_SIZE];
  253. if ( !idLib::IsMainThread() ) {
  254. return; // not thread safe!
  255. }
  256. va_start( argptr, fmt );
  257. idStr::vsnPrintf( msg, sizeof(msg), fmt, argptr );
  258. va_end( argptr );
  259. msg[sizeof(msg)-1] = 0;
  260. Printf( S_COLOR_YELLOW "WARNING: " S_COLOR_RED "%s\n", msg );
  261. if ( warningList.Num() < MAX_WARNING_LIST ) {
  262. warningList.AddUnique( msg );
  263. }
  264. }
  265. /*
  266. ==================
  267. idCommonLocal::PrintWarnings
  268. ==================
  269. */
  270. void idCommonLocal::PrintWarnings() {
  271. int i;
  272. if ( !warningList.Num() ) {
  273. return;
  274. }
  275. Printf( "------------- Warnings ---------------\n" );
  276. Printf( "during %s...\n", warningCaption.c_str() );
  277. for ( i = 0; i < warningList.Num(); i++ ) {
  278. Printf( S_COLOR_YELLOW "WARNING: " S_COLOR_RED "%s\n", warningList[i].c_str() );
  279. }
  280. if ( warningList.Num() ) {
  281. if ( warningList.Num() >= MAX_WARNING_LIST ) {
  282. Printf( "more than %d warnings\n", MAX_WARNING_LIST );
  283. } else {
  284. Printf( "%d warnings\n", warningList.Num() );
  285. }
  286. }
  287. }
  288. /*
  289. ==================
  290. idCommonLocal::ClearWarnings
  291. ==================
  292. */
  293. void idCommonLocal::ClearWarnings( const char *reason ) {
  294. warningCaption = reason;
  295. warningList.Clear();
  296. }
  297. /*
  298. ==================
  299. idCommonLocal::DumpWarnings
  300. ==================
  301. */
  302. void idCommonLocal::DumpWarnings() {
  303. int i;
  304. idFile *warningFile;
  305. if ( !warningList.Num() ) {
  306. return;
  307. }
  308. warningFile = fileSystem->OpenFileWrite( "warnings.txt", "fs_savepath" );
  309. if ( warningFile ) {
  310. warningFile->Printf( "------------- Warnings ---------------\n\n" );
  311. warningFile->Printf( "during %s...\n", warningCaption.c_str() );
  312. for ( i = 0; i < warningList.Num(); i++ ) {
  313. warningList[i].RemoveColors();
  314. warningFile->Printf( "WARNING: %s\n", warningList[i].c_str() );
  315. }
  316. if ( warningList.Num() >= MAX_WARNING_LIST ) {
  317. warningFile->Printf( "\nmore than %d warnings!\n", MAX_WARNING_LIST );
  318. } else {
  319. warningFile->Printf( "\n%d warnings.\n", warningList.Num() );
  320. }
  321. warningFile->Printf( "\n\n-------------- Errors ---------------\n\n" );
  322. for ( i = 0; i < errorList.Num(); i++ ) {
  323. errorList[i].RemoveColors();
  324. warningFile->Printf( "ERROR: %s", errorList[i].c_str() );
  325. }
  326. warningFile->ForceFlush();
  327. fileSystem->CloseFile( warningFile );
  328. #ifndef ID_DEBUG
  329. idStr osPath;
  330. osPath = fileSystem->RelativePathToOSPath( "warnings.txt", "fs_savepath" );
  331. WinExec( va( "Notepad.exe %s", osPath.c_str() ), SW_SHOW );
  332. #endif
  333. }
  334. }
  335. /*
  336. ==================
  337. idCommonLocal::Error
  338. ==================
  339. */
  340. void idCommonLocal::Error( const char *fmt, ... ) {
  341. va_list argptr;
  342. static int lastErrorTime;
  343. static int errorCount;
  344. int currentTime;
  345. errorParm_t code = ERP_DROP;
  346. // always turn this off after an error
  347. com_refreshOnPrint = false;
  348. if ( com_productionMode.GetInteger() == 3 ) {
  349. Sys_Quit();
  350. }
  351. // when we are running automated scripts, make sure we
  352. // know if anything failed
  353. if ( cvarSystem->GetCVarInteger( "fs_copyfiles" ) ) {
  354. code = ERP_FATAL;
  355. }
  356. // if we don't have GL running, make it a fatal error
  357. if ( !renderSystem->IsOpenGLRunning() ) {
  358. code = ERP_FATAL;
  359. }
  360. // if we got a recursive error, make it fatal
  361. if ( com_errorEntered ) {
  362. // if we are recursively erroring while exiting
  363. // from a fatal error, just kill the entire
  364. // process immediately, which will prevent a
  365. // full screen rendering window covering the
  366. // error dialog
  367. if ( com_errorEntered == ERP_FATAL ) {
  368. Sys_Quit();
  369. }
  370. code = ERP_FATAL;
  371. }
  372. // if we are getting a solid stream of ERP_DROP, do an ERP_FATAL
  373. currentTime = Sys_Milliseconds();
  374. if ( currentTime - lastErrorTime < 100 ) {
  375. if ( ++errorCount > 3 ) {
  376. code = ERP_FATAL;
  377. }
  378. } else {
  379. errorCount = 0;
  380. }
  381. lastErrorTime = currentTime;
  382. com_errorEntered = code;
  383. va_start (argptr,fmt);
  384. idStr::vsnPrintf( errorMessage, sizeof(errorMessage), fmt, argptr );
  385. va_end (argptr);
  386. errorMessage[sizeof(errorMessage)-1] = '\0';
  387. // copy the error message to the clip board
  388. Sys_SetClipboardData( errorMessage );
  389. // add the message to the error list
  390. errorList.AddUnique( errorMessage );
  391. Stop();
  392. if ( code == ERP_DISCONNECT ) {
  393. com_errorEntered = ERP_NONE;
  394. throw idException( errorMessage );
  395. } else if ( code == ERP_DROP ) {
  396. Printf( "********************\nERROR: %s\n********************\n", errorMessage );
  397. com_errorEntered = ERP_NONE;
  398. throw idException( errorMessage );
  399. } else {
  400. Printf( "********************\nERROR: %s\n********************\n", errorMessage );
  401. }
  402. if ( cvarSystem->GetCVarBool( "r_fullscreen" ) ) {
  403. cmdSystem->BufferCommandText( CMD_EXEC_NOW, "vid_restart partial windowed\n" );
  404. }
  405. Sys_Error( "%s", errorMessage );
  406. }
  407. /*
  408. ==================
  409. idCommonLocal::FatalError
  410. Dump out of the game to a system dialog
  411. ==================
  412. */
  413. void idCommonLocal::FatalError( const char *fmt, ... ) {
  414. va_list argptr;
  415. if ( com_productionMode.GetInteger() == 3 ) {
  416. Sys_Quit();
  417. }
  418. // if we got a recursive error, make it fatal
  419. if ( com_errorEntered ) {
  420. // if we are recursively erroring while exiting
  421. // from a fatal error, just kill the entire
  422. // process immediately, which will prevent a
  423. // full screen rendering window covering the
  424. // error dialog
  425. Sys_Printf( "FATAL: recursed fatal error:\n%s\n", errorMessage );
  426. va_start( argptr, fmt );
  427. idStr::vsnPrintf( errorMessage, sizeof(errorMessage), fmt, argptr );
  428. va_end( argptr );
  429. errorMessage[sizeof(errorMessage)-1] = '\0';
  430. Sys_Printf( "%s\n", errorMessage );
  431. // write the console to a log file?
  432. Sys_Quit();
  433. }
  434. com_errorEntered = ERP_FATAL;
  435. va_start( argptr, fmt );
  436. idStr::vsnPrintf( errorMessage, sizeof(errorMessage), fmt, argptr );
  437. va_end( argptr );
  438. errorMessage[sizeof(errorMessage)-1] = '\0';
  439. if ( cvarSystem->GetCVarBool( "r_fullscreen" ) ) {
  440. cmdSystem->BufferCommandText( CMD_EXEC_NOW, "vid_restart partial windowed\n" );
  441. }
  442. Sys_SetFatalError( errorMessage );
  443. Sys_Error( "%s", errorMessage );
  444. }