1 /*
2 ===========================================================================
3 Copyright (C) 1999 - 2005, Id Software, Inc.
4 Copyright (C) 2000 - 2013, Raven Software, Inc.
5 Copyright (C) 2001 - 2013, Activision, Inc.
6 Copyright (C) 2005 - 2015, ioquake3 contributors
7 Copyright (C) 2013 - 2015, OpenJK contributors
8 
9 This file is part of the OpenJK source code.
10 
11 OpenJK is free software; you can redistribute it and/or modify it
12 under the terms of the GNU General Public License version 2 as
13 published by the Free Software Foundation.
14 
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 GNU General Public License for more details.
19 
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, see <http://www.gnu.org/licenses/>.
22 ===========================================================================
23 */
24 
25 // common.c -- misc functions used in client and server
26 
27 #include "stringed_ingame.h"
28 #include "qcommon/cm_public.h"
29 #include "qcommon/game_version.h"
30 #include "qcommon/q_version.h"
31 #include "../server/NPCNav/navigator.h"
32 #include "../shared/sys/sys_local.h"
33 #if defined(_WIN32)
34 #define WIN32_LEAN_AND_MEAN
35 #include <windows.h>
36 #endif
37 
38 FILE *debuglogfile;
39 fileHandle_t logfile;
40 fileHandle_t	com_journalFile;			// events are written here
41 fileHandle_t	com_journalDataFile;		// config files are written here
42 
43 cvar_t	*com_speeds;
44 cvar_t	*com_developer;
45 cvar_t	*com_dedicated;
46 cvar_t	*com_timescale;
47 cvar_t	*com_fixedtime;
48 cvar_t	*com_journal;
49 cvar_t	*com_timedemo;
50 cvar_t	*com_sv_running;
51 cvar_t	*com_cl_running;
52 cvar_t	*com_logfile;		// 1 = buffer log, 2 = flush after each print
53 cvar_t	*com_showtrace;
54 
55 cvar_t	*com_optvehtrace;
56 
57 #ifdef G2_PERFORMANCE_ANALYSIS
58 cvar_t	*com_G2Report;
59 #endif
60 
61 cvar_t	*com_version;
62 cvar_t	*com_buildScript;	// for automated data building scripts
63 cvar_t	*com_bootlogo;
64 cvar_t	*cl_paused;
65 cvar_t	*sv_paused;
66 cvar_t	*com_cameraMode;
67 cvar_t  *com_homepath;
68 #ifndef _WIN32
69 cvar_t	*com_ansiColor = NULL;
70 #endif
71 cvar_t	*com_busyWait;
72 
73 cvar_t *com_affinity;
74 
75 // com_speeds times
76 int		time_game;
77 int		time_frontend;		// renderer frontend time
78 int		time_backend;		// renderer backend time
79 
80 int			com_frameTime;
81 int			com_frameNumber;
82 
83 qboolean	com_errorEntered = qfalse;
84 qboolean	com_fullyInitialized = qfalse;
85 
86 char	com_errorMessage[MAXPRINTMSG] = {0};
87 
88 void Com_WriteConfig_f( void );
89 
90 //============================================================================
91 
92 static char	*rd_buffer;
93 static int	rd_buffersize;
94 static void	(*rd_flush)( char *buffer );
95 
Com_BeginRedirect(char * buffer,int buffersize,void (* flush)(char *))96 void Com_BeginRedirect (char *buffer, int buffersize, void (*flush)( char *) )
97 {
98 	if (!buffer || !buffersize || !flush)
99 		return;
100 	rd_buffer = buffer;
101 	rd_buffersize = buffersize;
102 	rd_flush = flush;
103 
104 	*rd_buffer = 0;
105 }
106 
Com_EndRedirect(void)107 void Com_EndRedirect (void)
108 {
109 	if ( rd_flush ) {
110 		rd_flush(rd_buffer);
111 	}
112 
113 	rd_buffer = NULL;
114 	rd_buffersize = 0;
115 	rd_flush = NULL;
116 }
117 
118 /*
119 =============
120 Com_Printf
121 
122 Both client and server can use this, and it will output
123 to the appropriate place.
124 
125 A raw string should NEVER be passed as fmt, because of "%f" type crashers.
126 =============
127 */
Com_Printf(const char * fmt,...)128 void QDECL Com_Printf( const char *fmt, ... ) {
129 	va_list		argptr;
130 	char		msg[MAXPRINTMSG];
131 	static qboolean opening_qconsole = qfalse;
132 
133 	va_start (argptr,fmt);
134 	Q_vsnprintf (msg, sizeof(msg), fmt, argptr);
135 	va_end (argptr);
136 
137 	if ( rd_buffer ) {
138 		if ((strlen (msg) + strlen(rd_buffer)) > (size_t)(rd_buffersize - 1)) {
139 			rd_flush(rd_buffer);
140 			*rd_buffer = 0;
141 		}
142 		Q_strcat(rd_buffer, rd_buffersize, msg);
143     // TTimo nooo .. that would defeat the purpose
144 		//rd_flush(rd_buffer);
145 		//*rd_buffer = 0;
146 		return;
147 	}
148 
149 #ifndef DEDICATED
150 	CL_ConsolePrint( msg );
151 #endif
152 
153 	// echo to dedicated console and early console
154 	Sys_Print( msg );
155 
156 	// logfile
157 	if ( com_logfile && com_logfile->integer ) {
158     // TTimo: only open the qconsole.log if the filesystem is in an initialized state
159     //   also, avoid recursing in the qconsole.log opening (i.e. if fs_debug is on)
160 		if ( !logfile && FS_Initialized() && !opening_qconsole ) {
161 			struct tm *newtime;
162 			time_t aclock;
163 
164 			opening_qconsole = qtrue;
165 
166 			time( &aclock );
167 			newtime = localtime( &aclock );
168 
169 			logfile = FS_FOpenFileWrite( "qconsole.log" );
170 
171 			if ( logfile ) {
172 				Com_Printf( "logfile opened on %s\n", asctime( newtime ) );
173 				if ( com_logfile->integer > 1 ) {
174 					// force it to not buffer so we get valid
175 					// data even if we are crashing
176 					FS_ForceFlush(logfile);
177 				}
178 			}
179 			else {
180 				Com_Printf( "Opening qconsole.log failed!\n" );
181 				Cvar_SetValue( "logfile", 0 );
182 			}
183 		}
184 		opening_qconsole = qfalse;
185 		if ( logfile && FS_Initialized()) {
186 			FS_Write(msg, strlen(msg), logfile);
187 		}
188 	}
189 
190 
191 #if defined(_WIN32) && defined(_DEBUG)
192 	if ( *msg )
193 	{
194 		OutputDebugString ( Q_CleanStr(msg) );
195 		OutputDebugString ("\n");
196 	}
197 #endif
198 }
199 
200 
201 /*
202 ================
203 Com_DPrintf
204 
205 A Com_Printf that only shows up if the "developer" cvar is set
206 ================
207 */
Com_DPrintf(const char * fmt,...)208 void QDECL Com_DPrintf( const char *fmt, ...) {
209 	va_list		argptr;
210 	char		msg[MAXPRINTMSG];
211 
212 	if ( !com_developer || !com_developer->integer ) {
213 		return;			// don't confuse non-developers with techie stuff...
214 	}
215 
216 	va_start (argptr,fmt);
217 	Q_vsnprintf (msg, sizeof(msg), fmt, argptr);
218 	va_end (argptr);
219 
220 	Com_Printf ("%s", msg);
221 }
222 
223 // Outputs to the VC / Windows Debug window (only in debug compile)
Com_OPrintf(const char * fmt,...)224 void QDECL Com_OPrintf( const char *fmt, ...)
225 {
226 	va_list		argptr;
227 	char		msg[MAXPRINTMSG];
228 
229 	va_start (argptr,fmt);
230 	Q_vsnprintf (msg, sizeof(msg), fmt, argptr);
231 	va_end (argptr);
232 #ifdef _WIN32
233 	OutputDebugString(msg);
234 #else
235 	printf("%s", msg);
236 #endif
237 }
238 
239 /*
240 =============
241 Com_Error
242 
243 Both client and server can use this, and it will
244 do the appropriate things.
245 =============
246 */
Com_Error(int code,const char * fmt,...)247 void NORETURN QDECL Com_Error( int code, const char *fmt, ... ) {
248 	va_list		argptr;
249 	static int	lastErrorTime;
250 	static int	errorCount;
251 	int			currentTime;
252 
253 	if ( com_errorEntered ) {
254 		Sys_Error( "recursive error after: %s", com_errorMessage );
255 	}
256 	com_errorEntered = qtrue;
257 
258 	// when we are running automated scripts, make sure we
259 	// know if anything failed
260 	if ( com_buildScript && com_buildScript->integer ) {
261 		code = ERR_FATAL;
262 	}
263 
264 	// ERR_DROPs on dedicated drop to an interactive console
265 	// which doesn't make sense for dedicated as it's generally
266 	// run unattended
267 	if ( com_dedicated && com_dedicated->integer ) {
268 		code = ERR_FATAL;
269 	}
270 
271 	// if we are getting a solid stream of ERR_DROP, do an ERR_FATAL
272 	currentTime = Sys_Milliseconds();
273 	if ( currentTime - lastErrorTime < 100 ) {
274 		if ( ++errorCount > 3 ) {
275 			code = ERR_FATAL;
276 		}
277 	} else {
278 		errorCount = 0;
279 	}
280 	lastErrorTime = currentTime;
281 
282 	va_start (argptr,fmt);
283 	Q_vsnprintf (com_errorMessage,sizeof(com_errorMessage), fmt,argptr);
284 	va_end (argptr);
285 
286 	if ( code != ERR_DISCONNECT && code != ERR_NEED_CD ) {
287 		Cvar_Get("com_errorMessage", "", CVAR_ROM);	//give com_errorMessage a default so it won't come back to life after a resetDefaults
288 		Cvar_Set("com_errorMessage", com_errorMessage);
289 	}
290 
291 	if ( code == ERR_DISCONNECT || code == ERR_SERVERDISCONNECT || code == ERR_DROP || code == ERR_NEED_CD ) {
292 		throw code;
293 	} else {
294 		CL_Shutdown ();
295 		SV_Shutdown (va("Server fatal crashed: %s\n", com_errorMessage));
296 	}
297 
298 	Com_Shutdown ();
299 
300 	Sys_Error ("%s", com_errorMessage);
301 }
302 
303 
304 /*
305 =============
306 Com_Quit_f
307 
308 Both client and server can use this, and it will
309 do the appropriate things.
310 =============
311 */
Com_Quit_f(void)312 void Com_Quit_f( void ) {
313 	// don't try to shutdown if we are in a recursive error
314 	if ( !com_errorEntered ) {
315 		SV_Shutdown ("Server quit\n");
316 		CL_Shutdown ();
317 		Com_Shutdown ();
318 		FS_Shutdown(qtrue);
319 	}
320 	Sys_Quit ();
321 }
322 
323 
324 
325 /*
326 ============================================================================
327 
328 COMMAND LINE FUNCTIONS
329 
330 + characters separate the commandLine string into multiple console
331 command lines.
332 
333 All of these are valid:
334 
335 quake3 +set test blah +map test
336 quake3 set test blah+map test
337 quake3 set test blah + map test
338 
339 ============================================================================
340 */
341 
342 #define	MAX_CONSOLE_LINES	32
343 int		com_numConsoleLines;
344 char	*com_consoleLines[MAX_CONSOLE_LINES];
345 
346 /*
347 ==================
348 Com_ParseCommandLine
349 
350 Break it up into multiple console lines
351 ==================
352 */
Com_ParseCommandLine(char * commandLine)353 void Com_ParseCommandLine( char *commandLine ) {
354 	int inq = 0;
355 	com_consoleLines[0] = commandLine;
356 	com_numConsoleLines = 1;
357 
358 	while ( *commandLine ) {
359 		if (*commandLine == '"') {
360 			inq = !inq;
361 		}
362 		// look for a + seperating character
363 		// if commandLine came from a file, we might have real line seperators
364 		if ( (*commandLine == '+' && !inq) || *commandLine == '\n'  || *commandLine == '\r' ) {
365 			if ( com_numConsoleLines == MAX_CONSOLE_LINES ) {
366 				return;
367 			}
368 			com_consoleLines[com_numConsoleLines] = commandLine + 1;
369 			com_numConsoleLines++;
370 			*commandLine = 0;
371 		}
372 		commandLine++;
373 	}
374 }
375 
376 
377 /*
378 ===================
379 Com_SafeMode
380 
381 Check for "safe" on the command line, which will
382 skip loading of jampconfig.cfg
383 ===================
384 */
Com_SafeMode(void)385 qboolean Com_SafeMode( void ) {
386 	int		i;
387 
388 	for ( i = 0 ; i < com_numConsoleLines ; i++ ) {
389 		Cmd_TokenizeString( com_consoleLines[i] );
390 		if ( !Q_stricmp( Cmd_Argv(0), "safe" )
391 			|| !Q_stricmp( Cmd_Argv(0), "cvar_restart" ) ) {
392 			com_consoleLines[i][0] = 0;
393 			return qtrue;
394 		}
395 	}
396 	return qfalse;
397 }
398 
399 
400 /*
401 ===============
402 Com_StartupVariable
403 
404 Searches for command line parameters that are set commands.
405 If match is not NULL, only that cvar will be looked for.
406 That is necessary because cddir and basedir need to be set
407 before the filesystem is started, but all other sets shouls
408 be after execing the config and default.
409 ===============
410 */
Com_StartupVariable(const char * match)411 void Com_StartupVariable( const char *match ) {
412 	for (int i=0 ; i < com_numConsoleLines ; i++) {
413 		Cmd_TokenizeString( com_consoleLines[i] );
414 		if ( strcmp( Cmd_Argv(0), "set" ) ) {
415 			continue;
416 		}
417 
418 		char *s = Cmd_Argv(1);
419 
420 		if ( !match || !strcmp( s, match ) )
421 			Cvar_User_Set( s, Cmd_ArgsFrom( 2 ) );
422 	}
423 }
424 
425 
426 /*
427 =================
428 Com_AddStartupCommands
429 
430 Adds command line parameters as script statements
431 Commands are seperated by + signs
432 
433 Returns qtrue if any late commands were added, which
434 will keep the demoloop from immediately starting
435 =================
436 */
Com_AddStartupCommands(void)437 qboolean Com_AddStartupCommands( void ) {
438 	int		i;
439 	qboolean	added;
440 
441 	added = qfalse;
442 	// quote every token, so args with semicolons can work
443 	for (i=0 ; i < com_numConsoleLines ; i++) {
444 		if ( !com_consoleLines[i] || !com_consoleLines[i][0] ) {
445 			continue;
446 		}
447 
448 		// set commands won't override menu startup
449 		if ( Q_stricmpn( com_consoleLines[i], "set", 3 ) ) {
450 			added = qtrue;
451 		}
452 		Cbuf_AddText( com_consoleLines[i] );
453 		Cbuf_AddText( "\n" );
454 	}
455 
456 	return added;
457 }
458 
459 
460 //============================================================================
461 
Info_Print(const char * s)462 void Info_Print( const char *s ) {
463 	char	key[BIG_INFO_KEY];
464 	char	value[BIG_INFO_VALUE];
465 	char	*o;
466 	int		l;
467 
468 	if (*s == '\\')
469 		s++;
470 	while (*s)
471 	{
472 		o = key;
473 		while (*s && *s != '\\')
474 			*o++ = *s++;
475 
476 		l = o - key;
477 		if (l < 20)
478 		{
479 			Com_Memset (o, ' ', 20-l);
480 			key[20] = 0;
481 		}
482 		else
483 			*o = 0;
484 		Com_Printf ("%s ", key);
485 
486 		if (!*s)
487 		{
488 			Com_Printf ("MISSING VALUE\n");
489 			return;
490 		}
491 
492 		o = value;
493 		s++;
494 		while (*s && *s != '\\')
495 			*o++ = *s++;
496 		*o = 0;
497 
498 		if (*s)
499 			s++;
500 		Com_Printf ("%s\n", value);
501 	}
502 }
503 
504 /*
505 ============
506 Com_StringContains
507 ============
508 */
Com_StringContains(char * str1,char * str2,int casesensitive)509 char *Com_StringContains(char *str1, char *str2, int casesensitive) {
510 	int len, i, j;
511 
512 	len = strlen(str1) - strlen(str2);
513 	for (i = 0; i <= len; i++, str1++) {
514 		for (j = 0; str2[j]; j++) {
515 			if (casesensitive) {
516 				if (str1[j] != str2[j]) {
517 					break;
518 				}
519 			}
520 			else {
521 				if (toupper(str1[j]) != toupper(str2[j])) {
522 					break;
523 				}
524 			}
525 		}
526 		if (!str2[j]) {
527 			return str1;
528 		}
529 	}
530 	return NULL;
531 }
532 
533 /*
534 ============
535 Com_Filter
536 ============
537 */
Com_Filter(char * filter,char * name,int casesensitive)538 int Com_Filter(char *filter, char *name, int casesensitive)
539 {
540 	char buf[MAX_TOKEN_CHARS];
541 	char *ptr;
542 	int i, found;
543 
544 	while(*filter) {
545 		if (*filter == '*') {
546 			filter++;
547 			for (i = 0; *filter; i++) {
548 				if (*filter == '*' || *filter == '?') break;
549 				buf[i] = *filter;
550 				filter++;
551 			}
552 			buf[i] = '\0';
553 			if (strlen(buf)) {
554 				ptr = Com_StringContains(name, buf, casesensitive);
555 				if (!ptr) return qfalse;
556 				name = ptr + strlen(buf);
557 			}
558 		}
559 		else if (*filter == '?') {
560 			filter++;
561 			name++;
562 		}
563 		else if (*filter == '[' && *(filter+1) == '[') {
564 			filter++;
565 		}
566 		else if (*filter == '[') {
567 			filter++;
568 			found = qfalse;
569 			while(*filter && !found) {
570 				if (*filter == ']' && *(filter+1) != ']') break;
571 				if (*(filter+1) == '-' && *(filter+2) && (*(filter+2) != ']' || *(filter+3) == ']')) {
572 					if (casesensitive) {
573 						if (*name >= *filter && *name <= *(filter+2)) found = qtrue;
574 					}
575 					else {
576 						if (toupper(*name) >= toupper(*filter) &&
577 							toupper(*name) <= toupper(*(filter+2))) found = qtrue;
578 					}
579 					filter += 3;
580 				}
581 				else {
582 					if (casesensitive) {
583 						if (*filter == *name) found = qtrue;
584 					}
585 					else {
586 						if (toupper(*filter) == toupper(*name)) found = qtrue;
587 					}
588 					filter++;
589 				}
590 			}
591 			if (!found) return qfalse;
592 			while(*filter) {
593 				if (*filter == ']' && *(filter+1) != ']') break;
594 				filter++;
595 			}
596 			filter++;
597 			name++;
598 		}
599 		else {
600 			if (casesensitive) {
601 				if (*filter != *name) return qfalse;
602 			}
603 			else {
604 				if (toupper(*filter) != toupper(*name)) return qfalse;
605 			}
606 			filter++;
607 			name++;
608 		}
609 	}
610 	return qtrue;
611 }
612 
613 /*
614 ============
615 Com_FilterPath
616 ============
617 */
Com_FilterPath(char * filter,char * name,int casesensitive)618 int Com_FilterPath(char *filter, char *name, int casesensitive)
619 {
620 	int i;
621 	char new_filter[MAX_QPATH];
622 	char new_name[MAX_QPATH];
623 
624 	for (i = 0; i < MAX_QPATH-1 && filter[i]; i++) {
625 		if ( filter[i] == '\\' || filter[i] == ':' ) {
626 			new_filter[i] = '/';
627 		}
628 		else {
629 			new_filter[i] = filter[i];
630 		}
631 	}
632 	new_filter[i] = '\0';
633 	for (i = 0; i < MAX_QPATH-1 && name[i]; i++) {
634 		if ( name[i] == '\\' || name[i] == ':' ) {
635 			new_name[i] = '/';
636 		}
637 		else {
638 			new_name[i] = name[i];
639 		}
640 	}
641 	new_name[i] = '\0';
642 	return Com_Filter(new_filter, new_name, casesensitive);
643 }
644 
645 /*
646 ============
647 Com_HashKey
648 ============
649 */
Com_HashKey(char * string,int maxlen)650 int Com_HashKey(char *string, int maxlen) {
651 	int hash, i;
652 
653 	hash = 0;
654 	for (i = 0; i < maxlen && string[i] != '\0'; i++) {
655 		hash += string[i] * (119 + i);
656 	}
657 	hash = (hash ^ (hash >> 10) ^ (hash >> 20));
658 	return hash;
659 }
660 
661 /*
662 ================
663 Com_RealTime
664 ================
665 */
Com_RealTime(qtime_t * qtime)666 int Com_RealTime(qtime_t *qtime) {
667 	time_t t;
668 	struct tm *tms;
669 
670 	t = time(NULL);
671 	if (!qtime)
672 		return t;
673 	tms = localtime(&t);
674 	if (tms) {
675 		qtime->tm_sec = tms->tm_sec;
676 		qtime->tm_min = tms->tm_min;
677 		qtime->tm_hour = tms->tm_hour;
678 		qtime->tm_mday = tms->tm_mday;
679 		qtime->tm_mon = tms->tm_mon;
680 		qtime->tm_year = tms->tm_year;
681 		qtime->tm_wday = tms->tm_wday;
682 		qtime->tm_yday = tms->tm_yday;
683 		qtime->tm_isdst = tms->tm_isdst;
684 	}
685 	return t;
686 }
687 
688 
689 /*
690 ===================================================================
691 
692 EVENTS AND JOURNALING
693 
694 In addition to these events, .cfg files are also copied to the
695 journaled file
696 ===================================================================
697 */
698 
699 #define	MAX_PUSHED_EVENTS	            1024
700 static int		com_pushedEventsHead = 0;
701 static int             com_pushedEventsTail = 0;
702 static sysEvent_t	com_pushedEvents[MAX_PUSHED_EVENTS];
703 
704 /*
705 =================
706 Com_InitJournaling
707 =================
708 */
Com_InitJournaling(void)709 void Com_InitJournaling( void ) {
710 	Com_StartupVariable( "journal" );
711 	com_journal = Cvar_Get ("journal", "0", CVAR_INIT);
712 	if ( !com_journal->integer ) {
713 		return;
714 	}
715 
716 	if ( com_journal->integer == 1 ) {
717 		Com_Printf( "Journaling events\n");
718 		com_journalFile = FS_FOpenFileWrite( "journal.dat" );
719 		com_journalDataFile = FS_FOpenFileWrite( "journaldata.dat" );
720 	} else if ( com_journal->integer == 2 ) {
721 		Com_Printf( "Replaying journaled events\n");
722 		FS_FOpenFileRead( "journal.dat", &com_journalFile, qtrue );
723 		FS_FOpenFileRead( "journaldata.dat", &com_journalDataFile, qtrue );
724 	}
725 
726 	if ( !com_journalFile || !com_journalDataFile ) {
727 		Cvar_Set( "com_journal", "0" );
728 		com_journalFile = 0;
729 		com_journalDataFile = 0;
730 		Com_Printf( "Couldn't open journal files\n" );
731 	}
732 }
733 
734 /*
735 =================
736 Com_GetRealEvent
737 =================
738 */
Com_GetRealEvent(void)739 sysEvent_t	Com_GetRealEvent( void ) {
740 	int			r;
741 	sysEvent_t	ev;
742 
743 	// either get an event from the system or the journal file
744 	if ( com_journal->integer == 2 ) {
745 		r = FS_Read( &ev, sizeof(ev), com_journalFile );
746 		if ( r != sizeof(ev) ) {
747 			Com_Error( ERR_FATAL, "Error reading from journal file" );
748 		}
749 		if ( ev.evPtrLength ) {
750 			ev.evPtr = Z_Malloc( ev.evPtrLength, TAG_EVENT, qtrue );
751 			r = FS_Read( ev.evPtr, ev.evPtrLength, com_journalFile );
752 			if ( r != ev.evPtrLength ) {
753 				Com_Error( ERR_FATAL, "Error reading from journal file" );
754 			}
755 		}
756 	} else {
757 		ev = Sys_GetEvent();
758 
759 		// write the journal value out if needed
760 		if ( com_journal->integer == 1 ) {
761 			r = FS_Write( &ev, sizeof(ev), com_journalFile );
762 			if ( r != sizeof(ev) ) {
763 				Com_Error( ERR_FATAL, "Error writing to journal file" );
764 			}
765 			if ( ev.evPtrLength ) {
766 				r = FS_Write( ev.evPtr, ev.evPtrLength, com_journalFile );
767 				if ( r != ev.evPtrLength ) {
768 					Com_Error( ERR_FATAL, "Error writing to journal file" );
769 				}
770 			}
771 		}
772 	}
773 
774 	return ev;
775 }
776 
777 /*
778 =================
779 Com_InitPushEvent
780 =================
781 */
Com_InitPushEvent(void)782 void Com_InitPushEvent( void ) {
783 	// clear the static buffer array
784 	// this requires SE_NONE to be accepted as a valid but NOP event
785 	memset( com_pushedEvents, 0, sizeof(com_pushedEvents) );
786 	// reset counters while we are at it
787 	// beware: GetEvent might still return an SE_NONE from the buffer
788 	com_pushedEventsHead = 0;
789 	com_pushedEventsTail = 0;
790 }
791 
792 /*
793 =================
794 Com_PushEvent
795 =================
796 */
Com_PushEvent(sysEvent_t * event)797 void Com_PushEvent( sysEvent_t *event ) {
798 	sysEvent_t		*ev;
799 	static int printedWarning = 0;
800 
801 	ev = &com_pushedEvents[ com_pushedEventsHead & (MAX_PUSHED_EVENTS-1) ];
802 
803 	if ( com_pushedEventsHead - com_pushedEventsTail >= MAX_PUSHED_EVENTS ) {
804 
805 		// don't print the warning constantly, or it can give time for more...
806 		if ( !printedWarning ) {
807 			printedWarning = qtrue;
808 			Com_Printf( "WARNING: Com_PushEvent overflow\n" );
809 		}
810 
811 		if ( ev->evPtr ) {
812 			Z_Free( ev->evPtr );
813 		}
814 		com_pushedEventsTail++;
815 	} else {
816 		printedWarning = qfalse;
817 	}
818 
819 	*ev = *event;
820 	com_pushedEventsHead++;
821 }
822 
823 /*
824 =================
825 Com_GetEvent
826 =================
827 */
Com_GetEvent(void)828 sysEvent_t	Com_GetEvent( void ) {
829 	if ( com_pushedEventsHead > com_pushedEventsTail ) {
830 		com_pushedEventsTail++;
831 		return com_pushedEvents[ (com_pushedEventsTail-1) & (MAX_PUSHED_EVENTS-1) ];
832 	}
833 	return Com_GetRealEvent();
834 }
835 
836 /*
837 =================
838 Com_RunAndTimeServerPacket
839 =================
840 */
Com_RunAndTimeServerPacket(netadr_t * evFrom,msg_t * buf)841 void Com_RunAndTimeServerPacket( netadr_t *evFrom, msg_t *buf ) {
842 	int		t1, t2, msec;
843 
844 	t1 = 0;
845 
846 	if ( com_speeds->integer ) {
847 		t1 = Sys_Milliseconds ();
848 	}
849 
850 	SV_PacketEvent( *evFrom, buf );
851 
852 	if ( com_speeds->integer ) {
853 		t2 = Sys_Milliseconds ();
854 		msec = t2 - t1;
855 		if ( com_speeds->integer == 3 ) {
856 			Com_Printf( "SV_PacketEvent time: %i\n", msec );
857 		}
858 	}
859 }
860 
861 /*
862 =================
863 Com_EventLoop
864 
865 Returns last event time
866 =================
867 */
Com_EventLoop(void)868 int Com_EventLoop( void ) {
869 	sysEvent_t	ev;
870 	netadr_t	evFrom;
871 	byte		bufData[MAX_MSGLEN];
872 	msg_t		buf;
873 
874 	MSG_Init( &buf, bufData, sizeof( bufData ) );
875 
876 	while ( 1 ) {
877 		ev = Com_GetEvent();
878 
879 		// if no more events are available
880 		if ( ev.evType == SE_NONE ) {
881 			// manually send packet events for the loopback channel
882 			while ( NET_GetLoopPacket( NS_CLIENT, &evFrom, &buf ) ) {
883 				CL_PacketEvent( evFrom, &buf );
884 			}
885 
886 			while ( NET_GetLoopPacket( NS_SERVER, &evFrom, &buf ) ) {
887 				// if the server just shut down, flush the events
888 				if ( com_sv_running->integer ) {
889 					Com_RunAndTimeServerPacket( &evFrom, &buf );
890 				}
891 			}
892 
893 			return ev.evTime;
894 		}
895 
896 
897 		switch ( ev.evType ) {
898 		default:
899 			Com_Error( ERR_FATAL, "Com_EventLoop: bad event type %i", ev.evType );
900 			break;
901         case SE_NONE:
902             break;
903 		case SE_KEY:
904 			CL_KeyEvent( ev.evValue, (qboolean)ev.evValue2, ev.evTime );
905 			break;
906 		case SE_CHAR:
907 			CL_CharEvent( ev.evValue );
908 			break;
909 		case SE_MOUSE:
910 			CL_MouseEvent( ev.evValue, ev.evValue2, ev.evTime );
911 			break;
912 		case SE_JOYSTICK_AXIS:
913 			CL_JoystickEvent( ev.evValue, ev.evValue2, ev.evTime );
914 			break;
915 		case SE_CONSOLE:
916 			if ( ((char *)ev.evPtr)[0] == '\\' || ((char *)ev.evPtr)[0] == '/' )
917 			{
918 				Cbuf_AddText( (char *)ev.evPtr+1 );
919 			}
920 			else
921 			{
922 				Cbuf_AddText( (char *)ev.evPtr );
923 			}
924 			Cbuf_AddText( "\n" );
925 			break;
926 		}
927 
928 		// free any block data
929 		if ( ev.evPtr ) {
930 			Z_Free( ev.evPtr );
931 		}
932 	}
933 
934 	return 0;	// never reached
935 }
936 
937 /*
938 ================
939 Com_Milliseconds
940 
941 Can be used for profiling, but will be journaled accurately
942 ================
943 */
Com_Milliseconds(void)944 int Com_Milliseconds (void) {
945 	sysEvent_t	ev;
946 
947 	// get events and push them until we get a null event with the current time
948 	do {
949 
950 		ev = Com_GetRealEvent();
951 		if ( ev.evType != SE_NONE ) {
952 			Com_PushEvent( &ev );
953 		}
954 	} while ( ev.evType != SE_NONE );
955 
956 	return ev.evTime;
957 }
958 
959 //============================================================================
960 
961 /*
962 =============
963 Com_Error_f
964 
965 Just throw a fatal error to
966 test error shutdown procedures
967 =============
968 */
Com_Error_f(void)969 static void NORETURN Com_Error_f (void) {
970 	if ( Cmd_Argc() > 1 ) {
971 		Com_Error( ERR_DROP, "Testing drop error" );
972 	} else {
973 		Com_Error( ERR_FATAL, "Testing fatal error" );
974 	}
975 }
976 
977 
978 /*
979 =============
980 Com_Freeze_f
981 
982 Just freeze in place for a given number of seconds to test
983 error recovery
984 =============
985 */
Com_Freeze_f(void)986 static void Com_Freeze_f (void) {
987 	float	s;
988 	int		start, now;
989 
990 	if ( Cmd_Argc() != 2 ) {
991 		Com_Printf( "freeze <seconds>\n" );
992 		return;
993 	}
994 	s = atof( Cmd_Argv(1) );
995 
996 	start = Com_Milliseconds();
997 
998 	while ( 1 ) {
999 		now = Com_Milliseconds();
1000 		if ( ( now - start ) * 0.001 > s ) {
1001 			break;
1002 		}
1003 	}
1004 }
1005 
1006 /*
1007 =================
1008 Com_Crash_f
1009 
1010 A way to force a bus error for development reasons
1011 =================
1012 */
Com_Crash_f(void)1013 static void NORETURN Com_Crash_f( void ) {
1014 	* ( volatile int * ) 0 = 0x12345678;
1015 	/* that should crash already, but to reassure the compiler: */
1016 	abort();
1017 }
1018 
1019 /*
1020 ==================
1021 Com_ExecuteCfg
1022 ==================
1023 */
1024 
Com_ExecuteCfg(void)1025 void Com_ExecuteCfg(void)
1026 {
1027 	Cbuf_ExecuteText(EXEC_NOW, "exec mpdefault.cfg\n");
1028 	Cbuf_Execute(); // Always execute after exec to prevent text buffer overflowing
1029 
1030 	if(!Com_SafeMode())
1031 	{
1032 		// skip the q3config.cfg and autoexec.cfg if "safe" is on the command line
1033 		Cbuf_ExecuteText(EXEC_NOW, "exec " Q3CONFIG_CFG "\n");
1034 		Cbuf_Execute();
1035 		Cbuf_ExecuteText(EXEC_NOW, "exec autoexec.cfg\n");
1036 		Cbuf_Execute();
1037 	}
1038 }
1039 
1040 /*
1041 =================
1042 Com_InitRand
1043 Seed the random number generator, if possible with an OS supplied random seed.
1044 =================
1045 */
Com_InitRand(void)1046 static void Com_InitRand(void)
1047 {
1048 	unsigned int seed;
1049 
1050 	if(Sys_RandomBytes((byte *) &seed, sizeof(seed)))
1051 		srand(seed);
1052 	else
1053 		srand(time(NULL));
1054 }
1055 
1056 /*
1057 =================
1058 Com_ErrorString
1059 Error string for the given error code (from Com_Error).
1060 =================
1061 */
Com_ErrorString(int code)1062 static const char *Com_ErrorString ( int code )
1063 {
1064 	switch ( code )
1065 	{
1066 		case ERR_DISCONNECT:
1067 		// fallthrough
1068 		case ERR_SERVERDISCONNECT:
1069 			return "DISCONNECTED";
1070 
1071 		case ERR_DROP:
1072 			return "DROPPED";
1073 
1074 		case ERR_NEED_CD:
1075 			return "NEED CD";
1076 
1077 		default:
1078 			return "UNKNOWN";
1079 	}
1080 }
1081 
1082 /*
1083 =================
1084 Com_CatchError
1085 Handles freeing up of resources when Com_Error is called.
1086 =================
1087 */
Com_CatchError(int code)1088 static void Com_CatchError ( int code )
1089 {
1090 	if ( code == ERR_DISCONNECT || code == ERR_SERVERDISCONNECT ) {
1091 		SV_Shutdown( "Server disconnected" );
1092 		CL_Disconnect( qtrue );
1093 		CL_FlushMemory(  );
1094 		// make sure we can get at our local stuff
1095 		FS_PureServerSetLoadedPaks( "", "" );
1096 		com_errorEntered = qfalse;
1097 	} else if ( code == ERR_DROP ) {
1098 		Com_Printf ("********************\n"
1099 					"ERROR: %s\n"
1100 					"********************\n", com_errorMessage);
1101 		SV_Shutdown (va("Server crashed: %s\n",  com_errorMessage));
1102 		CL_Disconnect( qtrue );
1103 		CL_FlushMemory( );
1104 		// make sure we can get at our local stuff
1105 		FS_PureServerSetLoadedPaks( "", "" );
1106 		com_errorEntered = qfalse;
1107 	} else if ( code == ERR_NEED_CD ) {
1108 		SV_Shutdown( "Server didn't have CD" );
1109 		if ( com_cl_running && com_cl_running->integer ) {
1110 			CL_Disconnect( qtrue );
1111 			CL_FlushMemory( );
1112 		} else {
1113 			Com_Printf("Server didn't have CD\n" );
1114 		}
1115 		// make sure we can get at our local stuff
1116 		FS_PureServerSetLoadedPaks( "", "" );
1117 		com_errorEntered = qfalse;
1118 	}
1119 }
1120 
1121 /*
1122 =================
1123 Com_Init
1124 =================
1125 */
Com_Init(char * commandLine)1126 void Com_Init( char *commandLine ) {
1127 	char	*s;
1128 	int		qport;
1129 
1130 	Com_Printf( "%s %s %s\n", JK_VERSION, PLATFORM_STRING, SOURCE_DATE );
1131 
1132 	try
1133 	{
1134 		// initialize the weak pseudo-random number generator for use later.
1135 		Com_InitRand();
1136 
1137 		// do this before anything else decides to push events
1138 		Com_InitPushEvent();
1139 
1140 		Com_InitZoneMemory();
1141 		Cvar_Init ();
1142 
1143 		navigator.Init();
1144 
1145 		// prepare enough of the subsystems to handle
1146 		// cvar and command buffer management
1147 		Com_ParseCommandLine( commandLine );
1148 
1149 	//	Swap_Init ();
1150 		Cbuf_Init ();
1151 
1152 		// override anything from the config files with command line args
1153 		Com_StartupVariable( NULL );
1154 
1155 		Com_InitZoneMemoryVars();
1156 		Cmd_Init ();
1157 
1158 		// Seed the random number generator
1159 		Rand_Init(Sys_Milliseconds(true));
1160 
1161 		// get the developer cvar set as early as possible
1162 		com_developer = Cvar_Get("developer", "0", CVAR_TEMP, "Developer mode" );
1163 
1164 		// done early so bind command exists
1165 		CL_InitKeyCommands();
1166 
1167 		com_homepath = Cvar_Get("com_homepath", "", CVAR_INIT);
1168 
1169 		FS_InitFilesystem ();
1170 
1171 		Com_InitJournaling();
1172 
1173 		// Add some commands here already so users can use them from config files
1174 		if ( com_developer && com_developer->integer ) {
1175 			Cmd_AddCommand ("error", Com_Error_f);
1176 			Cmd_AddCommand ("crash", Com_Crash_f );
1177 			Cmd_AddCommand ("freeze", Com_Freeze_f);
1178 		}
1179 		Cmd_AddCommand ("quit", Com_Quit_f, "Quits the game" );
1180 #ifndef FINAL_BUILD
1181 		Cmd_AddCommand ("changeVectors", MSG_ReportChangeVectors_f );
1182 #endif
1183 		Cmd_AddCommand ("writeconfig", Com_WriteConfig_f, "Write the configuration to file" );
1184 		Cmd_SetCommandCompletionFunc( "writeconfig", Cmd_CompleteCfgName );
1185 
1186 		Com_ExecuteCfg();
1187 
1188 		// override anything from the config files with command line args
1189 		Com_StartupVariable( NULL );
1190 
1191 	  // get dedicated here for proper hunk megs initialization
1192 	#ifdef DEDICATED
1193 		com_dedicated = Cvar_Get ("dedicated", "2", CVAR_INIT);
1194 		Cvar_CheckRange( com_dedicated, 1, 2, qtrue );
1195 	#else
1196 		//OJKFIXME: Temporarily disabled dedicated server when not using the dedicated server binary.
1197 		//			Issue is the server not having a renderer when not using ^^^^^
1198 		//				and crashing in SV_SpawnServer calling re.RegisterMedia_LevelLoadBegin
1199 		//			Until we fully remove the renderer from the server, the client executable
1200 		//				will not have dedicated support capabilities.
1201 		//			Use the dedicated server package.
1202 		com_dedicated = Cvar_Get ("_dedicated", "0", CVAR_ROM|CVAR_INIT|CVAR_PROTECTED);
1203 	//	Cvar_CheckRange( com_dedicated, 0, 2, qtrue );
1204 	#endif
1205 		// allocate the stack based hunk allocator
1206 		Com_InitHunkMemory();
1207 
1208 		// if any archived cvars are modified after this, we will trigger a writing
1209 		// of the config file
1210 		cvar_modifiedFlags &= ~CVAR_ARCHIVE;
1211 
1212 		//
1213 		// init commands and vars
1214 		//
1215 		com_logfile = Cvar_Get ("logfile", "0", CVAR_TEMP );
1216 
1217 		com_timescale = Cvar_Get ("timescale", "1", CVAR_CHEAT | CVAR_SYSTEMINFO );
1218 		com_fixedtime = Cvar_Get ("fixedtime", "0", CVAR_CHEAT);
1219 		com_showtrace = Cvar_Get ("com_showtrace", "0", CVAR_CHEAT);
1220 
1221 		com_speeds = Cvar_Get ("com_speeds", "0", 0);
1222 		com_timedemo = Cvar_Get ("timedemo", "0", 0);
1223 		com_cameraMode = Cvar_Get ("com_cameraMode", "0", CVAR_CHEAT);
1224 
1225 		com_optvehtrace = Cvar_Get("com_optvehtrace", "0", 0);
1226 
1227 		cl_paused = Cvar_Get ("cl_paused", "0", CVAR_ROM);
1228 		sv_paused = Cvar_Get ("sv_paused", "0", CVAR_ROM);
1229 		com_sv_running = Cvar_Get ("sv_running", "0", CVAR_ROM, "Is a server running?" );
1230 		com_cl_running = Cvar_Get ("cl_running", "0", CVAR_ROM, "Is the client running?" );
1231 		com_buildScript = Cvar_Get( "com_buildScript", "0", 0 );
1232 #ifndef _WIN32
1233 		com_ansiColor = Cvar_Get( "com_ansiColor", "0", CVAR_ARCHIVE_ND );
1234 #endif
1235 
1236 #ifdef G2_PERFORMANCE_ANALYSIS
1237 		com_G2Report = Cvar_Get("com_G2Report", "0", 0);
1238 #endif
1239 
1240 		com_affinity = Cvar_Get( "com_affinity", "0", CVAR_ARCHIVE_ND );
1241 		com_busyWait = Cvar_Get( "com_busyWait", "0", CVAR_ARCHIVE_ND );
1242 
1243 		com_bootlogo = Cvar_Get( "com_bootlogo", "1", CVAR_ARCHIVE_ND, "Show intro movies" );
1244 
1245 		s = va("%s %s %s", JK_VERSION_OLD, PLATFORM_STRING, SOURCE_DATE );
1246 		com_version = Cvar_Get ("version", s, CVAR_ROM | CVAR_SERVERINFO );
1247 
1248 		SE_Init();
1249 
1250 		Sys_Init();
1251 
1252 		Sys_SetProcessorAffinity();
1253 
1254 		// Pick a random port value
1255 		Com_RandomBytes( (byte*)&qport, sizeof(int) );
1256 		Netchan_Init( qport & 0xffff );	// pick a port value that should be nice and random
1257 
1258 		VM_Init();
1259 		SV_Init();
1260 
1261 		com_dedicated->modified = qfalse;
1262 		if ( !com_dedicated->integer ) {
1263 			CL_Init();
1264 		}
1265 
1266 		// set com_frameTime so that if a map is started on the
1267 		// command line it will still be able to count on com_frameTime
1268 		// being random enough for a serverid
1269 		com_frameTime = Com_Milliseconds();
1270 
1271 
1272 		// add + commands from command line
1273 		if ( !Com_AddStartupCommands() )
1274 		{
1275 			// if the user didn't give any commands, run default action
1276 			if ( !com_dedicated->integer )
1277 			{
1278 				if ( com_bootlogo->integer )
1279 				{
1280 					Cbuf_AddText ("cinematic openinglogos.roq\n");
1281 				}
1282 			}
1283 		}
1284 
1285 		// start in full screen ui mode
1286 		Cvar_Set("r_uiFullScreen", "1");
1287 
1288 		CL_StartHunkUsers();
1289 
1290 		// make sure single player is off by default
1291 		Cvar_Set("ui_singlePlayerActive", "0");
1292 
1293 		com_fullyInitialized = qtrue;
1294 		Com_Printf ("--- Common Initialization Complete ---\n");
1295 	}
1296 	catch ( int code )
1297 	{
1298 		Com_CatchError (code);
1299 		Sys_Error ("Error during initialization: %s", Com_ErrorString (code));
1300 	}
1301 }
1302 
1303 //==================================================================
1304 
Com_WriteConfigToFile(const char * filename)1305 void Com_WriteConfigToFile( const char *filename ) {
1306 	fileHandle_t	f;
1307 
1308 	f = FS_FOpenFileWrite( filename );
1309 	if ( !f ) {
1310 		Com_Printf ("Couldn't write %s.\n", filename );
1311 		return;
1312 	}
1313 
1314 	FS_Printf (f, "// generated by OpenJK MP, do not modify\n");
1315 	Key_WriteBindings (f);
1316 	Cvar_WriteVariables (f);
1317 	FS_FCloseFile( f );
1318 }
1319 
1320 
1321 /*
1322 ===============
1323 Com_WriteConfiguration
1324 
1325 Writes key bindings and archived cvars to config file if modified
1326 ===============
1327 */
Com_WriteConfiguration(void)1328 void Com_WriteConfiguration( void ) {
1329 	// if we are quiting without fully initializing, make sure
1330 	// we don't write out anything
1331 	if ( !com_fullyInitialized ) {
1332 		return;
1333 	}
1334 
1335 	if ( !(cvar_modifiedFlags & CVAR_ARCHIVE ) ) {
1336 		return;
1337 	}
1338 	cvar_modifiedFlags &= ~CVAR_ARCHIVE;
1339 
1340 	Com_WriteConfigToFile( Q3CONFIG_CFG );
1341 }
1342 
1343 
1344 /*
1345 ===============
1346 Com_WriteConfig_f
1347 
1348 Write the config file to a specific name
1349 ===============
1350 */
Com_WriteConfig_f(void)1351 void Com_WriteConfig_f( void ) {
1352 	char	filename[MAX_QPATH];
1353 
1354 	if ( Cmd_Argc() != 2 ) {
1355 		Com_Printf( "Usage: writeconfig <filename>\n" );
1356 		return;
1357 	}
1358 
1359 	Q_strncpyz( filename, Cmd_Argv(1), sizeof( filename ) );
1360 	COM_DefaultExtension( filename, sizeof( filename ), ".cfg" );
1361 
1362 	if(!COM_CompareExtension(filename, ".cfg"))
1363 	{
1364 		Com_Printf( "Com_WriteConfig_f: Only the \".cfg\" extension is supported by this command!\n" );
1365 		return;
1366 	}
1367 
1368 	if(!FS_FilenameCompare(filename, "mpdefault.cfg") || !FS_FilenameCompare(filename, "default.cfg"))
1369 	{
1370 		Com_Printf( S_COLOR_YELLOW "Com_WriteConfig_f: The filename \"%s\" is reserved! Please choose another name.\n", filename );
1371 		return;
1372 	}
1373 
1374 	Com_Printf( "Writing %s.\n", filename );
1375 	Com_WriteConfigToFile( filename );
1376 }
1377 
1378 /*
1379 ================
1380 Com_ModifyMsec
1381 ================
1382 */
Com_ModifyMsec(int msec)1383 int Com_ModifyMsec( int msec ) {
1384 	int		clampTime;
1385 
1386 	//
1387 	// modify time for debugging values
1388 	//
1389 	if ( com_fixedtime->integer ) {
1390 		msec = com_fixedtime->integer;
1391 	} else if ( com_timescale->value ) {
1392 		msec *= com_timescale->value;
1393 	} else if (com_cameraMode->integer) {
1394 		msec *= com_timescale->value;
1395 	}
1396 
1397 	// don't let it scale below 1 msec
1398 	if ( msec < 1 && com_timescale->value) {
1399 		msec = 1;
1400 	}
1401 
1402 	if ( com_dedicated->integer ) {
1403 		// dedicated servers don't want to clamp for a much longer
1404 		// period, because it would mess up all the client's views
1405 		// of time.
1406 		if ( com_sv_running->integer && msec > 500 ) {
1407 			Com_Printf( "Hitch warning: %i msec frame time\n", msec );
1408 		}
1409 		clampTime = 5000;
1410 	} else
1411 	if ( !com_sv_running->integer ) {
1412 		// clients of remote servers do not want to clamp time, because
1413 		// it would skew their view of the server's time temporarily
1414 		clampTime = 5000;
1415 	} else {
1416 		// for local single player gaming
1417 		// we may want to clamp the time to prevent players from
1418 		// flying off edges when something hitches.
1419 		clampTime = 200;
1420 	}
1421 
1422 	if ( msec > clampTime ) {
1423 		msec = clampTime;
1424 	}
1425 
1426 	return msec;
1427 }
1428 
1429 #ifdef G2_PERFORMANCE_ANALYSIS
1430 #include "qcommon/timing.h"
1431 void G2Time_ResetTimers(void);
1432 void G2Time_ReportTimers(void);
1433 extern timing_c G2PerformanceTimer_PreciseFrame;
1434 extern int G2Time_PreciseFrame;
1435 #endif
1436 
1437 /*
1438 =================
1439 Com_TimeVal
1440 =================
1441 */
1442 
Com_TimeVal(int minMsec)1443 int Com_TimeVal(int minMsec)
1444 {
1445 	int timeVal;
1446 
1447 	timeVal = Sys_Milliseconds() - com_frameTime;
1448 
1449 	if(timeVal >= minMsec)
1450 		timeVal = 0;
1451 	else
1452 		timeVal = minMsec - timeVal;
1453 
1454 	return timeVal;
1455 }
1456 
1457 /*
1458 =================
1459 Com_Frame
1460 =================
1461 */
Com_Frame(void)1462 void Com_Frame( void ) {
1463 
1464 	try
1465 	{
1466 #ifdef G2_PERFORMANCE_ANALYSIS
1467 		G2PerformanceTimer_PreciseFrame.Start();
1468 #endif
1469 		int		msec, minMsec;
1470 		int		timeVal;
1471 		static int	lastTime = 0, bias = 0;
1472 
1473 		int		timeBeforeFirstEvents = 0;
1474 		int           timeBeforeServer = 0;
1475 		int           timeBeforeEvents = 0;
1476 		int           timeBeforeClient = 0;
1477 		int           timeAfter = 0;
1478 
1479 		// write config file if anything changed
1480 		Com_WriteConfiguration();
1481 
1482 		//
1483 		// main event loop
1484 		//
1485 		if ( com_speeds->integer ) {
1486 			timeBeforeFirstEvents = Sys_Milliseconds ();
1487 		}
1488 
1489 		// Figure out how much time we have
1490 		if(!com_timedemo->integer)
1491 		{
1492 			if(com_dedicated->integer)
1493 				minMsec = SV_FrameMsec();
1494 			else
1495 			{
1496 				if(com_minimized->integer && com_maxfpsMinimized->integer > 0)
1497 					minMsec = 1000 / com_maxfpsMinimized->integer;
1498 				else if(com_unfocused->integer && com_maxfpsUnfocused->integer > 0)
1499 					minMsec = 1000 / com_maxfpsUnfocused->integer;
1500 				else if(com_maxfps->integer > 0)
1501 					minMsec = 1000 / com_maxfps->integer;
1502 				else
1503 					minMsec = 1;
1504 
1505 				timeVal = com_frameTime - lastTime;
1506 				bias += timeVal - minMsec;
1507 
1508 				if (bias > minMsec)
1509 					bias = minMsec;
1510 
1511 				// Adjust minMsec if previous frame took too long to render so
1512 				// that framerate is stable at the requested value.
1513 				minMsec -= bias;
1514 			}
1515 		}
1516 		else
1517 			minMsec = 1;
1518 
1519 		timeVal = Com_TimeVal(minMsec);
1520 		do {
1521 			// Busy sleep the last millisecond for better timeout precision
1522 			if(com_busyWait->integer || timeVal < 1)
1523 				NET_Sleep(0);
1524 			else
1525 				NET_Sleep(timeVal - 1);
1526 		} while( (timeVal = Com_TimeVal(minMsec)) != 0 );
1527 		IN_Frame();
1528 
1529 		lastTime = com_frameTime;
1530 		com_frameTime = Com_EventLoop();
1531 
1532 		msec = com_frameTime - lastTime;
1533 
1534 		Cbuf_Execute ();
1535 
1536 		// mess with msec if needed
1537 		msec = Com_ModifyMsec( msec );
1538 
1539 		//
1540 		// server side
1541 		//
1542 		if ( com_speeds->integer ) {
1543 			timeBeforeServer = Sys_Milliseconds ();
1544 		}
1545 
1546 		SV_Frame( msec );
1547 
1548 		// if "dedicated" has been modified, start up
1549 		// or shut down the client system.
1550 		// Do this after the server may have started,
1551 		// but before the client tries to auto-connect
1552 		if ( com_dedicated->modified ) {
1553 			// get the latched value
1554 			Cvar_Get( "_dedicated", "0", 0 );
1555 			com_dedicated->modified = qfalse;
1556 			if ( !com_dedicated->integer ) {
1557 				CL_Init();
1558 				CL_StartHunkUsers();	//fire up the UI!
1559 			} else {
1560 				CL_Shutdown();
1561 			}
1562 		}
1563 
1564 		//
1565 		// client system
1566 		//
1567 		if ( !com_dedicated->integer ) {
1568 			//
1569 			// run event loop a second time to get server to client packets
1570 			// without a frame of latency
1571 			//
1572 			if ( com_speeds->integer ) {
1573 				timeBeforeEvents = Sys_Milliseconds ();
1574 			}
1575 			Com_EventLoop();
1576 			Cbuf_Execute ();
1577 
1578 
1579 			//
1580 			// client side
1581 			//
1582 			if ( com_speeds->integer ) {
1583 				timeBeforeClient = Sys_Milliseconds ();
1584 			}
1585 
1586 			CL_Frame( msec );
1587 
1588 			if ( com_speeds->integer ) {
1589 				timeAfter = Sys_Milliseconds ();
1590 			}
1591 		}
1592 		else
1593 		{
1594 			if ( com_speeds->integer )
1595 			{
1596 				timeBeforeEvents = timeBeforeClient = timeAfter = Sys_Milliseconds ();
1597 			}
1598 		}
1599 
1600 		//
1601 		// report timing information
1602 		//
1603 		if ( com_speeds->integer ) {
1604 			int			all, sv, ev, cl;
1605 
1606 			all = timeAfter - timeBeforeServer;
1607 			sv = timeBeforeEvents - timeBeforeServer;
1608 			ev = timeBeforeServer - timeBeforeFirstEvents + timeBeforeClient - timeBeforeEvents;
1609 			cl = timeAfter - timeBeforeClient;
1610 			sv -= time_game;
1611 			cl -= time_frontend + time_backend;
1612 
1613 			Com_Printf ("frame:%i all:%3i sv:%3i ev:%3i cl:%3i gm:%3i rf:%3i bk:%3i\n",
1614 						 com_frameNumber, all, sv, ev, cl, time_game, time_frontend, time_backend );
1615 		}
1616 
1617 		//
1618 		// trace optimization tracking
1619 		//
1620 		if ( com_showtrace->integer ) {
1621 
1622 			extern	int c_traces, c_brush_traces, c_patch_traces;
1623 			extern	int	c_pointcontents;
1624 
1625 			Com_Printf ("%4i traces  (%ib %ip) %4i points\n", c_traces,
1626 				c_brush_traces, c_patch_traces, c_pointcontents);
1627 			c_traces = 0;
1628 			c_brush_traces = 0;
1629 			c_patch_traces = 0;
1630 			c_pointcontents = 0;
1631 		}
1632 
1633 		if ( com_affinity->modified )
1634 		{
1635 			com_affinity->modified = qfalse;
1636 			Sys_SetProcessorAffinity();
1637 		}
1638 
1639 		com_frameNumber++;
1640 	}
1641 	catch (int code) {
1642 		Com_CatchError (code);
1643 		Com_Printf ("%s\n", Com_ErrorString (code));
1644 		return;
1645 	}
1646 
1647 #ifdef G2_PERFORMANCE_ANALYSIS
1648 	G2Time_PreciseFrame += G2PerformanceTimer_PreciseFrame.End();
1649 
1650 	if (com_G2Report && com_G2Report->integer)
1651 	{
1652 		G2Time_ReportTimers();
1653 	}
1654 
1655 	G2Time_ResetTimers();
1656 #endif
1657 }
1658 
1659 /*
1660 =================
1661 Com_Shutdown
1662 =================
1663 */
1664 void MSG_shutdownHuffman();
Com_Shutdown(void)1665 void Com_Shutdown (void)
1666 {
1667 	CM_ClearMap();
1668 
1669 	if (logfile) {
1670 		FS_FCloseFile (logfile);
1671 		logfile = 0;
1672 		com_logfile->integer = 0;//don't open up the log file again!!
1673 	}
1674 
1675 	if ( com_journalFile ) {
1676 		FS_FCloseFile( com_journalFile );
1677 		com_journalFile = 0;
1678 	}
1679 
1680 	MSG_shutdownHuffman();
1681 /*
1682 	// Only used for testing changes to huffman frequency table when tuning.
1683 	{
1684 		extern float Huff_GetCR(void);
1685 		char mess[256];
1686 		sprintf(mess,"Eff. CR = %f\n",Huff_GetCR());
1687 		OutputDebugString(mess);
1688 	}
1689 */
1690 }
1691 
1692 /*
1693 ==================
1694 Field_Clear
1695 ==================
1696 */
Field_Clear(field_t * edit)1697 void Field_Clear( field_t *edit ) {
1698 	memset(edit->buffer, 0, MAX_EDIT_LINE);
1699 	edit->cursor = 0;
1700 	edit->scroll = 0;
1701 }
1702 
1703 /*
1704 =============================================================================
1705 
1706 CONSOLE LINE EDITING
1707 
1708 ==============================================================================
1709 */
1710 
1711 static const char *completionString;
1712 static char shortestMatch[MAX_TOKEN_CHARS];
1713 static int	matchCount;
1714 // field we are working on, passed to Field_AutoComplete(&g_consoleCommand for instance)
1715 static field_t *completionField;
1716 
1717 /*
1718 ===============
1719 FindMatches
1720 
1721 ===============
1722 */
FindMatches(const char * s)1723 static void FindMatches( const char *s ) {
1724 	int		i;
1725 
1726 	if ( Q_stricmpn( s, completionString, strlen( completionString ) ) ) {
1727 		return;
1728 	}
1729 	matchCount++;
1730 	if ( matchCount == 1 ) {
1731 		Q_strncpyz( shortestMatch, s, sizeof( shortestMatch ) );
1732 		return;
1733 	}
1734 
1735 	// cut shortestMatch to the amount common with s
1736 	for ( i = 0 ; s[i] ; i++ ) {
1737 		if ( tolower(shortestMatch[i]) != tolower(s[i]) ) {
1738 			shortestMatch[i] = 0;
1739 			break;
1740 		}
1741 	}
1742 	if (!s[i])
1743 	{
1744 		shortestMatch[i] = 0;
1745 	}
1746 }
1747 
1748 /*
1749 ===============
1750 PrintMatches
1751 
1752 ===============
1753 */
1754 char *Cmd_DescriptionString( const char *cmd_name );
PrintMatches(const char * s)1755 static void PrintMatches( const char *s ) {
1756 	if ( !Q_stricmpn( s, shortestMatch, (int)strlen( shortestMatch ) ) ) {
1757 		const char *description = Cmd_DescriptionString( s );
1758 		Com_Printf( S_COLOR_GREY "Cmd   " S_COLOR_WHITE "%s\n", s );
1759 		if ( VALIDSTRING( description ) )
1760 			Com_Printf( S_COLOR_GREEN "      %s" S_COLOR_WHITE "\n", description );
1761 	}
1762 }
1763 
1764 /*
1765 ===============
1766 PrintArgMatches
1767 
1768 ===============
1769 */
1770 #if 0
1771 // This is here for if ever commands with other argument completion
1772 static void PrintArgMatches( const char *s ) {
1773 	if ( !Q_stricmpn( s, shortestMatch, strlen( shortestMatch ) ) ) {
1774 		Com_Printf( S_COLOR_WHITE "  %s\n", s );
1775 	}
1776 }
1777 #endif
1778 
1779 #ifndef DEDICATED
1780 /*
1781 ===============
1782 PrintKeyMatches
1783 
1784 ===============
1785 */
PrintKeyMatches(const char * s)1786 static void PrintKeyMatches( const char *s ) {
1787 	if ( !Q_stricmpn( s, shortestMatch, strlen( shortestMatch ) ) ) {
1788 		Com_Printf( S_COLOR_GREY "Key  " S_COLOR_WHITE "%s\n", s );
1789 	}
1790 }
1791 #endif
1792 
1793 /*
1794 ===============
1795 PrintFileMatches
1796 
1797 ===============
1798 */
PrintFileMatches(const char * s)1799 static void PrintFileMatches( const char *s ) {
1800 	if ( !Q_stricmpn( s, shortestMatch, strlen( shortestMatch ) ) ) {
1801 		Com_Printf( S_COLOR_GREY "File " S_COLOR_WHITE "%s\n", s );
1802 	}
1803 }
1804 
1805 /*
1806 ===============
1807 PrintCvarMatches
1808 
1809 ===============
1810 */
1811 char *Cvar_DescriptionString( const char *var_name );
PrintCvarMatches(const char * s)1812 static void PrintCvarMatches( const char *s ) {
1813 	if ( !Q_stricmpn( s, shortestMatch, (int)strlen( shortestMatch ) ) ) {
1814 		char value[TRUNCATE_LENGTH] = {0};
1815 		const char *description = Cvar_DescriptionString( s );
1816 		Com_TruncateLongString( value, Cvar_VariableString( s ) );
1817 		Com_Printf( S_COLOR_GREY "Cvar  " S_COLOR_WHITE "%s = " S_COLOR_GREY "\"" S_COLOR_WHITE "%s" S_COLOR_GREY "\"" S_COLOR_WHITE "\n", s, value );
1818 		if ( VALIDSTRING( description ) )
1819 			Com_Printf( S_COLOR_GREEN "      %s" S_COLOR_WHITE "\n", description );
1820 	}
1821 }
1822 
1823 /*
1824 ===============
1825 Field_FindFirstSeparator
1826 ===============
1827 */
Field_FindFirstSeparator(char * s)1828 static char *Field_FindFirstSeparator( char *s ) {
1829 	for ( size_t i=0; i<strlen( s ); i++ ) {
1830 		if ( s[i] == ';' )
1831 			return &s[ i ];
1832 	}
1833 
1834 	return NULL;
1835 }
1836 
1837 /*
1838 ===============
1839 Field_Complete
1840 ===============
1841 */
Field_Complete(void)1842 static qboolean Field_Complete( void ) {
1843 	int completionOffset;
1844 
1845 	if ( matchCount == 0 )
1846 		return qtrue;
1847 
1848 	completionOffset = strlen( completionField->buffer ) - strlen( completionString );
1849 
1850 	Q_strncpyz( &completionField->buffer[completionOffset], shortestMatch, sizeof( completionField->buffer ) - completionOffset );
1851 
1852 	completionField->cursor = strlen( completionField->buffer );
1853 
1854 	if ( matchCount == 1 ) {
1855 		Q_strcat( completionField->buffer, sizeof( completionField->buffer ), " " );
1856 		completionField->cursor++;
1857 		return qtrue;
1858 	}
1859 
1860 	Com_Printf( "%c%s\n", CONSOLE_PROMPT_CHAR, completionField->buffer );
1861 
1862 	return qfalse;
1863 }
1864 
1865 #ifndef DEDICATED
1866 /*
1867 ===============
1868 Field_CompleteKeyname
1869 ===============
1870 */
Field_CompleteKeyname(void)1871 void Field_CompleteKeyname( void )
1872 {
1873 	matchCount = 0;
1874 	shortestMatch[ 0 ] = 0;
1875 
1876 	Key_KeynameCompletion( FindMatches );
1877 
1878 	if( !Field_Complete( ) )
1879 		Key_KeynameCompletion( PrintKeyMatches );
1880 }
1881 #endif
1882 
1883 /*
1884 ===============
1885 Field_CompleteFilename
1886 ===============
1887 */
Field_CompleteFilename(const char * dir,const char * ext,qboolean stripExt,qboolean allowNonPureFilesOnDisk)1888 void Field_CompleteFilename( const char *dir, const char *ext, qboolean stripExt, qboolean allowNonPureFilesOnDisk )
1889 {
1890 	matchCount = 0;
1891 	shortestMatch[ 0 ] = 0;
1892 
1893 	FS_FilenameCompletion( dir, ext, stripExt, FindMatches, allowNonPureFilesOnDisk );
1894 
1895 	if ( !Field_Complete() )
1896 		FS_FilenameCompletion( dir, ext, stripExt, PrintFileMatches, allowNonPureFilesOnDisk );
1897 }
1898 
1899 /*
1900 ===============
1901 Field_CompleteCommand
1902 ===============
1903 */
Field_CompleteCommand(char * cmd,qboolean doCommands,qboolean doCvars)1904 void Field_CompleteCommand( char *cmd, qboolean doCommands, qboolean doCvars )
1905 {
1906 	int completionArgument = 0;
1907 
1908 	// Skip leading whitespace and quotes
1909 	cmd = Com_SkipCharset( cmd, " \"" );
1910 
1911 	Cmd_TokenizeStringIgnoreQuotes( cmd );
1912 	completionArgument = Cmd_Argc();
1913 
1914 	// If there is trailing whitespace on the cmd
1915 	if ( *(cmd + strlen( cmd )-1) == ' ' ) {
1916 		completionString = "";
1917 		completionArgument++;
1918 	}
1919 	else
1920 		completionString = Cmd_Argv( completionArgument - 1 );
1921 
1922 	if ( completionArgument > 1 ) {
1923 		const char *baseCmd = Cmd_Argv( 0 );
1924 		char *p;
1925 
1926 #ifndef DEDICATED
1927 		if ( baseCmd[0] == '\\' || baseCmd[0] == '/' )
1928 			baseCmd++;
1929 #endif
1930 
1931 		if( ( p = Field_FindFirstSeparator( cmd ) ) )
1932 			Field_CompleteCommand( p + 1, qtrue, qtrue ); // Compound command
1933 		else
1934 			Cmd_CompleteArgument( baseCmd, cmd, completionArgument );
1935 	}
1936 	else {
1937 		if ( completionString[0] == '\\' || completionString[0] == '/' )
1938 			completionString++;
1939 
1940 		matchCount = 0;
1941 		shortestMatch[ 0 ] = 0;
1942 
1943 		if ( strlen( completionString ) == 0 )
1944 			return;
1945 
1946 		if ( doCommands )
1947 			Cmd_CommandCompletion( FindMatches );
1948 
1949 		if ( doCvars )
1950 			Cvar_CommandCompletion( FindMatches );
1951 
1952 		if ( !Field_Complete() ) {
1953 			// run through again, printing matches
1954 			if ( doCommands )
1955 				Cmd_CommandCompletion( PrintMatches );
1956 
1957 			if ( doCvars )
1958 				Cvar_CommandCompletion( PrintCvarMatches );
1959 		}
1960 	}
1961 }
1962 
1963 /*
1964 ===============
1965 Field_AutoComplete
1966 
1967 Perform Tab expansion
1968 ===============
1969 */
Field_AutoComplete(field_t * field)1970 void Field_AutoComplete( field_t *field ) {
1971 	if ( !field || !field->buffer[0] )
1972 		return;
1973 
1974 	completionField = field;
1975 
1976 	Field_CompleteCommand( completionField->buffer, qtrue, qtrue );
1977 }
1978 
1979 /*
1980 ==================
1981 Com_RandomBytes
1982 
1983 fills string array with len radom bytes, peferably from the OS randomizer
1984 ==================
1985 */
Com_RandomBytes(byte * string,int len)1986 void Com_RandomBytes( byte *string, int len )
1987 {
1988 	int i;
1989 
1990 	if( Sys_RandomBytes( string, len ) )
1991 		return;
1992 
1993 	Com_Printf( "Com_RandomBytes: using weak randomization\n" );
1994 	for( i = 0; i < len; i++ )
1995 		string[i] = (unsigned char)( rand() % 256 );
1996 }
1997 
1998 /*
1999 ===============
2000 Converts a UTF-8 character to UTF-32.
2001 ===============
2002 */
ConvertUTF8ToUTF32(char * utf8CurrentChar,char ** utf8NextChar)2003 uint32_t ConvertUTF8ToUTF32( char *utf8CurrentChar, char **utf8NextChar )
2004 {
2005 	uint32_t utf32 = 0;
2006 	char *c = utf8CurrentChar;
2007 
2008 	if( ( *c & 0x80 ) == 0 )
2009 		utf32 = *c++;
2010 	else if( ( *c & 0xE0 ) == 0xC0 ) // 110x xxxx
2011 	{
2012 		utf32 |= ( *c++ & 0x1F ) << 6;
2013 		utf32 |= ( *c++ & 0x3F );
2014 	}
2015 	else if( ( *c & 0xF0 ) == 0xE0 ) // 1110 xxxx
2016 	{
2017 		utf32 |= ( *c++ & 0x0F ) << 12;
2018 		utf32 |= ( *c++ & 0x3F ) << 6;
2019 		utf32 |= ( *c++ & 0x3F );
2020 	}
2021 	else if( ( *c & 0xF8 ) == 0xF0 ) // 1111 0xxx
2022 	{
2023 		utf32 |= ( *c++ & 0x07 ) << 18;
2024 		utf32 |= ( *c++ & 0x3F ) << 12;
2025 		utf32 |= ( *c++ & 0x3F ) << 6;
2026 		utf32 |= ( *c++ & 0x3F );
2027 	}
2028 	else
2029 	{
2030 		Com_DPrintf( "Unrecognised UTF-8 lead byte: 0x%x\n", (unsigned int)*c );
2031 		c++;
2032 	}
2033 
2034 	*utf8NextChar = c;
2035 
2036 	return utf32;
2037 }
2038