1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4 
5 This file is part of Quake III Arena source code.
6 
7 Quake III Arena source code is free software; you can redistribute it
8 and/or modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the License,
10 or (at your option) any later version.
11 
12 Quake III Arena source code is distributed in the hope that it will be
13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with Quake III Arena source code; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 ===========================================================================
21 */
22 // common.c -- misc functions used in client and server
23 
24 #include "q_shared.h"
25 #include "qcommon.h"
26 #include <setjmp.h>
27 #ifndef _WIN32
28 #include <sys/types.h>
29 #include <netinet/in.h>
30 #include <sys/stat.h> // umask
31 #else
32 #include <winsock.h>
33 #endif
34 
35 int demo_protocols[] =
36 { 66, 67, 68, 0 };
37 
38 #define MAX_NUM_ARGVS	50
39 
40 #define MIN_DEDICATED_COMHUNKMEGS 1
41 #define MIN_COMHUNKMEGS		96
42 #define DEF_COMHUNKMEGS		128
43 #define DEF_COMZONEMEGS		24
44 #define XSTRING(x)				STRING(x)
45 #define STRING(x)					#x
46 #define DEF_COMHUNKMEGS_S	XSTRING(DEF_COMHUNKMEGS)
47 #define DEF_COMZONEMEGS_S	XSTRING(DEF_COMZONEMEGS)
48 
49 int		com_argc;
50 char	*com_argv[MAX_NUM_ARGVS+1];
51 
52 jmp_buf abortframe;		// an ERR_DROP occured, exit the entire frame
53 
54 
55 FILE *debuglogfile;
56 static fileHandle_t logfile;
57 fileHandle_t	com_journalFile;			// events are written here
58 fileHandle_t	com_journalDataFile;		// config files are written here
59 
60 cvar_t	*com_speeds;
61 cvar_t	*com_developer;
62 cvar_t	*com_dedicated;
63 cvar_t	*com_timescale;
64 cvar_t	*com_fixedtime;
65 cvar_t	*com_dropsim;		// 0.0 to 1.0, simulated packet drops
66 cvar_t	*com_journal;
67 cvar_t	*com_maxfps;
68 cvar_t	*com_altivec;
69 cvar_t	*com_timedemo;
70 cvar_t	*com_sv_running;
71 cvar_t	*com_cl_running;
72 cvar_t	*com_logfile;		// 1 = buffer log, 2 = flush after each print
73 cvar_t	*com_showtrace;
74 cvar_t	*com_version;
75 cvar_t	*com_blood;
76 cvar_t	*com_buildScript;	// for automated data building scripts
77 cvar_t	*com_introPlayed;
78 cvar_t	*cl_paused;
79 cvar_t	*sv_paused;
80 cvar_t  *cl_packetdelay;
81 cvar_t  *sv_packetdelay;
82 cvar_t	*com_cameraMode;
83 cvar_t	*com_ansiColor;
84 cvar_t	*com_unfocused;
85 cvar_t	*com_minimized;
86 cvar_t	*com_standalone;
87 cvar_t	*win_multiUser;
88 
89 // com_speeds times
90 int		time_game;
91 int		time_frontend;		// renderer frontend time
92 int		time_backend;		// renderer backend time
93 
94 int			com_frameTime;
95 int			com_frameMsec;
96 int			com_frameNumber;
97 
98 qboolean	com_errorEntered;
99 qboolean	com_fullyInitialized;
100 
101 char	com_errorMessage[MAXPRINTMSG];
102 
103 void Com_WriteConfig_f( void );
104 void CIN_CloseAllVideos( void );
105 
106 //============================================================================
107 
108 static char	*rd_buffer;
109 static int	rd_buffersize;
110 static void	(*rd_flush)( char *buffer );
111 
Com_BeginRedirect(char * buffer,int buffersize,void (* flush)(char *))112 void Com_BeginRedirect (char *buffer, int buffersize, void (*flush)( char *) )
113 {
114 	if (!buffer || !buffersize || !flush)
115 		return;
116 	rd_buffer = buffer;
117 	rd_buffersize = buffersize;
118 	rd_flush = flush;
119 
120 	*rd_buffer = 0;
121 }
122 
Com_EndRedirect(void)123 void Com_EndRedirect (void)
124 {
125 	if ( rd_flush ) {
126 		rd_flush(rd_buffer);
127 	}
128 
129 	rd_buffer = NULL;
130 	rd_buffersize = 0;
131 	rd_flush = NULL;
132 }
133 
134 /*
135 =============
136 Com_Printf
137 
138 Both client and server can use this, and it will output
139 to the apropriate place.
140 
141 A raw string should NEVER be passed as fmt, because of "%f" type crashers.
142 =============
143 */
Com_Printf(const char * fmt,...)144 void QDECL Com_Printf( const char *fmt, ... ) {
145 	va_list		argptr;
146 	char		msg[MAXPRINTMSG];
147   static qboolean opening_qconsole = qfalse;
148 
149 
150 	va_start (argptr,fmt);
151 	Q_vsnprintf (msg, sizeof(msg), fmt, argptr);
152 	va_end (argptr);
153 
154 	if ( rd_buffer ) {
155 		if ((strlen (msg) + strlen(rd_buffer)) > (rd_buffersize - 1)) {
156 			rd_flush(rd_buffer);
157 			*rd_buffer = 0;
158 		}
159 		Q_strcat(rd_buffer, rd_buffersize, msg);
160     // TTimo nooo .. that would defeat the purpose
161 		//rd_flush(rd_buffer);
162 		//*rd_buffer = 0;
163 		return;
164 	}
165 
166 #ifndef DEDICATED
167 	CL_ConsolePrint( msg );
168 #endif
169 
170 	// echo to dedicated console and early console
171 	Sys_Print( msg );
172 
173 	// logfile
174 	if ( com_logfile && com_logfile->integer ) {
175     // TTimo: only open the qconsole.log if the filesystem is in an initialized state
176     //   also, avoid recursing in the qconsole.log opening (i.e. if fs_debug is on)
177 		if ( !logfile && FS_Initialized() && !opening_qconsole) {
178 			struct tm *newtime;
179 			time_t aclock;
180 
181       opening_qconsole = qtrue;
182 
183 			time( &aclock );
184 			newtime = localtime( &aclock );
185 
186 			logfile = FS_FOpenFileWrite( "qconsole.log" );
187 
188 			if(logfile)
189 			{
190 				Com_Printf( "logfile opened on %s\n", asctime( newtime ) );
191 
192 				if ( com_logfile->integer > 1 )
193 				{
194 					// force it to not buffer so we get valid
195 					// data even if we are crashing
196 					FS_ForceFlush(logfile);
197 				}
198 			}
199 			else
200 			{
201 				Com_Printf("Opening qconsole.log failed!\n");
202 				Cvar_SetValue("logfile", 0);
203 			}
204 
205       opening_qconsole = qfalse;
206 		}
207 		if ( logfile && FS_Initialized()) {
208 			FS_Write(msg, strlen(msg), logfile);
209 		}
210 	}
211 }
212 
213 
214 /*
215 ================
216 Com_DPrintf
217 
218 A Com_Printf that only shows up if the "developer" cvar is set
219 ================
220 */
Com_DPrintf(const char * fmt,...)221 void QDECL Com_DPrintf( const char *fmt, ...) {
222 	va_list		argptr;
223 	char		msg[MAXPRINTMSG];
224 
225 	if ( !com_developer || !com_developer->integer ) {
226 		return;			// don't confuse non-developers with techie stuff...
227 	}
228 
229 	va_start (argptr,fmt);
230 	Q_vsnprintf (msg, sizeof(msg), fmt, argptr);
231 	va_end (argptr);
232 
233 	Com_Printf ("%s", msg);
234 }
235 
236 /*
237 =============
238 Com_Error
239 
240 Both client and server can use this, and it will
241 do the apropriate things.
242 =============
243 */
Com_Error(int code,const char * fmt,...)244 void QDECL Com_Error( int code, const char *fmt, ... ) {
245 	va_list		argptr;
246 	static int	lastErrorTime;
247 	static int	errorCount;
248 	int			currentTime;
249 
250 	Cvar_Set( "com_errorCode", va( "%i", code ) );
251 
252 	// when we are running automated scripts, make sure we
253 	// know if anything failed
254 	if ( com_buildScript && com_buildScript->integer ) {
255 		code = ERR_FATAL;
256 	}
257 
258 	// if we are getting a solid stream of ERR_DROP, do an ERR_FATAL
259 	currentTime = Sys_Milliseconds();
260 	if ( currentTime - lastErrorTime < 100 ) {
261 		if ( ++errorCount > 3 ) {
262 			code = ERR_FATAL;
263 		}
264 	} else {
265 		errorCount = 0;
266 	}
267 	lastErrorTime = currentTime;
268 
269 	if ( com_errorEntered ) {
270 		Sys_Error( "recursive error after: %s", com_errorMessage );
271 	}
272 	com_errorEntered = qtrue;
273 
274 	va_start (argptr,fmt);
275 	Q_vsnprintf (com_errorMessage, sizeof(com_errorMessage),fmt,argptr);
276 	va_end (argptr);
277 
278 	if (code != ERR_DISCONNECT && code != ERR_NEED_CD)
279 		Cvar_Set("com_errorMessage", com_errorMessage);
280 
281 	if (code == ERR_DISCONNECT || code == ERR_SERVERDISCONNECT) {
282 		CL_Disconnect( qtrue );
283 		VM_Forced_Unload_Start();
284 		CL_FlushMemory( );
285 		VM_Forced_Unload_Done();
286 		// make sure we can get at our local stuff
287 		FS_PureServerSetLoadedPaks("", "");
288 		com_errorEntered = qfalse;
289 		longjmp (abortframe, -1);
290 	} else if (code == ERR_DROP) {
291 		Com_Printf ("********************\nERROR: %s\n********************\n", com_errorMessage);
292 		SV_Shutdown (va("Server crashed: %s",  com_errorMessage));
293 		CL_Disconnect( qtrue );
294 		VM_Forced_Unload_Start();
295 		CL_FlushMemory( );
296 		VM_Forced_Unload_Done();
297 		FS_PureServerSetLoadedPaks("", "");
298 		com_errorEntered = qfalse;
299 		longjmp (abortframe, -1);
300 	} else if ( code == ERR_NEED_CD ) {
301 		SV_Shutdown( "Server didn't have CD" );
302 		if ( com_cl_running && com_cl_running->integer ) {
303 			CL_Disconnect( qtrue );
304 			VM_Forced_Unload_Start();
305 			CL_FlushMemory( );
306 			VM_Forced_Unload_Done();
307 			com_errorEntered = qfalse;
308 			CL_CDDialog();
309 		} else {
310 			Com_Printf("Server didn't have CD\n" );
311 		}
312 		FS_PureServerSetLoadedPaks("", "");
313 		longjmp (abortframe, -1);
314 	} else {
315 		CL_Shutdown ();
316 		SV_Shutdown (va("Server fatal crashed: %s", com_errorMessage));
317 	}
318 
319 	Com_Shutdown ();
320 
321 	Sys_Error ("%s", com_errorMessage);
322 }
323 
324 
325 /*
326 =============
327 Com_Quit_f
328 
329 Both client and server can use this, and it will
330 do the apropriate things.
331 =============
332 */
Com_Quit_f(void)333 void Com_Quit_f( void ) {
334 	// don't try to shutdown if we are in a recursive error
335 	if ( !com_errorEntered ) {
336 		SV_Shutdown ("Server quit");
337 		CL_Shutdown ();
338 		Com_Shutdown ();
339 		FS_Shutdown(qtrue);
340 	}
341 	Sys_Quit ();
342 }
343 
344 
345 
346 /*
347 ============================================================================
348 
349 COMMAND LINE FUNCTIONS
350 
351 + characters seperate the commandLine string into multiple console
352 command lines.
353 
354 All of these are valid:
355 
356 quake3 +set test blah +map test
357 quake3 set test blah+map test
358 quake3 set test blah + map test
359 
360 ============================================================================
361 */
362 
363 #define	MAX_CONSOLE_LINES	32
364 int		com_numConsoleLines;
365 char	*com_consoleLines[MAX_CONSOLE_LINES];
366 
367 /*
368 ==================
369 Com_ParseCommandLine
370 
371 Break it up into multiple console lines
372 ==================
373 */
Com_ParseCommandLine(char * commandLine)374 void Com_ParseCommandLine( char *commandLine ) {
375     int inq = 0;
376     com_consoleLines[0] = commandLine;
377     com_numConsoleLines = 1;
378 
379     while ( *commandLine ) {
380         if (*commandLine == '"') {
381             inq = !inq;
382         }
383         // look for a + seperating character
384         // if commandLine came from a file, we might have real line seperators
385         if ( (*commandLine == '+' && !inq) || *commandLine == '\n'  || *commandLine == '\r' ) {
386             if ( com_numConsoleLines == MAX_CONSOLE_LINES ) {
387                 return;
388             }
389             com_consoleLines[com_numConsoleLines] = commandLine + 1;
390             com_numConsoleLines++;
391             *commandLine = 0;
392         }
393         commandLine++;
394     }
395 }
396 
397 
398 /*
399 ===================
400 Com_SafeMode
401 
402 Check for "safe" on the command line, which will
403 skip loading of q3config.cfg
404 ===================
405 */
Com_SafeMode(void)406 qboolean Com_SafeMode( void ) {
407 	int		i;
408 
409 	for ( i = 0 ; i < com_numConsoleLines ; i++ ) {
410 		Cmd_TokenizeString( com_consoleLines[i] );
411 		if ( !Q_stricmp( Cmd_Argv(0), "safe" )
412 			|| !Q_stricmp( Cmd_Argv(0), "cvar_restart" ) ) {
413 			com_consoleLines[i][0] = 0;
414 			return qtrue;
415 		}
416 	}
417 	return qfalse;
418 }
419 
420 
421 /*
422 ===============
423 Com_StartupVariable
424 
425 Searches for command line parameters that are set commands.
426 If match is not NULL, only that cvar will be looked for.
427 That is necessary because cddir and basedir need to be set
428 before the filesystem is started, but all other sets should
429 be after execing the config and default.
430 ===============
431 */
Com_StartupVariable(const char * match)432 void Com_StartupVariable( const char *match ) {
433 	int		i;
434 	char	*s;
435 	cvar_t	*cv;
436 
437 	for (i=0 ; i < com_numConsoleLines ; i++) {
438 		Cmd_TokenizeString( com_consoleLines[i] );
439 		if ( strcmp( Cmd_Argv(0), "set" ) ) {
440 			continue;
441 		}
442 
443 		s = Cmd_Argv(1);
444 		if ( !match || !strcmp( s, match ) ) {
445 			Cvar_Set( s, Cmd_Argv(2) );
446 			cv = Cvar_Get( s, "", 0 );
447 			cv->flags |= CVAR_USER_CREATED;
448 //			com_consoleLines[i] = 0;
449 		}
450 	}
451 }
452 
453 
454 /*
455 =================
456 Com_AddStartupCommands
457 
458 Adds command line parameters as script statements
459 Commands are seperated by + signs
460 
461 Returns qtrue if any late commands were added, which
462 will keep the demoloop from immediately starting
463 =================
464 */
Com_AddStartupCommands(void)465 qboolean Com_AddStartupCommands( void ) {
466 	int		i;
467 	qboolean	added;
468 
469 	added = qfalse;
470 	// quote every token, so args with semicolons can work
471 	for (i=0 ; i < com_numConsoleLines ; i++) {
472 		if ( !com_consoleLines[i] || !com_consoleLines[i][0] ) {
473 			continue;
474 		}
475 
476 		// set commands won't override menu startup
477 		if ( Q_stricmpn( com_consoleLines[i], "set", 3 ) ) {
478 			added = qtrue;
479 		}
480 		Cbuf_AddText( com_consoleLines[i] );
481 		Cbuf_AddText( "\n" );
482 	}
483 
484 	return added;
485 }
486 
487 
488 //============================================================================
489 
Info_Print(const char * s)490 void Info_Print( const char *s ) {
491 	char	key[512];
492 	char	value[512];
493 	char	*o;
494 	int		l;
495 
496 	if (*s == '\\')
497 		s++;
498 	while (*s)
499 	{
500 		o = key;
501 		while (*s && *s != '\\')
502 			*o++ = *s++;
503 
504 		l = o - key;
505 		if (l < 20)
506 		{
507 			Com_Memset (o, ' ', 20-l);
508 			key[20] = 0;
509 		}
510 		else
511 			*o = 0;
512 		Com_Printf ("%s", key);
513 
514 		if (!*s)
515 		{
516 			Com_Printf ("MISSING VALUE\n");
517 			return;
518 		}
519 
520 		o = value;
521 		s++;
522 		while (*s && *s != '\\')
523 			*o++ = *s++;
524 		*o = 0;
525 
526 		if (*s)
527 			s++;
528 		Com_Printf ("%s\n", value);
529 	}
530 }
531 
532 /*
533 ============
534 Com_StringContains
535 ============
536 */
Com_StringContains(char * str1,char * str2,int casesensitive)537 char *Com_StringContains(char *str1, char *str2, int casesensitive) {
538 	int len, i, j;
539 
540 	len = strlen(str1) - strlen(str2);
541 	for (i = 0; i <= len; i++, str1++) {
542 		for (j = 0; str2[j]; j++) {
543 			if (casesensitive) {
544 				if (str1[j] != str2[j]) {
545 					break;
546 				}
547 			}
548 			else {
549 				if (toupper(str1[j]) != toupper(str2[j])) {
550 					break;
551 				}
552 			}
553 		}
554 		if (!str2[j]) {
555 			return str1;
556 		}
557 	}
558 	return NULL;
559 }
560 
561 /*
562 ============
563 Com_Filter
564 ============
565 */
Com_Filter(char * filter,char * name,int casesensitive)566 int Com_Filter(char *filter, char *name, int casesensitive)
567 {
568 	char buf[MAX_TOKEN_CHARS];
569 	char *ptr;
570 	int i, found;
571 
572 	while(*filter) {
573 		if (*filter == '*') {
574 			filter++;
575 			for (i = 0; *filter; i++) {
576 				if (*filter == '*' || *filter == '?') break;
577 				buf[i] = *filter;
578 				filter++;
579 			}
580 			buf[i] = '\0';
581 			if (strlen(buf)) {
582 				ptr = Com_StringContains(name, buf, casesensitive);
583 				if (!ptr) return qfalse;
584 				name = ptr + strlen(buf);
585 			}
586 		}
587 		else if (*filter == '?') {
588 			filter++;
589 			name++;
590 		}
591 		else if (*filter == '[' && *(filter+1) == '[') {
592 			filter++;
593 		}
594 		else if (*filter == '[') {
595 			filter++;
596 			found = qfalse;
597 			while(*filter && !found) {
598 				if (*filter == ']' && *(filter+1) != ']') break;
599 				if (*(filter+1) == '-' && *(filter+2) && (*(filter+2) != ']' || *(filter+3) == ']')) {
600 					if (casesensitive) {
601 						if (*name >= *filter && *name <= *(filter+2)) found = qtrue;
602 					}
603 					else {
604 						if (toupper(*name) >= toupper(*filter) &&
605 							toupper(*name) <= toupper(*(filter+2))) found = qtrue;
606 					}
607 					filter += 3;
608 				}
609 				else {
610 					if (casesensitive) {
611 						if (*filter == *name) found = qtrue;
612 					}
613 					else {
614 						if (toupper(*filter) == toupper(*name)) found = qtrue;
615 					}
616 					filter++;
617 				}
618 			}
619 			if (!found) return qfalse;
620 			while(*filter) {
621 				if (*filter == ']' && *(filter+1) != ']') break;
622 				filter++;
623 			}
624 			filter++;
625 			name++;
626 		}
627 		else {
628 			if (casesensitive) {
629 				if (*filter != *name) return qfalse;
630 			}
631 			else {
632 				if (toupper(*filter) != toupper(*name)) return qfalse;
633 			}
634 			filter++;
635 			name++;
636 		}
637 	}
638 	return qtrue;
639 }
640 
641 /*
642 ============
643 Com_FilterPath
644 ============
645 */
Com_FilterPath(char * filter,char * name,int casesensitive)646 int Com_FilterPath(char *filter, char *name, int casesensitive)
647 {
648 	int i;
649 	char new_filter[MAX_QPATH];
650 	char new_name[MAX_QPATH];
651 
652 	for (i = 0; i < MAX_QPATH-1 && filter[i]; i++) {
653 		if ( filter[i] == '\\' || filter[i] == ':' ) {
654 			new_filter[i] = '/';
655 		}
656 		else {
657 			new_filter[i] = filter[i];
658 		}
659 	}
660 	new_filter[i] = '\0';
661 	for (i = 0; i < MAX_QPATH-1 && name[i]; i++) {
662 		if ( name[i] == '\\' || name[i] == ':' ) {
663 			new_name[i] = '/';
664 		}
665 		else {
666 			new_name[i] = name[i];
667 		}
668 	}
669 	new_name[i] = '\0';
670 	return Com_Filter(new_filter, new_name, casesensitive);
671 }
672 
673 /*
674 ============
675 Com_HashKey
676 ============
677 */
Com_HashKey(char * string,int maxlen)678 int Com_HashKey(char *string, int maxlen) {
679 	int register hash, i;
680 
681 	hash = 0;
682 	for (i = 0; i < maxlen && string[i] != '\0'; i++) {
683 		hash += string[i] * (119 + i);
684 	}
685 	hash = (hash ^ (hash >> 10) ^ (hash >> 20));
686 	return hash;
687 }
688 
689 /*
690 ================
691 Com_RealTime
692 ================
693 */
Com_RealTime(qtime_t * qtime)694 int Com_RealTime(qtime_t *qtime) {
695 	time_t t;
696 	struct tm *tms;
697 
698 	t = time(NULL);
699 	if (!qtime)
700 		return t;
701 	tms = localtime(&t);
702 	if (tms) {
703 		qtime->tm_sec = tms->tm_sec;
704 		qtime->tm_min = tms->tm_min;
705 		qtime->tm_hour = tms->tm_hour;
706 		qtime->tm_mday = tms->tm_mday;
707 		qtime->tm_mon = tms->tm_mon;
708 		qtime->tm_year = tms->tm_year;
709 		qtime->tm_wday = tms->tm_wday;
710 		qtime->tm_yday = tms->tm_yday;
711 		qtime->tm_isdst = tms->tm_isdst;
712 	}
713 	return t;
714 }
715 
716 
717 /*
718 ==============================================================================
719 
720 						ZONE MEMORY ALLOCATION
721 
722 There is never any space between memblocks, and there will never be two
723 contiguous free memblocks.
724 
725 The rover can be left pointing at a non-empty block
726 
727 The zone calls are pretty much only used for small strings and structures,
728 all big things are allocated on the hunk.
729 ==============================================================================
730 */
731 
732 #define	ZONEID	0x1d4a11
733 #define MINFRAGMENT	64
734 
735 typedef struct zonedebug_s {
736 	char *label;
737 	char *file;
738 	int line;
739 	int allocSize;
740 } zonedebug_t;
741 
742 typedef struct memblock_s {
743 	int		size;           // including the header and possibly tiny fragments
744 	int     tag;            // a tag of 0 is a free block
745 	struct memblock_s       *next, *prev;
746 	int     id;        		// should be ZONEID
747 #ifdef ZONE_DEBUG
748 	zonedebug_t d;
749 #endif
750 } memblock_t;
751 
752 typedef struct {
753 	int		size;			// total bytes malloced, including header
754 	int		used;			// total bytes used
755 	memblock_t	blocklist;	// start / end cap for linked list
756 	memblock_t	*rover;
757 } memzone_t;
758 
759 // main zone for all "dynamic" memory allocation
760 memzone_t	*mainzone;
761 // we also have a small zone for small allocations that would only
762 // fragment the main zone (think of cvar and cmd strings)
763 memzone_t	*smallzone;
764 
765 void Z_CheckHeap( void );
766 
767 /*
768 ========================
769 Z_ClearZone
770 ========================
771 */
Z_ClearZone(memzone_t * zone,int size)772 void Z_ClearZone( memzone_t *zone, int size ) {
773 	memblock_t	*block;
774 
775 	// set the entire zone to one free block
776 
777 	zone->blocklist.next = zone->blocklist.prev = block =
778 		(memblock_t *)( (byte *)zone + sizeof(memzone_t) );
779 	zone->blocklist.tag = 1;	// in use block
780 	zone->blocklist.id = 0;
781 	zone->blocklist.size = 0;
782 	zone->rover = block;
783 	zone->size = size;
784 	zone->used = 0;
785 
786 	block->prev = block->next = &zone->blocklist;
787 	block->tag = 0;			// free block
788 	block->id = ZONEID;
789 	block->size = size - sizeof(memzone_t);
790 }
791 
792 /*
793 ========================
794 Z_AvailableZoneMemory
795 ========================
796 */
Z_AvailableZoneMemory(memzone_t * zone)797 int Z_AvailableZoneMemory( memzone_t *zone ) {
798 	return zone->size - zone->used;
799 }
800 
801 /*
802 ========================
803 Z_AvailableMemory
804 ========================
805 */
Z_AvailableMemory(void)806 int Z_AvailableMemory( void ) {
807 	return Z_AvailableZoneMemory( mainzone );
808 }
809 
810 /*
811 ========================
812 Z_Free
813 ========================
814 */
Z_Free(void * ptr)815 void Z_Free( void *ptr ) {
816 	memblock_t	*block, *other;
817 	memzone_t *zone;
818 
819 	if (!ptr) {
820 		Com_Error( ERR_DROP, "Z_Free: NULL pointer" );
821 	}
822 
823 	block = (memblock_t *) ( (byte *)ptr - sizeof(memblock_t));
824 	if (block->id != ZONEID) {
825 		Com_Error( ERR_FATAL, "Z_Free: freed a pointer without ZONEID" );
826 	}
827 	if (block->tag == 0) {
828 		Com_Error( ERR_FATAL, "Z_Free: freed a freed pointer" );
829 	}
830 	// if static memory
831 	if (block->tag == TAG_STATIC) {
832 		return;
833 	}
834 
835 	// check the memory trash tester
836 	if ( *(int *)((byte *)block + block->size - 4 ) != ZONEID ) {
837 		Com_Error( ERR_FATAL, "Z_Free: memory block wrote past end" );
838 	}
839 
840 	if (block->tag == TAG_SMALL) {
841 		zone = smallzone;
842 	}
843 	else {
844 		zone = mainzone;
845 	}
846 
847 	zone->used -= block->size;
848 	// set the block to something that should cause problems
849 	// if it is referenced...
850 	Com_Memset( ptr, 0xaa, block->size - sizeof( *block ) );
851 
852 	block->tag = 0;		// mark as free
853 
854 	other = block->prev;
855 	if (!other->tag) {
856 		// merge with previous free block
857 		other->size += block->size;
858 		other->next = block->next;
859 		other->next->prev = other;
860 		if (block == zone->rover) {
861 			zone->rover = other;
862 		}
863 		block = other;
864 	}
865 
866 	zone->rover = block;
867 
868 	other = block->next;
869 	if ( !other->tag ) {
870 		// merge the next free block onto the end
871 		block->size += other->size;
872 		block->next = other->next;
873 		block->next->prev = block;
874 		if (other == zone->rover) {
875 			zone->rover = block;
876 		}
877 	}
878 }
879 
880 
881 /*
882 ================
883 Z_FreeTags
884 ================
885 */
Z_FreeTags(int tag)886 void Z_FreeTags( int tag ) {
887 	int			count;
888 	memzone_t	*zone;
889 
890 	if ( tag == TAG_SMALL ) {
891 		zone = smallzone;
892 	}
893 	else {
894 		zone = mainzone;
895 	}
896 	count = 0;
897 	// use the rover as our pointer, because
898 	// Z_Free automatically adjusts it
899 	zone->rover = zone->blocklist.next;
900 	do {
901 		if ( zone->rover->tag == tag ) {
902 			count++;
903 			Z_Free( (void *)(zone->rover + 1) );
904 			continue;
905 		}
906 		zone->rover = zone->rover->next;
907 	} while ( zone->rover != &zone->blocklist );
908 }
909 
910 
911 /*
912 ================
913 Z_TagMalloc
914 ================
915 */
916 #ifdef ZONE_DEBUG
Z_TagMallocDebug(int size,int tag,char * label,char * file,int line)917 void *Z_TagMallocDebug( int size, int tag, char *label, char *file, int line ) {
918 #else
919 void *Z_TagMalloc( int size, int tag ) {
920 #endif
921 	int		extra, allocSize;
922 	memblock_t	*start, *rover, *new, *base;
923 	memzone_t *zone;
924 
925 	if (!tag) {
926 		Com_Error( ERR_FATAL, "Z_TagMalloc: tried to use a 0 tag" );
927 	}
928 
929 	if ( tag == TAG_SMALL ) {
930 		zone = smallzone;
931 	}
932 	else {
933 		zone = mainzone;
934 	}
935 
936 	allocSize = size;
937 	//
938 	// scan through the block list looking for the first free block
939 	// of sufficient size
940 	//
941 	size += sizeof(memblock_t);	// account for size of block header
942 	size += 4;					// space for memory trash tester
943 	size = PAD(size, sizeof(intptr_t));		// align to 32/64 bit boundary
944 
945 	base = rover = zone->rover;
946 	start = base->prev;
947 
948 	do {
949 		if (rover == start)	{
950 #ifdef ZONE_DEBUG
951 			Z_LogHeap();
952 #endif
953 			// scaned all the way around the list
954 			Com_Error( ERR_FATAL, "Z_Malloc: failed on allocation of %i bytes from the %s zone",
955 								size, zone == smallzone ? "small" : "main");
956 			return NULL;
957 		}
958 		if (rover->tag) {
959 			base = rover = rover->next;
960 		} else {
961 			rover = rover->next;
962 		}
963 	} while (base->tag || base->size < size);
964 
965 	//
966 	// found a block big enough
967 	//
968 	extra = base->size - size;
969 	if (extra > MINFRAGMENT) {
970 		// there will be a free fragment after the allocated block
971 		new = (memblock_t *) ((byte *)base + size );
972 		new->size = extra;
973 		new->tag = 0;			// free block
974 		new->prev = base;
975 		new->id = ZONEID;
976 		new->next = base->next;
977 		new->next->prev = new;
978 		base->next = new;
979 		base->size = size;
980 	}
981 
982 	base->tag = tag;			// no longer a free block
983 
984 	zone->rover = base->next;	// next allocation will start looking here
985 	zone->used += base->size;	//
986 
987 	base->id = ZONEID;
988 
989 #ifdef ZONE_DEBUG
990 	base->d.label = label;
991 	base->d.file = file;
992 	base->d.line = line;
993 	base->d.allocSize = allocSize;
994 #endif
995 
996 	// marker for memory trash testing
997 	*(int *)((byte *)base + base->size - 4) = ZONEID;
998 
999 	return (void *) ((byte *)base + sizeof(memblock_t));
1000 }
1001 
1002 /*
1003 ========================
1004 Z_Malloc
1005 ========================
1006 */
1007 #ifdef ZONE_DEBUG
1008 void *Z_MallocDebug( int size, char *label, char *file, int line ) {
1009 #else
1010 void *Z_Malloc( int size ) {
1011 #endif
1012 	void	*buf;
1013 
1014   //Z_CheckHeap ();	// DEBUG
1015 
1016 #ifdef ZONE_DEBUG
1017 	buf = Z_TagMallocDebug( size, TAG_GENERAL, label, file, line );
1018 #else
1019 	buf = Z_TagMalloc( size, TAG_GENERAL );
1020 #endif
1021 	Com_Memset( buf, 0, size );
1022 
1023 	return buf;
1024 }
1025 
1026 #ifdef ZONE_DEBUG
1027 void *S_MallocDebug( int size, char *label, char *file, int line ) {
1028 	return Z_TagMallocDebug( size, TAG_SMALL, label, file, line );
1029 }
1030 #else
1031 void *S_Malloc( int size ) {
1032 	return Z_TagMalloc( size, TAG_SMALL );
1033 }
1034 #endif
1035 
1036 /*
1037 ========================
1038 Z_CheckHeap
1039 ========================
1040 */
1041 void Z_CheckHeap( void ) {
1042 	memblock_t	*block;
1043 
1044 	for (block = mainzone->blocklist.next ; ; block = block->next) {
1045 		if (block->next == &mainzone->blocklist) {
1046 			break;			// all blocks have been hit
1047 		}
1048 		if ( (byte *)block + block->size != (byte *)block->next)
1049 			Com_Error( ERR_FATAL, "Z_CheckHeap: block size does not touch the next block\n" );
1050 		if ( block->next->prev != block) {
1051 			Com_Error( ERR_FATAL, "Z_CheckHeap: next block doesn't have proper back link\n" );
1052 		}
1053 		if ( !block->tag && !block->next->tag ) {
1054 			Com_Error( ERR_FATAL, "Z_CheckHeap: two consecutive free blocks\n" );
1055 		}
1056 	}
1057 }
1058 
1059 /*
1060 ========================
1061 Z_LogZoneHeap
1062 ========================
1063 */
1064 void Z_LogZoneHeap( memzone_t *zone, char *name ) {
1065 #ifdef ZONE_DEBUG
1066 	char dump[32], *ptr;
1067 	int  i, j;
1068 #endif
1069 	memblock_t	*block;
1070 	char		buf[4096];
1071 	int size, allocSize, numBlocks;
1072 
1073 	if (!logfile || !FS_Initialized())
1074 		return;
1075 	size = allocSize = numBlocks = 0;
1076 	Com_sprintf(buf, sizeof(buf), "\r\n================\r\n%s log\r\n================\r\n", name);
1077 	FS_Write(buf, strlen(buf), logfile);
1078 	for (block = zone->blocklist.next ; block->next != &zone->blocklist; block = block->next) {
1079 		if (block->tag) {
1080 #ifdef ZONE_DEBUG
1081 			ptr = ((char *) block) + sizeof(memblock_t);
1082 			j = 0;
1083 			for (i = 0; i < 20 && i < block->d.allocSize; i++) {
1084 				if (ptr[i] >= 32 && ptr[i] < 127) {
1085 					dump[j++] = ptr[i];
1086 				}
1087 				else {
1088 					dump[j++] = '_';
1089 				}
1090 			}
1091 			dump[j] = '\0';
1092 			Com_sprintf(buf, sizeof(buf), "size = %8d: %s, line: %d (%s) [%s]\r\n", block->d.allocSize, block->d.file, block->d.line, block->d.label, dump);
1093 			FS_Write(buf, strlen(buf), logfile);
1094 			allocSize += block->d.allocSize;
1095 #endif
1096 			size += block->size;
1097 			numBlocks++;
1098 		}
1099 	}
1100 #ifdef ZONE_DEBUG
1101 	// subtract debug memory
1102 	size -= numBlocks * sizeof(zonedebug_t);
1103 #else
1104 	allocSize = numBlocks * sizeof(memblock_t); // + 32 bit alignment
1105 #endif
1106 	Com_sprintf(buf, sizeof(buf), "%d %s memory in %d blocks\r\n", size, name, numBlocks);
1107 	FS_Write(buf, strlen(buf), logfile);
1108 	Com_sprintf(buf, sizeof(buf), "%d %s memory overhead\r\n", size - allocSize, name);
1109 	FS_Write(buf, strlen(buf), logfile);
1110 }
1111 
1112 /*
1113 ========================
1114 Z_LogHeap
1115 ========================
1116 */
1117 void Z_LogHeap( void ) {
1118 	Z_LogZoneHeap( mainzone, "MAIN" );
1119 	Z_LogZoneHeap( smallzone, "SMALL" );
1120 }
1121 
1122 // static mem blocks to reduce a lot of small zone overhead
1123 typedef struct memstatic_s {
1124 	memblock_t b;
1125 	byte mem[2];
1126 } memstatic_t;
1127 
1128 memstatic_t emptystring =
1129 	{ {(sizeof(memblock_t)+2 + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'\0', '\0'} };
1130 memstatic_t numberstring[] = {
1131 	{ {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'0', '\0'} },
1132 	{ {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'1', '\0'} },
1133 	{ {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'2', '\0'} },
1134 	{ {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'3', '\0'} },
1135 	{ {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'4', '\0'} },
1136 	{ {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'5', '\0'} },
1137 	{ {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'6', '\0'} },
1138 	{ {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'7', '\0'} },
1139 	{ {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'8', '\0'} },
1140 	{ {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'9', '\0'} }
1141 };
1142 
1143 /*
1144 ========================
1145 CopyString
1146 
1147  NOTE:	never write over the memory CopyString returns because
1148 		memory from a memstatic_t might be returned
1149 ========================
1150 */
1151 char *CopyString( const char *in ) {
1152 	char	*out;
1153 
1154 	if (!in[0]) {
1155 		return ((char *)&emptystring) + sizeof(memblock_t);
1156 	}
1157 	else if (!in[1]) {
1158 		if (in[0] >= '0' && in[0] <= '9') {
1159 			return ((char *)&numberstring[in[0]-'0']) + sizeof(memblock_t);
1160 		}
1161 	}
1162 	out = S_Malloc (strlen(in)+1);
1163 	strcpy (out, in);
1164 	return out;
1165 }
1166 
1167 /*
1168 ==============================================================================
1169 
1170 Goals:
1171 	reproducable without history effects -- no out of memory errors on weird map to map changes
1172 	allow restarting of the client without fragmentation
1173 	minimize total pages in use at run time
1174 	minimize total pages needed during load time
1175 
1176   Single block of memory with stack allocators coming from both ends towards the middle.
1177 
1178   One side is designated the temporary memory allocator.
1179 
1180   Temporary memory can be allocated and freed in any order.
1181 
1182   A highwater mark is kept of the most in use at any time.
1183 
1184   When there is no temporary memory allocated, the permanent and temp sides
1185   can be switched, allowing the already touched temp memory to be used for
1186   permanent storage.
1187 
1188   Temp memory must never be allocated on two ends at once, or fragmentation
1189   could occur.
1190 
1191   If we have any in-use temp memory, additional temp allocations must come from
1192   that side.
1193 
1194   If not, we can choose to make either side the new temp side and push future
1195   permanent allocations to the other side.  Permanent allocations should be
1196   kept on the side that has the current greatest wasted highwater mark.
1197 
1198 ==============================================================================
1199 */
1200 
1201 
1202 #define	HUNK_MAGIC	0x89537892
1203 #define	HUNK_FREE_MAGIC	0x89537893
1204 
1205 typedef struct {
1206 	int		magic;
1207 	int		size;
1208 } hunkHeader_t;
1209 
1210 typedef struct {
1211 	int		mark;
1212 	int		permanent;
1213 	int		temp;
1214 	int		tempHighwater;
1215 } hunkUsed_t;
1216 
1217 typedef struct hunkblock_s {
1218 	int size;
1219 	byte printed;
1220 	struct hunkblock_s *next;
1221 	char *label;
1222 	char *file;
1223 	int line;
1224 } hunkblock_t;
1225 
1226 static	hunkblock_t *hunkblocks;
1227 
1228 static	hunkUsed_t	hunk_low, hunk_high;
1229 static	hunkUsed_t	*hunk_permanent, *hunk_temp;
1230 
1231 static	byte	*s_hunkData = NULL;
1232 static	int		s_hunkTotal;
1233 
1234 static	int		s_zoneTotal;
1235 static	int		s_smallZoneTotal;
1236 
1237 
1238 /*
1239 =================
1240 Com_Meminfo_f
1241 =================
1242 */
1243 void Com_Meminfo_f( void ) {
1244 	memblock_t	*block;
1245 	int			zoneBytes, zoneBlocks;
1246 	int			smallZoneBytes, smallZoneBlocks;
1247 	int			botlibBytes, rendererBytes;
1248 	int			unused;
1249 
1250 	zoneBytes = 0;
1251 	botlibBytes = 0;
1252 	rendererBytes = 0;
1253 	zoneBlocks = 0;
1254 	for (block = mainzone->blocklist.next ; ; block = block->next) {
1255 		if ( Cmd_Argc() != 1 ) {
1256 			Com_Printf ("block:%p    size:%7i    tag:%3i\n",
1257 				(void *)block, block->size, block->tag);
1258 		}
1259 		if ( block->tag ) {
1260 			zoneBytes += block->size;
1261 			zoneBlocks++;
1262 			if ( block->tag == TAG_BOTLIB ) {
1263 				botlibBytes += block->size;
1264 			} else if ( block->tag == TAG_RENDERER ) {
1265 				rendererBytes += block->size;
1266 			}
1267 		}
1268 
1269 		if (block->next == &mainzone->blocklist) {
1270 			break;			// all blocks have been hit
1271 		}
1272 		if ( (byte *)block + block->size != (byte *)block->next) {
1273 			Com_Printf ("ERROR: block size does not touch the next block\n");
1274 		}
1275 		if ( block->next->prev != block) {
1276 			Com_Printf ("ERROR: next block doesn't have proper back link\n");
1277 		}
1278 		if ( !block->tag && !block->next->tag ) {
1279 			Com_Printf ("ERROR: two consecutive free blocks\n");
1280 		}
1281 	}
1282 
1283 	smallZoneBytes = 0;
1284 	smallZoneBlocks = 0;
1285 	for (block = smallzone->blocklist.next ; ; block = block->next) {
1286 		if ( block->tag ) {
1287 			smallZoneBytes += block->size;
1288 			smallZoneBlocks++;
1289 		}
1290 
1291 		if (block->next == &smallzone->blocklist) {
1292 			break;			// all blocks have been hit
1293 		}
1294 	}
1295 
1296 	Com_Printf( "%8i bytes total hunk\n", s_hunkTotal );
1297 	Com_Printf( "%8i bytes total zone\n", s_zoneTotal );
1298 	Com_Printf( "\n" );
1299 	Com_Printf( "%8i low mark\n", hunk_low.mark );
1300 	Com_Printf( "%8i low permanent\n", hunk_low.permanent );
1301 	if ( hunk_low.temp != hunk_low.permanent ) {
1302 		Com_Printf( "%8i low temp\n", hunk_low.temp );
1303 	}
1304 	Com_Printf( "%8i low tempHighwater\n", hunk_low.tempHighwater );
1305 	Com_Printf( "\n" );
1306 	Com_Printf( "%8i high mark\n", hunk_high.mark );
1307 	Com_Printf( "%8i high permanent\n", hunk_high.permanent );
1308 	if ( hunk_high.temp != hunk_high.permanent ) {
1309 		Com_Printf( "%8i high temp\n", hunk_high.temp );
1310 	}
1311 	Com_Printf( "%8i high tempHighwater\n", hunk_high.tempHighwater );
1312 	Com_Printf( "\n" );
1313 	Com_Printf( "%8i total hunk in use\n", hunk_low.permanent + hunk_high.permanent );
1314 	unused = 0;
1315 	if ( hunk_low.tempHighwater > hunk_low.permanent ) {
1316 		unused += hunk_low.tempHighwater - hunk_low.permanent;
1317 	}
1318 	if ( hunk_high.tempHighwater > hunk_high.permanent ) {
1319 		unused += hunk_high.tempHighwater - hunk_high.permanent;
1320 	}
1321 	Com_Printf( "%8i unused highwater\n", unused );
1322 	Com_Printf( "\n" );
1323 	Com_Printf( "%8i bytes in %i zone blocks\n", zoneBytes, zoneBlocks	);
1324 	Com_Printf( "        %8i bytes in dynamic botlib\n", botlibBytes );
1325 	Com_Printf( "        %8i bytes in dynamic renderer\n", rendererBytes );
1326 	Com_Printf( "        %8i bytes in dynamic other\n", zoneBytes - ( botlibBytes + rendererBytes ) );
1327 	Com_Printf( "        %8i bytes in small Zone memory\n", smallZoneBytes );
1328 }
1329 
1330 /*
1331 ===============
1332 Com_TouchMemory
1333 
1334 Touch all known used data to make sure it is paged in
1335 ===============
1336 */
1337 void Com_TouchMemory( void ) {
1338 	int		start, end;
1339 	int		i, j;
1340 	int		sum;
1341 	memblock_t	*block;
1342 
1343 	Z_CheckHeap();
1344 
1345 	start = Sys_Milliseconds();
1346 
1347 	sum = 0;
1348 
1349 	j = hunk_low.permanent >> 2;
1350 	for ( i = 0 ; i < j ; i+=64 ) {			// only need to touch each page
1351 		sum += ((int *)s_hunkData)[i];
1352 	}
1353 
1354 	i = ( s_hunkTotal - hunk_high.permanent ) >> 2;
1355 	j = hunk_high.permanent >> 2;
1356 	for (  ; i < j ; i+=64 ) {			// only need to touch each page
1357 		sum += ((int *)s_hunkData)[i];
1358 	}
1359 
1360 	for (block = mainzone->blocklist.next ; ; block = block->next) {
1361 		if ( block->tag ) {
1362 			j = block->size >> 2;
1363 			for ( i = 0 ; i < j ; i+=64 ) {				// only need to touch each page
1364 				sum += ((int *)block)[i];
1365 			}
1366 		}
1367 		if ( block->next == &mainzone->blocklist ) {
1368 			break;			// all blocks have been hit
1369 		}
1370 	}
1371 
1372 	end = Sys_Milliseconds();
1373 
1374 	Com_Printf( "Com_TouchMemory: %i msec\n", end - start );
1375 }
1376 
1377 
1378 
1379 /*
1380 =================
1381 Com_InitZoneMemory
1382 =================
1383 */
1384 void Com_InitSmallZoneMemory( void ) {
1385 	s_smallZoneTotal = 512 * 1024;
1386 	smallzone = calloc( s_smallZoneTotal, 1 );
1387 	if ( !smallzone ) {
1388 		Com_Error( ERR_FATAL, "Small zone data failed to allocate %1.1f megs", (float)s_smallZoneTotal / (1024*1024) );
1389 	}
1390 	Z_ClearZone( smallzone, s_smallZoneTotal );
1391 
1392 	return;
1393 }
1394 
1395 void Com_InitZoneMemory( void ) {
1396 	cvar_t	*cv;
1397 
1398 	//FIXME: 05/01/06 com_zoneMegs is useless right now as neither q3config.cfg nor
1399 	// Com_StartupVariable have been executed by this point. The net result is that
1400 	// s_zoneTotal will always be set to the default value.
1401 
1402 	// allocate the random block zone
1403 	cv = Cvar_Get( "com_zoneMegs", DEF_COMZONEMEGS_S, CVAR_LATCH | CVAR_ARCHIVE );
1404 
1405 	if ( cv->integer < DEF_COMZONEMEGS ) {
1406 		s_zoneTotal = 1024 * 1024 * DEF_COMZONEMEGS;
1407 	} else {
1408 		s_zoneTotal = cv->integer * 1024 * 1024;
1409 	}
1410 
1411 	mainzone = calloc( s_zoneTotal, 1 );
1412 	if ( !mainzone ) {
1413 		Com_Error( ERR_FATAL, "Zone data failed to allocate %i megs", s_zoneTotal / (1024*1024) );
1414 	}
1415 	Z_ClearZone( mainzone, s_zoneTotal );
1416 
1417 }
1418 
1419 /*
1420 =================
1421 Hunk_Log
1422 =================
1423 */
1424 void Hunk_Log( void) {
1425 	hunkblock_t	*block;
1426 	char		buf[4096];
1427 	int size, numBlocks;
1428 
1429 	if (!logfile || !FS_Initialized())
1430 		return;
1431 	size = 0;
1432 	numBlocks = 0;
1433 	Com_sprintf(buf, sizeof(buf), "\r\n================\r\nHunk log\r\n================\r\n");
1434 	FS_Write(buf, strlen(buf), logfile);
1435 	for (block = hunkblocks ; block; block = block->next) {
1436 #ifdef HUNK_DEBUG
1437 		Com_sprintf(buf, sizeof(buf), "size = %8d: %s, line: %d (%s)\r\n", block->size, block->file, block->line, block->label);
1438 		FS_Write(buf, strlen(buf), logfile);
1439 #endif
1440 		size += block->size;
1441 		numBlocks++;
1442 	}
1443 	Com_sprintf(buf, sizeof(buf), "%d Hunk memory\r\n", size);
1444 	FS_Write(buf, strlen(buf), logfile);
1445 	Com_sprintf(buf, sizeof(buf), "%d hunk blocks\r\n", numBlocks);
1446 	FS_Write(buf, strlen(buf), logfile);
1447 }
1448 
1449 /*
1450 =================
1451 Hunk_SmallLog
1452 =================
1453 */
1454 void Hunk_SmallLog( void) {
1455 	hunkblock_t	*block, *block2;
1456 	char		buf[4096];
1457 	int size, locsize, numBlocks;
1458 
1459 	if (!logfile || !FS_Initialized())
1460 		return;
1461 	for (block = hunkblocks ; block; block = block->next) {
1462 		block->printed = qfalse;
1463 	}
1464 	size = 0;
1465 	numBlocks = 0;
1466 	Com_sprintf(buf, sizeof(buf), "\r\n================\r\nHunk Small log\r\n================\r\n");
1467 	FS_Write(buf, strlen(buf), logfile);
1468 	for (block = hunkblocks; block; block = block->next) {
1469 		if (block->printed) {
1470 			continue;
1471 		}
1472 		locsize = block->size;
1473 		for (block2 = block->next; block2; block2 = block2->next) {
1474 			if (block->line != block2->line) {
1475 				continue;
1476 			}
1477 			if (Q_stricmp(block->file, block2->file)) {
1478 				continue;
1479 			}
1480 			size += block2->size;
1481 			locsize += block2->size;
1482 			block2->printed = qtrue;
1483 		}
1484 #ifdef HUNK_DEBUG
1485 		Com_sprintf(buf, sizeof(buf), "size = %8d: %s, line: %d (%s)\r\n", locsize, block->file, block->line, block->label);
1486 		FS_Write(buf, strlen(buf), logfile);
1487 #endif
1488 		size += block->size;
1489 		numBlocks++;
1490 	}
1491 	Com_sprintf(buf, sizeof(buf), "%d Hunk memory\r\n", size);
1492 	FS_Write(buf, strlen(buf), logfile);
1493 	Com_sprintf(buf, sizeof(buf), "%d hunk blocks\r\n", numBlocks);
1494 	FS_Write(buf, strlen(buf), logfile);
1495 }
1496 
1497 /*
1498 =================
1499 Com_InitZoneMemory
1500 =================
1501 */
1502 void Com_InitHunkMemory( void ) {
1503 	cvar_t	*cv;
1504 	int nMinAlloc;
1505 	char *pMsg = NULL;
1506 
1507 	// make sure the file system has allocated and "not" freed any temp blocks
1508 	// this allows the config and product id files ( journal files too ) to be loaded
1509 	// by the file system without redunant routines in the file system utilizing different
1510 	// memory systems
1511 	if (FS_LoadStack() != 0) {
1512 		Com_Error( ERR_FATAL, "Hunk initialization failed. File system load stack not zero");
1513 	}
1514 
1515 	// allocate the stack based hunk allocator
1516 	cv = Cvar_Get( "com_hunkMegs", DEF_COMHUNKMEGS_S, CVAR_LATCH | CVAR_ARCHIVE );
1517 
1518 	// if we are not dedicated min allocation is 56, otherwise min is 1
1519 	if (com_dedicated && com_dedicated->integer) {
1520 		nMinAlloc = MIN_DEDICATED_COMHUNKMEGS;
1521 		pMsg = "Minimum com_hunkMegs for a dedicated server is %i, allocating %i megs.\n";
1522 	}
1523 	else {
1524 		nMinAlloc = MIN_COMHUNKMEGS;
1525 		pMsg = "Minimum com_hunkMegs is %i, allocating %i megs.\n";
1526 	}
1527 
1528 	if ( cv->integer < nMinAlloc ) {
1529 		s_hunkTotal = 1024 * 1024 * nMinAlloc;
1530 	    Com_Printf(pMsg, nMinAlloc, s_hunkTotal / (1024 * 1024));
1531 	} else {
1532 		s_hunkTotal = cv->integer * 1024 * 1024;
1533 	}
1534 
1535 	s_hunkData = calloc( s_hunkTotal + 31, 1 );
1536 	if ( !s_hunkData ) {
1537 		Com_Error( ERR_FATAL, "Hunk data failed to allocate %i megs", s_hunkTotal / (1024*1024) );
1538 	}
1539 	// cacheline align
1540 	s_hunkData = (byte *) ( ( (intptr_t)s_hunkData + 31 ) & ~31 );
1541 	Hunk_Clear();
1542 
1543 	Cmd_AddCommand( "meminfo", Com_Meminfo_f );
1544 #ifdef ZONE_DEBUG
1545 	Cmd_AddCommand( "zonelog", Z_LogHeap );
1546 #endif
1547 #ifdef HUNK_DEBUG
1548 	Cmd_AddCommand( "hunklog", Hunk_Log );
1549 	Cmd_AddCommand( "hunksmalllog", Hunk_SmallLog );
1550 #endif
1551 }
1552 
1553 /*
1554 ====================
1555 Hunk_MemoryRemaining
1556 ====================
1557 */
1558 int	Hunk_MemoryRemaining( void ) {
1559 	int		low, high;
1560 
1561 	low = hunk_low.permanent > hunk_low.temp ? hunk_low.permanent : hunk_low.temp;
1562 	high = hunk_high.permanent > hunk_high.temp ? hunk_high.permanent : hunk_high.temp;
1563 
1564 	return s_hunkTotal - ( low + high );
1565 }
1566 
1567 /*
1568 ===================
1569 Hunk_SetMark
1570 
1571 The server calls this after the level and game VM have been loaded
1572 ===================
1573 */
1574 void Hunk_SetMark( void ) {
1575 	hunk_low.mark = hunk_low.permanent;
1576 	hunk_high.mark = hunk_high.permanent;
1577 }
1578 
1579 /*
1580 =================
1581 Hunk_ClearToMark
1582 
1583 The client calls this before starting a vid_restart or snd_restart
1584 =================
1585 */
1586 void Hunk_ClearToMark( void ) {
1587 	hunk_low.permanent = hunk_low.temp = hunk_low.mark;
1588 	hunk_high.permanent = hunk_high.temp = hunk_high.mark;
1589 }
1590 
1591 /*
1592 =================
1593 Hunk_CheckMark
1594 =================
1595 */
1596 qboolean Hunk_CheckMark( void ) {
1597 	if( hunk_low.mark || hunk_high.mark ) {
1598 		return qtrue;
1599 	}
1600 	return qfalse;
1601 }
1602 
1603 void CL_ShutdownCGame( void );
1604 void CL_ShutdownUI( void );
1605 void SV_ShutdownGameProgs( void );
1606 
1607 /*
1608 =================
1609 Hunk_Clear
1610 
1611 The server calls this before shutting down or loading a new map
1612 =================
1613 */
1614 void Hunk_Clear( void ) {
1615 
1616 #ifndef DEDICATED
1617 	CL_ShutdownCGame();
1618 	CL_ShutdownUI();
1619 #endif
1620 	SV_ShutdownGameProgs();
1621 #ifndef DEDICATED
1622 	CIN_CloseAllVideos();
1623 #endif
1624 	hunk_low.mark = 0;
1625 	hunk_low.permanent = 0;
1626 	hunk_low.temp = 0;
1627 	hunk_low.tempHighwater = 0;
1628 
1629 	hunk_high.mark = 0;
1630 	hunk_high.permanent = 0;
1631 	hunk_high.temp = 0;
1632 	hunk_high.tempHighwater = 0;
1633 
1634 	hunk_permanent = &hunk_low;
1635 	hunk_temp = &hunk_high;
1636 
1637 	Com_Printf( "Hunk_Clear: reset the hunk ok\n" );
1638 	VM_Clear();
1639 #ifdef HUNK_DEBUG
1640 	hunkblocks = NULL;
1641 #endif
1642 }
1643 
1644 static void Hunk_SwapBanks( void ) {
1645 	hunkUsed_t	*swap;
1646 
1647 	// can't swap banks if there is any temp already allocated
1648 	if ( hunk_temp->temp != hunk_temp->permanent ) {
1649 		return;
1650 	}
1651 
1652 	// if we have a larger highwater mark on this side, start making
1653 	// our permanent allocations here and use the other side for temp
1654 	if ( hunk_temp->tempHighwater - hunk_temp->permanent >
1655 		hunk_permanent->tempHighwater - hunk_permanent->permanent ) {
1656 		swap = hunk_temp;
1657 		hunk_temp = hunk_permanent;
1658 		hunk_permanent = swap;
1659 	}
1660 }
1661 
1662 /*
1663 =================
1664 Hunk_Alloc
1665 
1666 Allocate permanent (until the hunk is cleared) memory
1667 =================
1668 */
1669 #ifdef HUNK_DEBUG
1670 void *Hunk_AllocDebug( int size, ha_pref preference, char *label, char *file, int line ) {
1671 #else
1672 void *Hunk_Alloc( int size, ha_pref preference ) {
1673 #endif
1674 	void	*buf;
1675 
1676 	if ( s_hunkData == NULL)
1677 	{
1678 		Com_Error( ERR_FATAL, "Hunk_Alloc: Hunk memory system not initialized" );
1679 	}
1680 
1681 	// can't do preference if there is any temp allocated
1682 	if (preference == h_dontcare || hunk_temp->temp != hunk_temp->permanent) {
1683 		Hunk_SwapBanks();
1684 	} else {
1685 		if (preference == h_low && hunk_permanent != &hunk_low) {
1686 			Hunk_SwapBanks();
1687 		} else if (preference == h_high && hunk_permanent != &hunk_high) {
1688 			Hunk_SwapBanks();
1689 		}
1690 	}
1691 
1692 #ifdef HUNK_DEBUG
1693 	size += sizeof(hunkblock_t);
1694 #endif
1695 
1696 	// round to cacheline
1697 	size = (size+31)&~31;
1698 
1699 	if ( hunk_low.temp + hunk_high.temp + size > s_hunkTotal ) {
1700 #ifdef HUNK_DEBUG
1701 		Hunk_Log();
1702 		Hunk_SmallLog();
1703 #endif
1704 		Com_Error( ERR_DROP, "Hunk_Alloc failed on %i", size );
1705 	}
1706 
1707 	if ( hunk_permanent == &hunk_low ) {
1708 		buf = (void *)(s_hunkData + hunk_permanent->permanent);
1709 		hunk_permanent->permanent += size;
1710 	} else {
1711 		hunk_permanent->permanent += size;
1712 		buf = (void *)(s_hunkData + s_hunkTotal - hunk_permanent->permanent );
1713 	}
1714 
1715 	hunk_permanent->temp = hunk_permanent->permanent;
1716 
1717 	Com_Memset( buf, 0, size );
1718 
1719 #ifdef HUNK_DEBUG
1720 	{
1721 		hunkblock_t *block;
1722 
1723 		block = (hunkblock_t *) buf;
1724 		block->size = size - sizeof(hunkblock_t);
1725 		block->file = file;
1726 		block->label = label;
1727 		block->line = line;
1728 		block->next = hunkblocks;
1729 		hunkblocks = block;
1730 		buf = ((byte *) buf) + sizeof(hunkblock_t);
1731 	}
1732 #endif
1733 	return buf;
1734 }
1735 
1736 /*
1737 =================
1738 Hunk_AllocateTempMemory
1739 
1740 This is used by the file loading system.
1741 Multiple files can be loaded in temporary memory.
1742 When the files-in-use count reaches zero, all temp memory will be deleted
1743 =================
1744 */
1745 void *Hunk_AllocateTempMemory( int size ) {
1746 	void		*buf;
1747 	hunkHeader_t	*hdr;
1748 
1749 	// return a Z_Malloc'd block if the hunk has not been initialized
1750 	// this allows the config and product id files ( journal files too ) to be loaded
1751 	// by the file system without redunant routines in the file system utilizing different
1752 	// memory systems
1753 	if ( s_hunkData == NULL )
1754 	{
1755 		return Z_Malloc(size);
1756 	}
1757 
1758 	Hunk_SwapBanks();
1759 
1760 	size = PAD(size, sizeof(intptr_t)) + sizeof( hunkHeader_t );
1761 
1762 	if ( hunk_temp->temp + hunk_permanent->permanent + size > s_hunkTotal ) {
1763 		Com_Error( ERR_DROP, "Hunk_AllocateTempMemory: failed on %i", size );
1764 	}
1765 
1766 	if ( hunk_temp == &hunk_low ) {
1767 		buf = (void *)(s_hunkData + hunk_temp->temp);
1768 		hunk_temp->temp += size;
1769 	} else {
1770 		hunk_temp->temp += size;
1771 		buf = (void *)(s_hunkData + s_hunkTotal - hunk_temp->temp );
1772 	}
1773 
1774 	if ( hunk_temp->temp > hunk_temp->tempHighwater ) {
1775 		hunk_temp->tempHighwater = hunk_temp->temp;
1776 	}
1777 
1778 	hdr = (hunkHeader_t *)buf;
1779 	buf = (void *)(hdr+1);
1780 
1781 	hdr->magic = HUNK_MAGIC;
1782 	hdr->size = size;
1783 
1784 	// don't bother clearing, because we are going to load a file over it
1785 	return buf;
1786 }
1787 
1788 
1789 /*
1790 ==================
1791 Hunk_FreeTempMemory
1792 ==================
1793 */
1794 void Hunk_FreeTempMemory( void *buf ) {
1795 	hunkHeader_t	*hdr;
1796 
1797 	  // free with Z_Free if the hunk has not been initialized
1798 	  // this allows the config and product id files ( journal files too ) to be loaded
1799 	  // by the file system without redunant routines in the file system utilizing different
1800 	  // memory systems
1801 	if ( s_hunkData == NULL )
1802 	{
1803 		Z_Free(buf);
1804 		return;
1805 	}
1806 
1807 
1808 	hdr = ( (hunkHeader_t *)buf ) - 1;
1809 	if ( hdr->magic != HUNK_MAGIC ) {
1810 		Com_Error( ERR_FATAL, "Hunk_FreeTempMemory: bad magic" );
1811 	}
1812 
1813 	hdr->magic = HUNK_FREE_MAGIC;
1814 
1815 	// this only works if the files are freed in stack order,
1816 	// otherwise the memory will stay around until Hunk_ClearTempMemory
1817 	if ( hunk_temp == &hunk_low ) {
1818 		if ( hdr == (void *)(s_hunkData + hunk_temp->temp - hdr->size ) ) {
1819 			hunk_temp->temp -= hdr->size;
1820 		} else {
1821 			Com_Printf( "Hunk_FreeTempMemory: not the final block\n" );
1822 		}
1823 	} else {
1824 		if ( hdr == (void *)(s_hunkData + s_hunkTotal - hunk_temp->temp ) ) {
1825 			hunk_temp->temp -= hdr->size;
1826 		} else {
1827 			Com_Printf( "Hunk_FreeTempMemory: not the final block\n" );
1828 		}
1829 	}
1830 }
1831 
1832 
1833 /*
1834 =================
1835 Hunk_ClearTempMemory
1836 
1837 The temp space is no longer needed.  If we have left more
1838 touched but unused memory on this side, have future
1839 permanent allocs use this side.
1840 =================
1841 */
1842 void Hunk_ClearTempMemory( void ) {
1843 	if ( s_hunkData != NULL ) {
1844 		hunk_temp->temp = hunk_temp->permanent;
1845 	}
1846 }
1847 
1848 /*
1849 =================
1850 Hunk_Trash
1851 =================
1852 */
1853 void Hunk_Trash( void ) {
1854 	int length, i, rnd;
1855 	char *buf, value;
1856 
1857 	return;
1858 
1859 	if ( s_hunkData == NULL )
1860 		return;
1861 
1862 #ifdef _DEBUG
1863 	Com_Error(ERR_DROP, "hunk trashed\n");
1864 	return;
1865 #endif
1866 
1867 	Cvar_Set("com_jp", "1");
1868 	Hunk_SwapBanks();
1869 
1870 	if ( hunk_permanent == &hunk_low ) {
1871 		buf = (void *)(s_hunkData + hunk_permanent->permanent);
1872 	} else {
1873 		buf = (void *)(s_hunkData + s_hunkTotal - hunk_permanent->permanent );
1874 	}
1875 	length = hunk_permanent->permanent;
1876 
1877 	if (length > 0x7FFFF) {
1878 		//randomly trash data within buf
1879 		rnd = random() * (length - 0x7FFFF);
1880 		value = 31;
1881 		for (i = 0; i < 0x7FFFF; i++) {
1882 			value *= 109;
1883 			buf[rnd+i] ^= value;
1884 		}
1885 	}
1886 }
1887 
1888 /*
1889 ===================================================================
1890 
1891 EVENTS AND JOURNALING
1892 
1893 In addition to these events, .cfg files are also copied to the
1894 journaled file
1895 ===================================================================
1896 */
1897 
1898 #define	MAX_PUSHED_EVENTS	            1024
1899 static int com_pushedEventsHead = 0;
1900 static int com_pushedEventsTail = 0;
1901 static sysEvent_t	com_pushedEvents[MAX_PUSHED_EVENTS];
1902 
1903 /*
1904 =================
1905 Com_InitJournaling
1906 =================
1907 */
1908 void Com_InitJournaling( void ) {
1909 	Com_StartupVariable( "journal" );
1910 	com_journal = Cvar_Get ("journal", "0", CVAR_INIT);
1911 	if ( !com_journal->integer ) {
1912 		return;
1913 	}
1914 
1915 	if ( com_journal->integer == 1 ) {
1916 		Com_Printf( "Journaling events\n");
1917 		com_journalFile = FS_FOpenFileWrite( "journal.dat" );
1918 		com_journalDataFile = FS_FOpenFileWrite( "journaldata.dat" );
1919 	} else if ( com_journal->integer == 2 ) {
1920 		Com_Printf( "Replaying journaled events\n");
1921 		FS_FOpenFileRead( "journal.dat", &com_journalFile, qtrue );
1922 		FS_FOpenFileRead( "journaldata.dat", &com_journalDataFile, qtrue );
1923 	}
1924 
1925 	if ( !com_journalFile || !com_journalDataFile ) {
1926 		Cvar_Set( "com_journal", "0" );
1927 		com_journalFile = 0;
1928 		com_journalDataFile = 0;
1929 		Com_Printf( "Couldn't open journal files\n" );
1930 	}
1931 }
1932 
1933 /*
1934 ========================================================================
1935 
1936 EVENT LOOP
1937 
1938 ========================================================================
1939 */
1940 
1941 #define MAX_QUEUED_EVENTS  256
1942 #define MASK_QUEUED_EVENTS ( MAX_QUEUED_EVENTS - 1 )
1943 
1944 static sysEvent_t  eventQueue[ MAX_QUEUED_EVENTS ];
1945 static int         eventHead = 0;
1946 static int         eventTail = 0;
1947 static byte        sys_packetReceived[ MAX_MSGLEN ];
1948 
1949 /*
1950 ================
1951 Com_QueueEvent
1952 
1953 A time of 0 will get the current time
1954 Ptr should either be null, or point to a block of data that can
1955 be freed by the game later.
1956 ================
1957 */
1958 void Com_QueueEvent( int time, sysEventType_t type, int value, int value2, int ptrLength, void *ptr )
1959 {
1960 	sysEvent_t  *ev;
1961 
1962 	ev = &eventQueue[ eventHead & MASK_QUEUED_EVENTS ];
1963 
1964 	if ( eventHead - eventTail >= MAX_QUEUED_EVENTS )
1965 	{
1966 		Com_Printf("Com_QueueEvent: overflow\n");
1967 		// we are discarding an event, but don't leak memory
1968 		if ( ev->evPtr )
1969 		{
1970 			Z_Free( ev->evPtr );
1971 		}
1972 		eventTail++;
1973 	}
1974 
1975 	eventHead++;
1976 
1977 	if ( time == 0 )
1978 	{
1979 		time = Sys_Milliseconds();
1980 	}
1981 
1982 	ev->evTime = time;
1983 	ev->evType = type;
1984 	ev->evValue = value;
1985 	ev->evValue2 = value2;
1986 	ev->evPtrLength = ptrLength;
1987 	ev->evPtr = ptr;
1988 }
1989 
1990 /*
1991 ================
1992 Com_GetSystemEvent
1993 
1994 ================
1995 */
1996 sysEvent_t Com_GetSystemEvent( void )
1997 {
1998 	sysEvent_t  ev;
1999 	char        *s;
2000 	msg_t       netmsg;
2001 	netadr_t    adr;
2002 
2003 	// return if we have data
2004 	if ( eventHead > eventTail )
2005 	{
2006 		eventTail++;
2007 		return eventQueue[ ( eventTail - 1 ) & MASK_QUEUED_EVENTS ];
2008 	}
2009 
2010 	// check for console commands
2011 	s = Sys_ConsoleInput();
2012 	if ( s )
2013 	{
2014 		char  *b;
2015 		int   len;
2016 
2017 		len = strlen( s ) + 1;
2018 		b = Z_Malloc( len );
2019 		strcpy( b, s );
2020 		Com_QueueEvent( 0, SE_CONSOLE, 0, 0, len, b );
2021 	}
2022 
2023 	// check for network packets
2024 	MSG_Init( &netmsg, sys_packetReceived, sizeof( sys_packetReceived ) );
2025 	if ( Sys_GetPacket ( &adr, &netmsg ) )
2026 	{
2027 		netadr_t  *buf;
2028 		int       len;
2029 
2030 		// copy out to a seperate buffer for qeueing
2031 		len = sizeof( netadr_t ) + netmsg.cursize;
2032 		buf = Z_Malloc( len );
2033 		*buf = adr;
2034 		memcpy( buf+1, netmsg.data, netmsg.cursize );
2035 		Com_QueueEvent( 0, SE_PACKET, 0, 0, len, buf );
2036 	}
2037 
2038 	// return if we have data
2039 	if ( eventHead > eventTail )
2040 	{
2041 		eventTail++;
2042 		return eventQueue[ ( eventTail - 1 ) & MASK_QUEUED_EVENTS ];
2043 	}
2044 
2045 	// create an empty event to return
2046 	memset( &ev, 0, sizeof( ev ) );
2047 	ev.evTime = Sys_Milliseconds();
2048 
2049 	return ev;
2050 }
2051 
2052 /*
2053 =================
2054 Com_GetRealEvent
2055 =================
2056 */
2057 sysEvent_t	Com_GetRealEvent( void ) {
2058 	int			r;
2059 	sysEvent_t	ev;
2060 
2061 	// either get an event from the system or the journal file
2062 	if ( com_journal->integer == 2 ) {
2063 		r = FS_Read( &ev, sizeof(ev), com_journalFile );
2064 		if ( r != sizeof(ev) ) {
2065 			Com_Error( ERR_FATAL, "Error reading from journal file" );
2066 		}
2067 		if ( ev.evPtrLength ) {
2068 			ev.evPtr = Z_Malloc( ev.evPtrLength );
2069 			r = FS_Read( ev.evPtr, ev.evPtrLength, com_journalFile );
2070 			if ( r != ev.evPtrLength ) {
2071 				Com_Error( ERR_FATAL, "Error reading from journal file" );
2072 			}
2073 		}
2074 	} else {
2075 		ev = Com_GetSystemEvent();
2076 
2077 		// write the journal value out if needed
2078 		if ( com_journal->integer == 1 ) {
2079 			r = FS_Write( &ev, sizeof(ev), com_journalFile );
2080 			if ( r != sizeof(ev) ) {
2081 				Com_Error( ERR_FATAL, "Error writing to journal file" );
2082 			}
2083 			if ( ev.evPtrLength ) {
2084 				r = FS_Write( ev.evPtr, ev.evPtrLength, com_journalFile );
2085 				if ( r != ev.evPtrLength ) {
2086 					Com_Error( ERR_FATAL, "Error writing to journal file" );
2087 				}
2088 			}
2089 		}
2090 	}
2091 
2092 	return ev;
2093 }
2094 
2095 
2096 /*
2097 =================
2098 Com_InitPushEvent
2099 =================
2100 */
2101 void Com_InitPushEvent( void ) {
2102   // clear the static buffer array
2103   // this requires SE_NONE to be accepted as a valid but NOP event
2104   memset( com_pushedEvents, 0, sizeof(com_pushedEvents) );
2105   // reset counters while we are at it
2106   // beware: GetEvent might still return an SE_NONE from the buffer
2107   com_pushedEventsHead = 0;
2108   com_pushedEventsTail = 0;
2109 }
2110 
2111 
2112 /*
2113 =================
2114 Com_PushEvent
2115 =================
2116 */
2117 void Com_PushEvent( sysEvent_t *event ) {
2118 	sysEvent_t		*ev;
2119 	static int printedWarning = 0;
2120 
2121 	ev = &com_pushedEvents[ com_pushedEventsHead & (MAX_PUSHED_EVENTS-1) ];
2122 
2123 	if ( com_pushedEventsHead - com_pushedEventsTail >= MAX_PUSHED_EVENTS ) {
2124 
2125 		// don't print the warning constantly, or it can give time for more...
2126 		if ( !printedWarning ) {
2127 			printedWarning = qtrue;
2128 			Com_Printf( "WARNING: Com_PushEvent overflow\n" );
2129 		}
2130 
2131 		if ( ev->evPtr ) {
2132 			Z_Free( ev->evPtr );
2133 		}
2134 		com_pushedEventsTail++;
2135 	} else {
2136 		printedWarning = qfalse;
2137 	}
2138 
2139 	*ev = *event;
2140 	com_pushedEventsHead++;
2141 }
2142 
2143 /*
2144 =================
2145 Com_GetEvent
2146 =================
2147 */
2148 sysEvent_t	Com_GetEvent( void ) {
2149 	if ( com_pushedEventsHead > com_pushedEventsTail ) {
2150 		com_pushedEventsTail++;
2151 		return com_pushedEvents[ (com_pushedEventsTail-1) & (MAX_PUSHED_EVENTS-1) ];
2152 	}
2153 	return Com_GetRealEvent();
2154 }
2155 
2156 /*
2157 =================
2158 Com_RunAndTimeServerPacket
2159 =================
2160 */
2161 void Com_RunAndTimeServerPacket( netadr_t *evFrom, msg_t *buf ) {
2162 	int		t1, t2, msec;
2163 
2164 	t1 = 0;
2165 
2166 	if ( com_speeds->integer ) {
2167 		t1 = Sys_Milliseconds ();
2168 	}
2169 
2170 	SV_PacketEvent( *evFrom, buf );
2171 
2172 	if ( com_speeds->integer ) {
2173 		t2 = Sys_Milliseconds ();
2174 		msec = t2 - t1;
2175 		if ( com_speeds->integer == 3 ) {
2176 			Com_Printf( "SV_PacketEvent time: %i\n", msec );
2177 		}
2178 	}
2179 }
2180 
2181 /*
2182 =================
2183 Com_EventLoop
2184 
2185 Returns last event time
2186 =================
2187 */
2188 int Com_EventLoop( void ) {
2189 	sysEvent_t	ev;
2190 	netadr_t	evFrom;
2191 	byte		bufData[MAX_MSGLEN];
2192 	msg_t		buf;
2193 
2194 	MSG_Init( &buf, bufData, sizeof( bufData ) );
2195 
2196 	while ( 1 ) {
2197 		NET_FlushPacketQueue();
2198 		ev = Com_GetEvent();
2199 
2200 		// if no more events are available
2201 		if ( ev.evType == SE_NONE ) {
2202 			// manually send packet events for the loopback channel
2203 			while ( NET_GetLoopPacket( NS_CLIENT, &evFrom, &buf ) ) {
2204 				CL_PacketEvent( evFrom, &buf );
2205 			}
2206 
2207 			while ( NET_GetLoopPacket( NS_SERVER, &evFrom, &buf ) ) {
2208 				// if the server just shut down, flush the events
2209 				if ( com_sv_running->integer ) {
2210 					Com_RunAndTimeServerPacket( &evFrom, &buf );
2211 				}
2212 			}
2213 
2214 			return ev.evTime;
2215 		}
2216 
2217 
2218 		switch ( ev.evType ) {
2219 		default:
2220 			Com_Error( ERR_FATAL, "Com_EventLoop: bad event type %i", ev.evType );
2221 			break;
2222         case SE_NONE:
2223             break;
2224 		case SE_KEY:
2225 			CL_KeyEvent( ev.evValue, ev.evValue2, ev.evTime );
2226 			break;
2227 		case SE_CHAR:
2228 			CL_CharEvent( ev.evValue );
2229 			break;
2230 		case SE_MOUSE:
2231 			CL_MouseEvent( ev.evValue, ev.evValue2, ev.evTime );
2232 			break;
2233 		case SE_JOYSTICK_AXIS:
2234 			CL_JoystickEvent( ev.evValue, ev.evValue2, ev.evTime );
2235 			break;
2236 		case SE_CONSOLE:
2237 			Cbuf_AddText( (char *)ev.evPtr );
2238 			Cbuf_AddText( "\n" );
2239 			break;
2240 		case SE_PACKET:
2241 			// this cvar allows simulation of connections that
2242 			// drop a lot of packets.  Note that loopback connections
2243 			// don't go through here at all.
2244 			if ( com_dropsim->value > 0 ) {
2245 				static int seed;
2246 
2247 				if ( Q_random( &seed ) < com_dropsim->value ) {
2248 					break;		// drop this packet
2249 				}
2250 			}
2251 
2252 			evFrom = *(netadr_t *)ev.evPtr;
2253 			buf.cursize = ev.evPtrLength - sizeof( evFrom );
2254 
2255 			// we must copy the contents of the message out, because
2256 			// the event buffers are only large enough to hold the
2257 			// exact payload, but channel messages need to be large
2258 			// enough to hold fragment reassembly
2259 			if ( (unsigned)buf.cursize > buf.maxsize ) {
2260 				Com_Printf("Com_EventLoop: oversize packet\n");
2261 				continue;
2262 			}
2263 			Com_Memcpy( buf.data, (byte *)((netadr_t *)ev.evPtr + 1), buf.cursize );
2264 			if ( com_sv_running->integer ) {
2265 				Com_RunAndTimeServerPacket( &evFrom, &buf );
2266 			} else {
2267 				CL_PacketEvent( evFrom, &buf );
2268 			}
2269 			break;
2270 		}
2271 
2272 		// free any block data
2273 		if ( ev.evPtr ) {
2274 			Z_Free( ev.evPtr );
2275 		}
2276 	}
2277 
2278 	return 0;	// never reached
2279 }
2280 
2281 /*
2282 ================
2283 Com_Milliseconds
2284 
2285 Can be used for profiling, but will be journaled accurately
2286 ================
2287 */
2288 int Com_Milliseconds (void) {
2289 	sysEvent_t	ev;
2290 
2291 	// get events and push them until we get a null event with the current time
2292 	do {
2293 
2294 		ev = Com_GetRealEvent();
2295 		if ( ev.evType != SE_NONE ) {
2296 			Com_PushEvent( &ev );
2297 		}
2298 	} while ( ev.evType != SE_NONE );
2299 
2300 	return ev.evTime;
2301 }
2302 
2303 //============================================================================
2304 
2305 /*
2306 =============
2307 Com_Error_f
2308 
2309 Just throw a fatal error to
2310 test error shutdown procedures
2311 =============
2312 */
2313 static void Com_Error_f (void) {
2314 	if ( Cmd_Argc() > 1 ) {
2315 		Com_Error( ERR_DROP, "Testing drop error" );
2316 	} else {
2317 		Com_Error( ERR_FATAL, "Testing fatal error" );
2318 	}
2319 }
2320 
2321 
2322 /*
2323 =============
2324 Com_Freeze_f
2325 
2326 Just freeze in place for a given number of seconds to test
2327 error recovery
2328 =============
2329 */
2330 static void Com_Freeze_f (void) {
2331 	float	s;
2332 	int		start, now;
2333 
2334 	if ( Cmd_Argc() != 2 ) {
2335 		Com_Printf( "freeze <seconds>\n" );
2336 		return;
2337 	}
2338 	s = atof( Cmd_Argv(1) );
2339 
2340 	start = Com_Milliseconds();
2341 
2342 	while ( 1 ) {
2343 		now = Com_Milliseconds();
2344 		if ( ( now - start ) * 0.001 > s ) {
2345 			break;
2346 		}
2347 	}
2348 }
2349 
2350 /*
2351 =================
2352 Com_Crash_f
2353 
2354 A way to force a bus error for development reasons
2355 =================
2356 */
2357 static void Com_Crash_f( void ) {
2358 	* ( int * ) 0 = 0x12345678;
2359 }
2360 
2361 #ifndef STANDALONE
2362 
2363 // TTimo: centralizing the cl_cdkey stuff after I discovered a buffer overflow problem with the dedicated server version
2364 //   not sure it's necessary to have different defaults for regular and dedicated, but I don't want to risk it
2365 //   https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=470
2366 #ifndef DEDICATED
2367 char	cl_cdkey[34] = "                                ";
2368 #else
2369 char	cl_cdkey[34] = "123456789";
2370 #endif
2371 
2372 /*
2373 =================
2374 Com_ReadCDKey
2375 =================
2376 */
2377 qboolean CL_CDKeyValidate( const char *key, const char *checksum );
2378 void Com_ReadCDKey( const char *filename ) {
2379 	fileHandle_t	f;
2380 	char			buffer[33];
2381 	char			fbuffer[MAX_OSPATH];
2382 
2383 	sprintf(fbuffer, "%s/q3key", filename);
2384 
2385 	FS_SV_FOpenFileRead( fbuffer, &f );
2386 	if ( !f ) {
2387 		Q_strncpyz( cl_cdkey, "                ", 17 );
2388 		return;
2389 	}
2390 
2391 	Com_Memset( buffer, 0, sizeof(buffer) );
2392 
2393 	FS_Read( buffer, 16, f );
2394 	FS_FCloseFile( f );
2395 
2396 	if (CL_CDKeyValidate(buffer, NULL)) {
2397 		Q_strncpyz( cl_cdkey, buffer, 17 );
2398 	} else {
2399 		Q_strncpyz( cl_cdkey, "                ", 17 );
2400 	}
2401 }
2402 
2403 /*
2404 =================
2405 Com_AppendCDKey
2406 =================
2407 */
2408 void Com_AppendCDKey( const char *filename ) {
2409 	fileHandle_t	f;
2410 	char			buffer[33];
2411 	char			fbuffer[MAX_OSPATH];
2412 
2413 	sprintf(fbuffer, "%s/q3key", filename);
2414 
2415 	FS_SV_FOpenFileRead( fbuffer, &f );
2416 	if (!f) {
2417 		Q_strncpyz( &cl_cdkey[16], "                ", 17 );
2418 		return;
2419 	}
2420 
2421 	Com_Memset( buffer, 0, sizeof(buffer) );
2422 
2423 	FS_Read( buffer, 16, f );
2424 	FS_FCloseFile( f );
2425 
2426 	if (CL_CDKeyValidate(buffer, NULL)) {
2427 		strcat( &cl_cdkey[16], buffer );
2428 	} else {
2429 		Q_strncpyz( &cl_cdkey[16], "                ", 17 );
2430 	}
2431 }
2432 
2433 #ifndef DEDICATED
2434 /*
2435 =================
2436 Com_WriteCDKey
2437 =================
2438 */
2439 /* ... nonCDKEY
2440 static void Com_WriteCDKey( const char *filename, const char *ikey ) {
2441 	fileHandle_t	f;
2442 	char			fbuffer[MAX_OSPATH];
2443 	char			key[17];
2444 #ifndef _WIN32
2445 	mode_t			savedumask;
2446 #endif
2447 
2448 
2449 	sprintf(fbuffer, "%s/q3key", filename);
2450 
2451 
2452 	Q_strncpyz( key, ikey, 17 );
2453 
2454 	if(!CL_CDKeyValidate(key, NULL) ) {
2455 		return;
2456 	}
2457 
2458 #ifndef _WIN32
2459 	savedumask = umask(0077);
2460 #endif
2461 	f = FS_SV_FOpenFileWrite( fbuffer );
2462 	if ( !f ) {
2463 		Com_Printf ("Couldn't write CD key to %s.\n", fbuffer );
2464 		goto out;
2465 	}
2466 
2467 	FS_Write( key, 16, f );
2468 
2469 	FS_Printf( f, "\n// generated by quake, do not modify\r\n" );
2470 	FS_Printf( f, "// Do not give this file to ANYONE.\r\n" );
2471 	FS_Printf( f, "// id Software and Activision will NOT ask you to send this file to them.\r\n");
2472 
2473 	FS_FCloseFile( f );
2474 out:
2475 #ifndef _WIN32
2476 	umask(savedumask);
2477 #endif
2478 	return;
2479 }
2480 */
2481 #endif
2482 
2483 #endif // STANDALONE
2484 
2485 static void Com_DetectAltivec(void)
2486 {
2487 	// Only detect if user hasn't forcibly disabled it.
2488 	if (com_altivec->integer) {
2489 		static qboolean altivec = qfalse;
2490 		static qboolean detected = qfalse;
2491 		if (!detected) {
2492 			altivec = ( Sys_GetProcessorFeatures( ) & CF_ALTIVEC );
2493 			detected = qtrue;
2494 		}
2495 
2496 		if (!altivec) {
2497 			Cvar_Set( "com_altivec", "0" );  // we don't have it! Disable support!
2498 		}
2499 	}
2500 }
2501 
2502 
2503 /*
2504 =================
2505 Com_Init
2506 =================
2507 */
2508 void Com_Init( char *commandLine ) {
2509 	char	*s;
2510 
2511 	Com_Printf( "%s %s %s\n", VERSION_INFO, PLATFORM_STRING, __DATE__ );
2512 
2513 	if ( setjmp (abortframe) ) {
2514 		Sys_Error ("Error during initialization");
2515 	}
2516 
2517 	// Clear queues
2518 	Com_Memset( &eventQueue[ 0 ], 0, MAX_QUEUED_EVENTS * sizeof( sysEvent_t ) );
2519 	Com_Memset( &sys_packetReceived[ 0 ], 0, MAX_MSGLEN * sizeof( byte ) );
2520 
2521   // do this before anything else decides to push events
2522   Com_InitPushEvent();
2523 
2524 	Com_InitSmallZoneMemory();
2525 	Cvar_Init ();
2526 
2527 	win_multiUser = Cvar_Get("win_multiUser","0",0);// CVAR_ARCHIVE
2528 
2529 	// prepare enough of the subsystems to handle
2530 	// cvar and command buffer management
2531 	Com_ParseCommandLine( commandLine );
2532 
2533 //	Swap_Init ();
2534 	Cbuf_Init ();
2535 
2536 	Com_InitZoneMemory();
2537 	Cmd_Init ();
2538 
2539 	// override anything from the config files with command line args
2540 	Com_StartupVariable( NULL );
2541 
2542 	// get the developer cvar set as early as possible
2543 	Com_StartupVariable( "developer" );
2544 
2545 	// done early so bind command exists
2546 	CL_InitKeyCommands();
2547 
2548 	FS_InitFilesystem ();
2549 
2550 	Com_InitJournaling();
2551 
2552 	Cbuf_AddText ("exec default.cfg\n");
2553 
2554 	// skip the q3config.cfg if "safe" is on the command line
2555 	if ( !Com_SafeMode() ) {
2556 		Cbuf_AddText ("exec " Q3CONFIG_CFG "\n");
2557 	}
2558 
2559 	Cbuf_AddText ("exec autoexec.cfg\n");
2560 
2561 	Cbuf_Execute ();
2562 
2563 	// override anything from the config files with command line args
2564 	Com_StartupVariable( NULL );
2565 
2566   // get dedicated here for proper hunk megs initialization
2567 #ifdef DEDICATED
2568 	com_dedicated = Cvar_Get ("dedicated", "1", CVAR_ROM);
2569 #else
2570 	com_dedicated = Cvar_Get ("dedicated", "0", CVAR_LATCH);
2571 #endif
2572 	// allocate the stack based hunk allocator
2573 	Com_InitHunkMemory();
2574 
2575 	// if any archived cvars are modified after this, we will trigger a writing
2576 	// of the config file
2577 	cvar_modifiedFlags &= ~CVAR_ARCHIVE;
2578 
2579 	//
2580 	// init commands and vars
2581 	//
2582 	com_altivec = Cvar_Get ("com_altivec", "1", CVAR_ARCHIVE);
2583 	com_maxfps = Cvar_Get ("com_maxfps", "85", CVAR_ARCHIVE);
2584 	com_blood = Cvar_Get ("com_blood", "1", CVAR_ARCHIVE);
2585 
2586 	com_developer = Cvar_Get ("developer", "0", CVAR_TEMP );
2587 	com_logfile = Cvar_Get ("logfile", "0", CVAR_TEMP );
2588 
2589 	com_timescale = Cvar_Get ("timescale", "1", CVAR_CHEAT | CVAR_SYSTEMINFO );
2590 	com_fixedtime = Cvar_Get ("fixedtime", "0", CVAR_CHEAT);
2591 	com_showtrace = Cvar_Get ("com_showtrace", "0", CVAR_CHEAT);
2592 	com_dropsim = Cvar_Get ("com_dropsim", "0", CVAR_CHEAT);
2593 	com_speeds = Cvar_Get ("com_speeds", "0", 0);
2594 	com_timedemo = Cvar_Get ("timedemo", "0", CVAR_CHEAT);
2595 	com_cameraMode = Cvar_Get ("com_cameraMode", "0", CVAR_CHEAT);
2596 
2597 	cl_paused = Cvar_Get ("cl_paused", "0", CVAR_ROM);
2598 	sv_paused = Cvar_Get ("sv_paused", "0", CVAR_ROM);
2599 	cl_packetdelay = Cvar_Get ("cl_packetdelay", "0", CVAR_CHEAT);
2600 	sv_packetdelay = Cvar_Get ("sv_packetdelay", "0", CVAR_CHEAT);
2601 	com_sv_running = Cvar_Get ("sv_running", "0", CVAR_ROM);
2602 	com_cl_running = Cvar_Get ("cl_running", "0", CVAR_ROM);
2603 	com_buildScript = Cvar_Get( "com_buildScript", "0", 0 );
2604 	com_ansiColor = Cvar_Get( "com_ansiColor", "0", CVAR_ARCHIVE );
2605 
2606 	com_unfocused = Cvar_Get( "com_unfocused", "0", CVAR_ROM );
2607 	com_minimized = Cvar_Get( "com_minimized", "0", CVAR_ROM );
2608 	com_standalone = Cvar_Get( "com_standalone", "0", CVAR_INIT );
2609 
2610 	com_introPlayed = Cvar_Get( "com_introplayed", "0", CVAR_ARCHIVE);
2611 
2612 	if ( com_developer && com_developer->integer ) {
2613 		Cmd_AddCommand ("error", Com_Error_f);
2614 		Cmd_AddCommand ("crash", Com_Crash_f );
2615 		Cmd_AddCommand ("freeze", Com_Freeze_f);
2616 	}
2617 	Cmd_AddCommand ("quit", Com_Quit_f);
2618 	Cmd_AddCommand ("changeVectors", MSG_ReportChangeVectors_f );
2619 	Cmd_AddCommand ("writeconfig", Com_WriteConfig_f );
2620 
2621 	s = va("%s %s %s", Q3_VERSION, PLATFORM_STRING, __DATE__ );
2622 	com_version = Cvar_Get ("version", s, CVAR_ROM | CVAR_SERVERINFO );
2623 
2624 	Sys_Init();
2625 	Netchan_Init( Com_Milliseconds() & 0xffff );	// pick a port value that should be nice and random
2626 	VM_Init();
2627 	SV_Init();
2628 
2629 	com_dedicated->modified = qfalse;
2630 #ifndef DEDICATED
2631 	CL_Init();
2632 #endif
2633 
2634 	// set com_frameTime so that if a map is started on the
2635 	// command line it will still be able to count on com_frameTime
2636 	// being random enough for a serverid
2637 	com_frameTime = Com_Milliseconds();
2638 
2639 	// add + commands from command line
2640 	if ( !Com_AddStartupCommands() ) {
2641 		// if the user didn't give any commands, run default action
2642 		if ( !com_dedicated->integer ) {
2643 			Cbuf_AddText ("cinematic idlogo.RoQ\n");
2644 /*
2645 			if( !com_introPlayed->integer ) {
2646 				Cvar_Set( com_introPlayed->name, "1" );
2647 				Cvar_Set( "nextmap", "cinematic intro.RoQ" );
2648 			}
2649 */
2650 		}
2651 	}
2652 
2653 	// start in full screen ui mode
2654 	Cvar_Set("r_uiFullScreen", "1");
2655 
2656 	CL_StartHunkUsers( qfalse );
2657 
2658 	// make sure single player is off by default
2659 	Cvar_Set("ui_singlePlayerActive", "0");
2660 
2661 	com_fullyInitialized = qtrue;
2662 
2663 	// always set the cvar, but only print the info if it makes sense.
2664 	Com_DetectAltivec();
2665 #if idppc
2666 	Com_Printf ("Altivec support is %s\n", com_altivec->integer ? "enabled" : "disabled");
2667 #endif
2668 
2669 	Com_Printf ("--- Common Initialization Complete ---\n");
2670 }
2671 
2672 //==================================================================
2673 
2674 void Com_WriteConfigToFile( const char *filename ) {
2675 	fileHandle_t	f;
2676 
2677 	f = FS_FOpenFileWrite( filename );
2678 	if ( !f ) {
2679 		Com_Printf ("Couldn't write %s.\n", filename );
2680 		return;
2681 	}
2682 
2683 	FS_Printf (f, "// generated by WoP-Engine, do not modify\n");
2684 	Key_WriteBindings (f);
2685 	Cvar_WriteVariables (f);
2686 	FS_FCloseFile( f );
2687 }
2688 
2689 
2690 /*
2691 ===============
2692 Com_WriteConfiguration
2693 
2694 Writes key bindings and archived cvars to config file if modified
2695 ===============
2696 */
2697 void Com_WriteConfiguration( void ) {
2698 #ifndef DEDICATED
2699 	cvar_t	*fs;
2700 #endif
2701 	// if we are quiting without fully initializing, make sure
2702 	// we don't write out anything
2703 	if ( !com_fullyInitialized ) {
2704 		return;
2705 	}
2706 
2707 	if ( !(cvar_modifiedFlags & CVAR_ARCHIVE ) ) {
2708 		return;
2709 	}
2710 	cvar_modifiedFlags &= ~CVAR_ARCHIVE;
2711 
2712 	Com_WriteConfigToFile( Q3CONFIG_CFG );
2713 
2714 	// not needed for dedicated
2715 #ifndef DEDICATED
2716 	fs = Cvar_Get ("fs_game", "", CVAR_INIT|CVAR_SYSTEMINFO );
2717 #ifndef STANDALONE
2718 	if(!Cvar_VariableIntegerValue("com_standalone"))
2719 	{
2720 		if (UI_usesUniqueCDKey() && fs && fs->string[0] != 0) {
2721 			Com_WriteCDKey( fs->string, &cl_cdkey[16] );
2722 		} else {
2723 			Com_WriteCDKey( BASEGAME, cl_cdkey );
2724 		}
2725 	}
2726 #endif
2727 #endif
2728 }
2729 
2730 
2731 /*
2732 ===============
2733 Com_WriteConfig_f
2734 
2735 Write the config file to a specific name
2736 ===============
2737 */
2738 void Com_WriteConfig_f( void ) {
2739 	char	filename[MAX_QPATH];
2740 
2741 	if ( Cmd_Argc() != 2 ) {
2742 		Com_Printf( "Usage: writeconfig <filename>\n" );
2743 		return;
2744 	}
2745 
2746 	Q_strncpyz( filename, Cmd_Argv(1), sizeof( filename ) );
2747 	COM_DefaultExtension( filename, sizeof( filename ), ".cfg" );
2748 	Com_Printf( "Writing %s.\n", filename );
2749 	Com_WriteConfigToFile( filename );
2750 }
2751 
2752 /*
2753 ================
2754 Com_ModifyMsec
2755 ================
2756 */
2757 int Com_ModifyMsec( int msec ) {
2758 	int		clampTime;
2759 
2760 	//
2761 	// modify time for debugging values
2762 	//
2763 	if ( com_fixedtime->integer ) {
2764 		msec = com_fixedtime->integer;
2765 	} else if ( com_timescale->value ) {
2766 		msec *= com_timescale->value;
2767 	} else if (com_cameraMode->integer) {
2768 		msec *= com_timescale->value;
2769 	}
2770 
2771 	// don't let it scale below 1 msec
2772 	if ( msec < 1 && com_timescale->value) {
2773 		msec = 1;
2774 	}
2775 
2776 	if ( com_dedicated->integer ) {
2777 		// dedicated servers don't want to clamp for a much longer
2778 		// period, because it would mess up all the client's views
2779 		// of time.
2780 		if (com_sv_running->integer && msec > 500)
2781 			Com_Printf( "Hitch warning: %i msec frame time\n", msec );
2782 
2783 		clampTime = 5000;
2784 	} else
2785 	if ( !com_sv_running->integer ) {
2786 		// clients of remote servers do not want to clamp time, because
2787 		// it would skew their view of the server's time temporarily
2788 		clampTime = 5000;
2789 	} else {
2790 		// for local single player gaming
2791 		// we may want to clamp the time to prevent players from
2792 		// flying off edges when something hitches.
2793 		clampTime = 200;
2794 	}
2795 
2796 	if ( msec > clampTime ) {
2797 		msec = clampTime;
2798 	}
2799 
2800 	return msec;
2801 }
2802 
2803 /*
2804 =================
2805 Com_Frame
2806 =================
2807 */
2808 void Com_Frame( void ) {
2809 
2810 	int		msec, minMsec;
2811 	static int	lastTime;
2812 	int key;
2813 
2814 	int		timeBeforeFirstEvents;
2815 	int           timeBeforeServer;
2816 	int           timeBeforeEvents;
2817 	int           timeBeforeClient;
2818 	int           timeAfter;
2819 
2820 
2821 
2822 
2823 
2824 	if ( setjmp (abortframe) ) {
2825 		return;			// an ERR_DROP was thrown
2826 	}
2827 
2828 	timeBeforeFirstEvents =0;
2829 	timeBeforeServer =0;
2830 	timeBeforeEvents =0;
2831 	timeBeforeClient = 0;
2832 	timeAfter = 0;
2833 
2834 
2835 	// old net chan encryption key
2836 	key = 0x87243987;
2837 
2838 	// write config file if anything changed
2839 	Com_WriteConfiguration();
2840 
2841 	//
2842 	// main event loop
2843 	//
2844 	if ( com_speeds->integer ) {
2845 		timeBeforeFirstEvents = Sys_Milliseconds ();
2846 	}
2847 
2848 	// we may want to spin here if things are going too fast
2849 	if ( !com_dedicated->integer && com_maxfps->integer > 0 && !com_timedemo->integer ) {
2850 		minMsec = 1000 / com_maxfps->integer;
2851 	} else {
2852 		minMsec = 1;
2853 	}
2854 	do {
2855 		com_frameTime = Com_EventLoop();
2856 		if ( lastTime > com_frameTime ) {
2857 			lastTime = com_frameTime;		// possible on first frame
2858 		}
2859 		msec = com_frameTime - lastTime;
2860 	} while ( msec < minMsec );
2861 	Cbuf_Execute ();
2862 
2863 	if (com_altivec->modified)
2864 	{
2865 		Com_DetectAltivec();
2866 		com_altivec->modified = qfalse;
2867 	}
2868 
2869 	lastTime = com_frameTime;
2870 
2871 	// mess with msec if needed
2872 	com_frameMsec = msec;
2873 	msec = Com_ModifyMsec( msec );
2874 
2875 	//
2876 	// server side
2877 	//
2878 	if ( com_speeds->integer ) {
2879 		timeBeforeServer = Sys_Milliseconds ();
2880 	}
2881 
2882 	SV_Frame( msec );
2883 
2884 	// if "dedicated" has been modified, start up
2885 	// or shut down the client system.
2886 	// Do this after the server may have started,
2887 	// but before the client tries to auto-connect
2888 	if ( com_dedicated->modified ) {
2889 		// get the latched value
2890 		Cvar_Get( "dedicated", "0", 0 );
2891 		com_dedicated->modified = qfalse;
2892 		if ( !com_dedicated->integer ) {
2893 			SV_Shutdown( "dedicated set to 0" );
2894 			CL_FlushMemory();
2895 		}
2896 	}
2897 
2898 #ifndef DEDICATED
2899 	//
2900 	// client system
2901 	//
2902 	//
2903 	// run event loop a second time to get server to client packets
2904 	// without a frame of latency
2905 	//
2906 	if ( com_speeds->integer ) {
2907 		timeBeforeEvents = Sys_Milliseconds ();
2908 	}
2909 	Com_EventLoop();
2910 	Cbuf_Execute ();
2911 
2912 
2913 	//
2914 	// client side
2915 	//
2916 	if ( com_speeds->integer ) {
2917 		timeBeforeClient = Sys_Milliseconds ();
2918 	}
2919 
2920 	CL_Frame( msec );
2921 
2922 	if ( com_speeds->integer ) {
2923 		timeAfter = Sys_Milliseconds ();
2924 	}
2925 #endif
2926 
2927 	//
2928 	// report timing information
2929 	//
2930 	if ( com_speeds->integer ) {
2931 		int			all, sv, ev, cl;
2932 
2933 		all = timeAfter - timeBeforeServer;
2934 		sv = timeBeforeEvents - timeBeforeServer;
2935 		ev = timeBeforeServer - timeBeforeFirstEvents + timeBeforeClient - timeBeforeEvents;
2936 		cl = timeAfter - timeBeforeClient;
2937 		sv -= time_game;
2938 		cl -= time_frontend + time_backend;
2939 
2940 		Com_Printf ("frame:%i all:%3i sv:%3i ev:%3i cl:%3i gm:%3i rf:%3i bk:%3i\n",
2941 					 com_frameNumber, all, sv, ev, cl, time_game, time_frontend, time_backend );
2942 	}
2943 
2944 	//
2945 	// trace optimization tracking
2946 	//
2947 	if ( com_showtrace->integer ) {
2948 
2949 		extern	int c_traces, c_brush_traces, c_patch_traces;
2950 		extern	int	c_pointcontents;
2951 
2952 		Com_Printf ("%4i traces  (%ib %ip) %4i points\n", c_traces,
2953 			c_brush_traces, c_patch_traces, c_pointcontents);
2954 		c_traces = 0;
2955 		c_brush_traces = 0;
2956 		c_patch_traces = 0;
2957 		c_pointcontents = 0;
2958 	}
2959 
2960 	// old net chan encryption key
2961 	key = lastTime * 0x87243987;
2962 
2963 	com_frameNumber++;
2964 }
2965 
2966 /*
2967 =================
2968 Com_Shutdown
2969 =================
2970 */
2971 void Com_Shutdown (void) {
2972 	if (logfile) {
2973 		FS_FCloseFile (logfile);
2974 		logfile = 0;
2975 	}
2976 
2977 	if ( com_journalFile ) {
2978 		FS_FCloseFile( com_journalFile );
2979 		com_journalFile = 0;
2980 	}
2981 
2982 }
2983 
2984 //------------------------------------------------------------------------
2985 
2986 
2987 /*
2988 =====================
2989 Q_acos
2990 
2991 the msvc acos doesn't always return a value between -PI and PI:
2992 
2993 int i;
2994 i = 1065353246;
2995 acos(*(float*) &i) == -1.#IND0
2996 
2997 	This should go in q_math but it is too late to add new traps
2998 	to game and ui
2999 =====================
3000 */
3001 float Q_acos(float c) {
3002 	float angle;
3003 
3004 	angle = acos(c);
3005 
3006 	if (angle > M_PI) {
3007 		return (float)M_PI;
3008 	}
3009 	if (angle < -M_PI) {
3010 		return (float)M_PI;
3011 	}
3012 	return angle;
3013 }
3014 
3015 /*
3016 ===========================================
3017 command line completion
3018 ===========================================
3019 */
3020 
3021 /*
3022 ==================
3023 Field_Clear
3024 ==================
3025 */
3026 void Field_Clear( field_t *edit ) {
3027   memset(edit->buffer, 0, MAX_EDIT_LINE);
3028 	edit->cursor = 0;
3029 	edit->scroll = 0;
3030 }
3031 
3032 static const char *completionString;
3033 static char shortestMatch[MAX_TOKEN_CHARS];
3034 static int	matchCount;
3035 // field we are working on, passed to Field_AutoComplete(&g_consoleCommand for instance)
3036 static field_t *completionField;
3037 
3038 /*
3039 ===============
3040 FindMatches
3041 
3042 ===============
3043 */
3044 static void FindMatches( const char *s ) {
3045 	int		i;
3046 
3047 	if ( Q_stricmpn( s, completionString, strlen( completionString ) ) ) {
3048 		return;
3049 	}
3050 	matchCount++;
3051 	if ( matchCount == 1 ) {
3052 		Q_strncpyz( shortestMatch, s, sizeof( shortestMatch ) );
3053 		return;
3054 	}
3055 
3056 	// cut shortestMatch to the amount common with s
3057 	for ( i = 0 ; shortestMatch[i] ; i++ ) {
3058 		if ( i >= strlen( s ) ) {
3059 			shortestMatch[i] = 0;
3060 			break;
3061 		}
3062 
3063 		if ( tolower(shortestMatch[i]) != tolower(s[i]) ) {
3064 			shortestMatch[i] = 0;
3065 		}
3066 	}
3067 }
3068 
3069 /*
3070 ===============
3071 PrintMatches
3072 
3073 ===============
3074 */
3075 static void PrintMatches( const char *s ) {
3076 	if ( !Q_stricmpn( s, shortestMatch, strlen( shortestMatch ) ) ) {
3077 		Com_Printf( "    %s\n", s );
3078 	}
3079 }
3080 
3081 /*
3082 ===============
3083 PrintCvarMatches
3084 
3085 ===============
3086 */
3087 static void PrintCvarMatches( const char *s ) {
3088 	char value[ TRUNCATE_LENGTH ];
3089 
3090 	if ( !Q_stricmpn( s, shortestMatch, strlen( shortestMatch ) ) ) {
3091 		Com_TruncateLongString( value, Cvar_VariableString( s ) );
3092 		Com_Printf( "    %s = \"%s\"\n", s, value );
3093 	}
3094 }
3095 
3096 /*
3097 ===============
3098 Field_FindFirstSeparator
3099 ===============
3100 */
3101 static char *Field_FindFirstSeparator( char *s )
3102 {
3103 	int i;
3104 
3105 	for( i = 0; i < strlen( s ); i++ )
3106 	{
3107 		if( s[ i ] == ';' )
3108 			return &s[ i ];
3109 	}
3110 
3111 	return NULL;
3112 }
3113 
3114 #ifndef DEDICATED
3115 /*
3116 ===============
3117 Field_CompleteKeyname
3118 ===============
3119 */
3120 static void Field_CompleteKeyname( void )
3121 {
3122 	matchCount = 0;
3123 	shortestMatch[ 0 ] = 0;
3124 
3125 	Key_KeynameCompletion( FindMatches );
3126 
3127 	if( matchCount == 0 )
3128 		return;
3129 
3130 	Q_strncpyz( &completionField->buffer[ strlen( completionField->buffer ) -
3131 		strlen( completionString ) ], shortestMatch,
3132 		sizeof( completionField->buffer ) );
3133 	completionField->cursor = strlen( completionField->buffer );
3134 
3135 	if( matchCount == 1 )
3136 	{
3137 		Q_strcat( completionField->buffer, sizeof( completionField->buffer ), " " );
3138 		completionField->cursor++;
3139 		return;
3140 	}
3141 
3142 	Com_Printf( "]%s\n", completionField->buffer );
3143 
3144 	Key_KeynameCompletion( PrintMatches );
3145 }
3146 #endif
3147 
3148 /*
3149 ===============
3150 Field_CompleteFilename
3151 ===============
3152 */
3153 static void Field_CompleteFilename( const char *dir,
3154 		const char *ext, qboolean stripExt )
3155 {
3156 	matchCount = 0;
3157 	shortestMatch[ 0 ] = 0;
3158 
3159 	FS_FilenameCompletion( dir, ext, stripExt, FindMatches );
3160 
3161 	if( matchCount == 0 )
3162 		return;
3163 
3164 	Q_strncpyz( &completionField->buffer[ strlen( completionField->buffer ) -
3165 		strlen( completionString ) ], shortestMatch,
3166 		sizeof( completionField->buffer ) );
3167 	completionField->cursor = strlen( completionField->buffer );
3168 
3169 	if( matchCount == 1 )
3170 	{
3171 		Q_strcat( completionField->buffer, sizeof( completionField->buffer ), " " );
3172 		completionField->cursor++;
3173 		return;
3174 	}
3175 
3176 	Com_Printf( "]%s\n", completionField->buffer );
3177 
3178 	FS_FilenameCompletion( dir, ext, stripExt, PrintMatches );
3179 }
3180 
3181 /*
3182 ===============
3183 Field_CompleteCommand
3184 ===============
3185 */
3186 static void Field_CompleteCommand( char *cmd,
3187 		qboolean doCommands, qboolean doCvars )
3188 {
3189 	int		completionArgument = 0;
3190 	char	*p;
3191 
3192 	// Skip leading whitespace and quotes
3193 	cmd = Com_SkipCharset( cmd, " \"" );
3194 
3195 	Cmd_TokenizeStringIgnoreQuotes( cmd );
3196 	completionArgument = Cmd_Argc( );
3197 
3198 	// If there is trailing whitespace on the cmd
3199 	if( *( cmd + strlen( cmd ) - 1 ) == ' ' )
3200 	{
3201 		completionString = "";
3202 		completionArgument++;
3203 	}
3204 	else
3205 		completionString = Cmd_Argv( completionArgument - 1 );
3206 
3207 #ifndef DEDICATED
3208 	// Unconditionally add a '\' to the start of the buffer
3209 	if( completionField->buffer[ 0 ] &&
3210 			completionField->buffer[ 0 ] != '\\' )
3211 	{
3212 		if( completionField->buffer[ 0 ] != '/' )
3213 		{
3214 			// Buffer is full, refuse to complete
3215 			if( strlen( completionField->buffer ) + 1 >=
3216 				sizeof( completionField->buffer ) )
3217 				return;
3218 
3219 			memmove( &completionField->buffer[ 1 ],
3220 				&completionField->buffer[ 0 ],
3221 				strlen( completionField->buffer ) + 1 );
3222 			completionField->cursor++;
3223 		}
3224 
3225 		completionField->buffer[ 0 ] = '\\';
3226 	}
3227 #endif
3228 
3229 	if( completionArgument > 1 )
3230 	{
3231 		const char *baseCmd = Cmd_Argv( 0 );
3232 
3233 #ifndef DEDICATED
3234 		// This should always be true
3235 		if( baseCmd[ 0 ] == '\\' || baseCmd[ 0 ] == '/' )
3236 			baseCmd++;
3237 #endif
3238 
3239 		if( ( p = Field_FindFirstSeparator( cmd ) ) )
3240 		{
3241 			// Compound command
3242 			Field_CompleteCommand( p + 1, qtrue, qtrue );
3243 		}
3244 		else
3245 		{
3246 			// FIXME: all this junk should really be associated with the respective
3247 			// commands, instead of being hard coded here
3248 			if( ( !Q_stricmp( baseCmd, "map" ) ||
3249 						!Q_stricmp( baseCmd, "devmap" ) ||
3250 						!Q_stricmp( baseCmd, "spmap" ) ||
3251 						!Q_stricmp( baseCmd, "spdevmap" ) ) &&
3252 					completionArgument == 2 )
3253 			{
3254 				Field_CompleteFilename( "maps", "bsp", qtrue );
3255 			}
3256 			else if( ( !Q_stricmp( baseCmd, "exec" ) ||
3257 						!Q_stricmp( baseCmd, "writeconfig" ) ) &&
3258 					completionArgument == 2 )
3259 			{
3260 				Field_CompleteFilename( "", "cfg", qfalse );
3261 			}
3262 			else if( !Q_stricmp( baseCmd, "condump" ) &&
3263 					completionArgument == 2 )
3264 			{
3265 				Field_CompleteFilename( "", "txt", qfalse );
3266 			}
3267 			else if( ( !Q_stricmp( baseCmd, "toggle" ) ||
3268 						!Q_stricmp( baseCmd, "vstr" ) ||
3269 						!Q_stricmp( baseCmd, "set" ) ||
3270 						!Q_stricmp( baseCmd, "seta" ) ||
3271 						!Q_stricmp( baseCmd, "setu" ) ||
3272 						!Q_stricmp( baseCmd, "sets" ) ) &&
3273 					completionArgument == 2 )
3274 			{
3275 				// Skip "<cmd> "
3276 				p = Com_SkipTokens( cmd, 1, " " );
3277 
3278 				if( p > cmd )
3279 					Field_CompleteCommand( p, qfalse, qtrue );
3280 			}
3281 #ifndef DEDICATED
3282 			else if( !Q_stricmp( baseCmd, "demo" ) && completionArgument == 2 )
3283 			{
3284 				char demoExt[ 16 ];
3285 
3286 				Com_sprintf( demoExt, sizeof( demoExt ), ".dm_%d", PROTOCOL_VERSION );
3287 				Field_CompleteFilename( "demos", demoExt, qtrue );
3288 			}
3289 			else if( !Q_stricmp( baseCmd, "rcon" ) && completionArgument == 2 )
3290 			{
3291 				// Skip "rcon "
3292 				p = Com_SkipTokens( cmd, 1, " " );
3293 
3294 				if( p > cmd )
3295 					Field_CompleteCommand( p, qtrue, qtrue );
3296 			}
3297 			else if( !Q_stricmp( baseCmd, "bind" ) )
3298 			{
3299 				if( completionArgument == 2 )
3300 				{
3301 					// Skip "bind "
3302 					p = Com_SkipTokens( cmd, 1, " " );
3303 
3304 					if( p > cmd )
3305 						Field_CompleteKeyname( );
3306 				}
3307 				else if( completionArgument >= 3 )
3308 				{
3309 					// Skip "bind <key> "
3310 					p = Com_SkipTokens( cmd, 2, " " );
3311 
3312 					if( p > cmd )
3313 						Field_CompleteCommand( p, qtrue, qtrue );
3314 				}
3315 			}
3316 #endif
3317 		}
3318 	}
3319 	else
3320 	{
3321 		int completionOffset;
3322 
3323 		if( completionString[0] == '\\' || completionString[0] == '/' )
3324 			completionString++;
3325 
3326 		matchCount = 0;
3327 		shortestMatch[ 0 ] = 0;
3328 
3329 		if( strlen( completionString ) == 0 )
3330 			return;
3331 
3332 		if( doCommands )
3333 			Cmd_CommandCompletion( FindMatches );
3334 
3335 		if( doCvars )
3336 			Cvar_CommandCompletion( FindMatches );
3337 
3338 		if( matchCount == 0 )
3339 			return; // no matches
3340 
3341 		completionOffset = strlen( completionField->buffer ) - strlen( completionString );
3342 
3343 		Q_strncpyz( &completionField->buffer[ completionOffset ], shortestMatch,
3344 			sizeof( completionField->buffer ) - completionOffset );
3345 
3346 		completionField->cursor = strlen( completionField->buffer );
3347 
3348 		if( matchCount == 1 )
3349 		{
3350 			Q_strcat( completionField->buffer, sizeof( completionField->buffer ), " " );
3351 			completionField->cursor++;
3352 			return;
3353 		}
3354 
3355 		Com_Printf( "]%s\n", completionField->buffer );
3356 
3357 		// run through again, printing matches
3358 		if( doCommands )
3359 			Cmd_CommandCompletion( PrintMatches );
3360 
3361 		if( doCvars )
3362 			Cvar_CommandCompletion( PrintCvarMatches );
3363 	}
3364 }
3365 
3366 /*
3367 ===============
3368 Field_AutoComplete
3369 
3370 Perform Tab expansion
3371 ===============
3372 */
3373 void Field_AutoComplete( field_t *field )
3374 {
3375 	completionField = field;
3376 
3377 	Field_CompleteCommand( completionField->buffer, qtrue, qtrue );
3378 }
3379 
3380 /*
3381 ==================
3382 Com_RandomBytes
3383 
3384 fills string array with len radom bytes, peferably from the OS randomizer
3385 ==================
3386 */
3387 void Com_RandomBytes( byte *string, int len )
3388 {
3389 	int i;
3390 
3391 	if( Sys_RandomBytes( string, len ) )
3392 		return;
3393 
3394 	Com_Printf( "Com_RandomBytes: using weak randomization\n" );
3395 	srand( time( 0 ) );
3396 	for( i = 0; i < len; i++ )
3397 		string[i] = (unsigned char)( rand() % 255 );
3398 }
3399 
3400